mirror of
https://github.com/meshtastic/firmware.git
synced 2025-08-01 03:15:42 +00:00
Merge branch 'master' into chatter_2_fixes
This commit is contained in:
commit
6f47fa3c34
2
.github/workflows/test_native.yml
vendored
2
.github/workflows/test_native.yml
vendored
@ -143,7 +143,7 @@ jobs:
|
||||
merge-multiple: true
|
||||
|
||||
- name: Test Report
|
||||
uses: dorny/test-reporter@v2.1.0
|
||||
uses: dorny/test-reporter@v2.1.1
|
||||
with:
|
||||
name: PlatformIO Tests
|
||||
path: testreport.xml
|
||||
|
@ -87,6 +87,9 @@
|
||||
</screenshots>
|
||||
|
||||
<releases>
|
||||
<release version="2.7.3" date="2025-07-10">
|
||||
<url type="details">https://github.com/meshtastic/firmware/releases?q=tag%3Av2.7.3</url>
|
||||
</release>
|
||||
<release version="2.7.2" date="2025-07-04">
|
||||
<url type="details">https://github.com/meshtastic/firmware/releases?q=tag%3Av2.7.2</url>
|
||||
</release>
|
||||
|
7
debian/changelog
vendored
7
debian/changelog
vendored
@ -1,4 +1,4 @@
|
||||
meshtasticd (2.7.2.0) UNRELEASED; urgency=medium
|
||||
meshtasticd (2.7.3.0) UNRELEASED; urgency=medium
|
||||
|
||||
[ Austin Lane ]
|
||||
* Initial packaging
|
||||
@ -28,4 +28,7 @@ meshtasticd (2.7.2.0) UNRELEASED; urgency=medium
|
||||
[ ]
|
||||
* GitHub Actions Automatic version bump
|
||||
|
||||
-- <github-actions[bot]@users.noreply.github.com> Fri, 04 Jul 2025 11:58:01 +0000
|
||||
[ Ubuntu ]
|
||||
* GitHub Actions Automatic version bump
|
||||
|
||||
-- Ubuntu <github-actions[bot]@users.noreply.github.com> Thu, 10 Jul 2025 16:29:27 +0000
|
||||
|
@ -104,7 +104,7 @@ lib_deps =
|
||||
[radiolib_base]
|
||||
lib_deps =
|
||||
# renovate: datasource=custom.pio depName=RadioLib packageName=jgromes/library/RadioLib
|
||||
jgromes/RadioLib@7.2.0
|
||||
jgromes/RadioLib@7.2.1
|
||||
|
||||
[device-ui_base]
|
||||
lib_deps =
|
||||
@ -115,7 +115,7 @@ lib_deps =
|
||||
[environmental_base]
|
||||
lib_deps =
|
||||
# renovate: datasource=custom.pio depName=Adafruit BusIO packageName=adafruit/library/Adafruit BusIO
|
||||
adafruit/Adafruit BusIO@1.17.1
|
||||
adafruit/Adafruit BusIO@1.17.2
|
||||
# renovate: datasource=custom.pio depName=Adafruit Unified Sensor packageName=adafruit/library/Adafruit Unified Sensor
|
||||
adafruit/Adafruit Unified Sensor@1.1.15
|
||||
# renovate: datasource=custom.pio depName=Adafruit BMP280 packageName=adafruit/library/Adafruit BMP280 Library
|
||||
|
@ -83,6 +83,29 @@ extern uint16_t TFT_MESH;
|
||||
#include "platform/portduino/PortduinoGlue.h"
|
||||
#endif
|
||||
|
||||
bool shouldWakeOnReceivedMessage()
|
||||
{
|
||||
/*
|
||||
The goal here is to determine when we do NOT wake up the screen on message received:
|
||||
- Any ext. notifications are turned on
|
||||
- If role is not client / client_mute
|
||||
- If the battery level is very low
|
||||
*/
|
||||
if (moduleConfig.external_notification.enabled) {
|
||||
return false;
|
||||
}
|
||||
if (config.device.role != meshtastic_Config_DeviceConfig_Role_CLIENT &&
|
||||
config.device.role != meshtastic_Config_DeviceConfig_Role_CLIENT_MUTE) {
|
||||
return false;
|
||||
}
|
||||
if (powerStatus && powerStatus->getBatteryChargePercent() < 10) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool wake_on_received_message = shouldWakeOnReceivedMessage(); // Master Switch to enable here
|
||||
|
||||
using namespace meshtastic; /** @todo remove */
|
||||
|
||||
namespace graphics
|
||||
@ -171,7 +194,7 @@ void Screen::showOverlayBanner(BannerOverlayOptions banner_overlay_options)
|
||||
}
|
||||
|
||||
// Called to trigger a banner with custom message and duration
|
||||
void Screen::showNodePicker(const char *message, uint32_t durationMs, std::function<void(int)> bannerCallback)
|
||||
void Screen::showNodePicker(const char *message, uint32_t durationMs, std::function<void(uint32_t)> bannerCallback)
|
||||
{
|
||||
#ifdef USE_EINK
|
||||
EINK_ADD_FRAMEFLAG(dispdev, DEMAND_FAST); // Skip full refresh for all overlay menus
|
||||
@ -196,7 +219,6 @@ void Screen::showNodePicker(const char *message, uint32_t durationMs, std::funct
|
||||
void Screen::showNumberPicker(const char *message, uint32_t durationMs, uint8_t digits,
|
||||
std::function<void(uint32_t)> bannerCallback)
|
||||
{
|
||||
LOG_WARN("Show Number Picker");
|
||||
#ifdef USE_EINK
|
||||
EINK_ADD_FRAMEFLAG(dispdev, DEMAND_FAST); // Skip full refresh for all overlay menus
|
||||
#endif
|
||||
@ -1258,40 +1280,46 @@ int Screen::handleTextMessage(const meshtastic_MeshPacket *packet)
|
||||
devicestate.has_rx_text_message = true; // Needed to include the message frame
|
||||
hasUnreadMessage = true; // Enables mail icon in the header
|
||||
setFrames(FOCUS_PRESERVE); // Refresh frame list without switching view
|
||||
forceDisplay(); // Forces screen redraw
|
||||
|
||||
// === Prepare banner content ===
|
||||
const meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(packet->from);
|
||||
const char *longName = (node && node->has_user) ? node->user.long_name : nullptr;
|
||||
// Only wake/force display if the configuration allows it
|
||||
wake_on_received_message = shouldWakeOnReceivedMessage();
|
||||
if (wake_on_received_message) {
|
||||
setOn(true); // Wake up the screen first
|
||||
forceDisplay(); // Forces screen redraw
|
||||
|
||||
const char *msgRaw = reinterpret_cast<const char *>(packet->decoded.payload.bytes);
|
||||
// === Prepare banner content ===
|
||||
const meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(packet->from);
|
||||
const char *longName = (node && node->has_user) ? node->user.long_name : nullptr;
|
||||
|
||||
char banner[256];
|
||||
const char *msgRaw = reinterpret_cast<const char *>(packet->decoded.payload.bytes);
|
||||
|
||||
// Check for bell character in message to determine alert type
|
||||
bool isAlert = false;
|
||||
for (size_t i = 0; i < packet->decoded.payload.size && i < 100; i++) {
|
||||
if (msgRaw[i] == '\x07') {
|
||||
isAlert = true;
|
||||
break;
|
||||
char banner[256];
|
||||
|
||||
// Check for bell character in message to determine alert type
|
||||
bool isAlert = false;
|
||||
for (size_t i = 0; i < packet->decoded.payload.size && i < 100; i++) {
|
||||
if (msgRaw[i] == '\x07') {
|
||||
isAlert = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isAlert) {
|
||||
if (longName && longName[0]) {
|
||||
snprintf(banner, sizeof(banner), "Alert Received from\n%s", longName);
|
||||
if (isAlert) {
|
||||
if (longName && longName[0]) {
|
||||
snprintf(banner, sizeof(banner), "Alert Received from\n%s", longName);
|
||||
} else {
|
||||
strcpy(banner, "Alert Received");
|
||||
}
|
||||
} else {
|
||||
strcpy(banner, "Alert Received");
|
||||
if (longName && longName[0]) {
|
||||
snprintf(banner, sizeof(banner), "New Message from\n%s", longName);
|
||||
} else {
|
||||
strcpy(banner, "New Message");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (longName && longName[0]) {
|
||||
snprintf(banner, sizeof(banner), "New Message from\n%s", longName);
|
||||
} else {
|
||||
strcpy(banner, "New Message");
|
||||
}
|
||||
}
|
||||
|
||||
screen->showSimpleBanner(banner, 3000);
|
||||
screen->showSimpleBanner(banner, 3000);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1330,7 +1358,7 @@ int Screen::handleInputEvent(const InputEvent *event)
|
||||
setFastFramerate(); // Draw ASAP
|
||||
#endif
|
||||
if (NotificationRenderer::isOverlayBannerShowing()) {
|
||||
NotificationRenderer::inEvent = event->inputEvent;
|
||||
NotificationRenderer::inEvent = *event;
|
||||
static OverlayCallback overlays[] = {graphics::UIRenderer::drawNavigationBar, NotificationRenderer::drawBannercallback};
|
||||
ui->setOverlays(overlays, sizeof(overlays) / sizeof(overlays[0]));
|
||||
setFastFramerate(); // Draw ASAP
|
||||
|
@ -26,6 +26,8 @@ struct BannerOverlayOptions {
|
||||
};
|
||||
} // namespace graphics
|
||||
|
||||
bool shouldWakeOnReceivedMessage();
|
||||
|
||||
#if !HAS_SCREEN
|
||||
#include "power.h"
|
||||
namespace graphics
|
||||
@ -92,6 +94,7 @@ class Screen
|
||||
#include "commands.h"
|
||||
#include "concurrency/LockGuard.h"
|
||||
#include "concurrency/OSThread.h"
|
||||
#include "graphics/draw/MenuHandler.h"
|
||||
#include "input/InputBroker.h"
|
||||
#include "mesh/MeshModule.h"
|
||||
#include "modules/AdminModule.h"
|
||||
@ -122,6 +125,8 @@ class Screen
|
||||
#define SEGMENT_WIDTH 16
|
||||
#define SEGMENT_HEIGHT 4
|
||||
|
||||
extern bool wake_on_received_message;
|
||||
|
||||
/// Convert an integer GPS coords to a floating point
|
||||
#define DegD(i) (i * 1e-7)
|
||||
extern bool hasUnreadMessage;
|
||||
@ -308,9 +313,15 @@ class Screen : public concurrency::OSThread
|
||||
void showSimpleBanner(const char *message, uint32_t durationMs = 0);
|
||||
void showOverlayBanner(BannerOverlayOptions);
|
||||
|
||||
void showNodePicker(const char *message, uint32_t durationMs, std::function<void(int)> bannerCallback);
|
||||
void showNodePicker(const char *message, uint32_t durationMs, std::function<void(uint32_t)> bannerCallback);
|
||||
void showNumberPicker(const char *message, uint32_t durationMs, uint8_t digits, std::function<void(uint32_t)> bannerCallback);
|
||||
|
||||
void requestMenu(graphics::menuHandler::screenMenus menuToShow)
|
||||
{
|
||||
graphics::menuHandler::menuQueue = menuToShow;
|
||||
runNow();
|
||||
}
|
||||
|
||||
void startFirmwareUpdateScreen()
|
||||
{
|
||||
ScreenCmd cmd;
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include "main.h"
|
||||
#include "modules/AdminModule.h"
|
||||
#include "modules/CannedMessageModule.h"
|
||||
#include "modules/KeyVerificationModule.h"
|
||||
|
||||
extern uint16_t TFT_MESH;
|
||||
|
||||
@ -128,11 +129,11 @@ void menuHandler::ClockFacePicker()
|
||||
screen->runNow();
|
||||
} else if (selected == Digital) {
|
||||
uiconfig.is_clockface_analog = false;
|
||||
nodeDB->saveProto("/prefs/uiconfig.proto", meshtastic_DeviceUIConfig_size, &meshtastic_DeviceUIConfig_msg, &uiconfig);
|
||||
saveUIConfig();
|
||||
screen->setFrames(Screen::FOCUS_CLOCK);
|
||||
} else {
|
||||
uiconfig.is_clockface_analog = true;
|
||||
nodeDB->saveProto("/prefs/uiconfig.proto", meshtastic_DeviceUIConfig_size, &meshtastic_DeviceUIConfig_msg, &uiconfig);
|
||||
saveUIConfig();
|
||||
screen->setFrames(Screen::FOCUS_CLOCK);
|
||||
}
|
||||
};
|
||||
@ -237,27 +238,25 @@ void menuHandler::clockMenu()
|
||||
|
||||
void menuHandler::messageResponseMenu()
|
||||
{
|
||||
enum optionsNumbers { Back = 0, Dismiss = 1, Preset = 2, Freetext = 3, Aloud = 4, enumEnd = 5 };
|
||||
|
||||
static const char *optionsArray[enumEnd] = {"Back", "Dismiss", "Reply via Preset"};
|
||||
static int optionsEnumArray[enumEnd] = {Back, Dismiss, Preset};
|
||||
int options = 3;
|
||||
|
||||
static const char **optionsArrayPtr;
|
||||
int options;
|
||||
enum optionsNumbers { Back = 0, Dismiss = 1, Preset = 2, Freetext = 3 };
|
||||
if (kb_found) {
|
||||
static const char *optionsArray[] = {"Back", "Dismiss", "Reply via Preset", "Reply via Freetext"};
|
||||
optionsArrayPtr = optionsArray;
|
||||
options = 4;
|
||||
} else {
|
||||
static const char *optionsArray[] = {"Back", "Dismiss", "Reply via Preset"};
|
||||
optionsArrayPtr = optionsArray;
|
||||
options = 3;
|
||||
optionsArray[options] = "Reply via Freetext";
|
||||
optionsEnumArray[options++] = Freetext;
|
||||
}
|
||||
|
||||
#ifdef HAS_I2S
|
||||
static const char *optionsArray[] = {"Back", "Dismiss", "Reply via Preset", "Reply via Freetext", "Read Aloud"};
|
||||
optionsArrayPtr = optionsArray;
|
||||
options = 5;
|
||||
optionsArray[options] = "Read Aloud";
|
||||
optionsEnumArray[options++] = Aloud;
|
||||
#endif
|
||||
BannerOverlayOptions bannerOptions;
|
||||
bannerOptions.message = "Message Action";
|
||||
bannerOptions.optionsArrayPtr = optionsArrayPtr;
|
||||
bannerOptions.optionsArrayPtr = optionsArray;
|
||||
bannerOptions.optionsEnumPtr = optionsEnumArray;
|
||||
bannerOptions.optionsCount = options;
|
||||
bannerOptions.bannerCallback = [](int selected) -> void {
|
||||
if (selected == Dismiss) {
|
||||
@ -276,7 +275,7 @@ void menuHandler::messageResponseMenu()
|
||||
}
|
||||
}
|
||||
#ifdef HAS_I2S
|
||||
else if (selected == 4) {
|
||||
else if (selected == Aloud) {
|
||||
const meshtastic_MeshPacket &mp = devicestate.rx_text_message;
|
||||
const char *msg = reinterpret_cast<const char *>(mp.decoded.payload.bytes);
|
||||
|
||||
@ -289,10 +288,10 @@ void menuHandler::messageResponseMenu()
|
||||
|
||||
void menuHandler::homeBaseMenu()
|
||||
{
|
||||
enum optionsNumbers { Back, Backlight, Position, Preset, Freetext, Bluetooth, Sleep };
|
||||
enum optionsNumbers { Back, Backlight, Position, Preset, Freetext, Bluetooth, Sleep, enumEnd };
|
||||
|
||||
static const char *optionsArray[6] = {"Back"};
|
||||
static int optionsEnumArray[6] = {Back};
|
||||
static const char *optionsArray[enumEnd] = {"Back"};
|
||||
static int optionsEnumArray[enumEnd] = {Back};
|
||||
int options = 1;
|
||||
|
||||
#ifdef PIN_EINK_EN
|
||||
@ -347,37 +346,28 @@ void menuHandler::homeBaseMenu()
|
||||
|
||||
void menuHandler::systemBaseMenu()
|
||||
{
|
||||
|
||||
// Check if brightness is supported
|
||||
bool hasSupportBrightness = false;
|
||||
#if defined(ST7789_CS) || defined(USE_OLED) || defined(USE_SSD1306) || defined(USE_SH1106) || defined(USE_SH1107) || HAS_TFT
|
||||
hasSupportBrightness = true;
|
||||
#endif
|
||||
|
||||
enum optionsNumbers { Back, Beeps, Brightness, Reboot, Color, MUI, Test };
|
||||
static const char *optionsArray[7] = {"Back"};
|
||||
static int optionsEnumArray[7] = {Back};
|
||||
enum optionsNumbers { Back, Notifications, ScreenOptions, PowerMenu, Test, enumEnd };
|
||||
static const char *optionsArray[enumEnd] = {"Back"};
|
||||
static int optionsEnumArray[enumEnd] = {Back};
|
||||
int options = 1;
|
||||
|
||||
optionsArray[options] = "Reboot";
|
||||
optionsEnumArray[options++] = Reboot;
|
||||
|
||||
optionsArray[options] = "Beeps Action";
|
||||
optionsEnumArray[options++] = Beeps;
|
||||
|
||||
if (hasSupportBrightness) {
|
||||
optionsArray[options] = "Brightness";
|
||||
optionsEnumArray[options++] = Brightness;
|
||||
}
|
||||
|
||||
#if defined(HELTEC_MESH_NODE_T114) || defined(HELTEC_VISION_MASTER_T190) || HAS_TFT
|
||||
optionsArray[options] = "Screen Color";
|
||||
optionsEnumArray[options++] = Color;
|
||||
#endif
|
||||
#if HAS_TFT
|
||||
optionsArray[options] = "Switch to MUI";
|
||||
optionsEnumArray[options++] = MUI;
|
||||
optionsArray[options] = "Notifications";
|
||||
optionsEnumArray[options++] = Notifications;
|
||||
#if defined(ST7789_CS) || defined(USE_OLED) || defined(USE_SSD1306) || defined(USE_SH1106) || defined(USE_SH1107) || \
|
||||
defined(HELTEC_MESH_NODE_T114) || defined(HELTEC_VISION_MASTER_T190) || HAS_TFT
|
||||
optionsArray[options] = "Screen Options";
|
||||
optionsEnumArray[options++] = ScreenOptions;
|
||||
#endif
|
||||
|
||||
optionsArray[options] = "Reboot/Shutdown";
|
||||
optionsEnumArray[options++] = PowerMenu;
|
||||
|
||||
if (test_enabled) {
|
||||
optionsArray[options] = "Test Menu";
|
||||
optionsEnumArray[options++] = Test;
|
||||
@ -389,20 +379,14 @@ void menuHandler::systemBaseMenu()
|
||||
bannerOptions.optionsCount = options;
|
||||
bannerOptions.optionsEnumPtr = optionsEnumArray;
|
||||
bannerOptions.bannerCallback = [](int selected) -> void {
|
||||
if (selected == Beeps) {
|
||||
menuHandler::menuQueue = menuHandler::buzzermodemenupicker;
|
||||
if (selected == Notifications) {
|
||||
menuHandler::menuQueue = menuHandler::notifications_menu;
|
||||
screen->runNow();
|
||||
} else if (selected == Brightness) {
|
||||
menuHandler::menuQueue = menuHandler::brightness_picker;
|
||||
} else if (selected == ScreenOptions) {
|
||||
menuHandler::menuQueue = menuHandler::screen_options_menu;
|
||||
screen->runNow();
|
||||
} else if (selected == Reboot) {
|
||||
menuHandler::menuQueue = menuHandler::reboot_menu;
|
||||
screen->runNow();
|
||||
} else if (selected == MUI) {
|
||||
menuHandler::menuQueue = menuHandler::mui_picker;
|
||||
screen->runNow();
|
||||
} else if (selected == Color) {
|
||||
menuHandler::menuQueue = menuHandler::tftcolormenupicker;
|
||||
} else if (selected == PowerMenu) {
|
||||
menuHandler::menuQueue = menuHandler::power_menu;
|
||||
screen->runNow();
|
||||
} else if (selected == Test) {
|
||||
menuHandler::menuQueue = menuHandler::test_menu;
|
||||
@ -419,21 +403,22 @@ void menuHandler::systemBaseMenu()
|
||||
|
||||
void menuHandler::favoriteBaseMenu()
|
||||
{
|
||||
int options;
|
||||
static const char **optionsArrayPtr;
|
||||
enum optionsNumbers { Back, Preset, Freetext, Remove, enumEnd };
|
||||
static const char *optionsArray[enumEnd] = {"Back", "New Preset Msg"};
|
||||
static int optionsEnumArray[enumEnd] = {Back, Preset};
|
||||
int options = 2;
|
||||
|
||||
if (kb_found) {
|
||||
static const char *optionsArray[] = {"Back", "New Preset Msg", "New Freetext Msg", "Remove Favorite"};
|
||||
optionsArrayPtr = optionsArray;
|
||||
options = 4;
|
||||
} else {
|
||||
static const char *optionsArray[] = {"Back", "New Preset Msg", "Remove Favorite"};
|
||||
optionsArrayPtr = optionsArray;
|
||||
options = 3;
|
||||
optionsArray[options] = "New Freetext Msg";
|
||||
optionsEnumArray[options++] = Freetext;
|
||||
}
|
||||
optionsArray[options] = "Remove Favorite";
|
||||
optionsEnumArray[options++] = Remove;
|
||||
|
||||
BannerOverlayOptions bannerOptions;
|
||||
bannerOptions.message = "Favorites Action";
|
||||
bannerOptions.optionsArrayPtr = optionsArrayPtr;
|
||||
bannerOptions.optionsArrayPtr = optionsArray;
|
||||
bannerOptions.optionsEnumPtr = optionsEnumArray;
|
||||
bannerOptions.optionsCount = options;
|
||||
bannerOptions.bannerCallback = [](int selected) -> void {
|
||||
if (selected == 1) {
|
||||
@ -450,34 +435,29 @@ void menuHandler::favoriteBaseMenu()
|
||||
|
||||
void menuHandler::positionBaseMenu()
|
||||
{
|
||||
int options;
|
||||
static const char **optionsArrayPtr;
|
||||
static const char *optionsArray[] = {"Back", "GPS Toggle", "Compass"};
|
||||
static const char *optionsArrayCalibrate[] = {"Back", "GPS Toggle", "Compass", "Compass Calibrate"};
|
||||
enum optionsNumbers { Back, GPSToggle, CompassMenu, CompassCalibrate, enumEnd };
|
||||
|
||||
static const char *optionsArray[enumEnd] = {"Back", "GPS Toggle", "Compass"};
|
||||
static int optionsEnumArray[enumEnd] = {Back, GPSToggle, CompassMenu};
|
||||
int options = 3;
|
||||
|
||||
if (accelerometerThread) {
|
||||
optionsArrayPtr = optionsArrayCalibrate;
|
||||
options = 4;
|
||||
} else {
|
||||
optionsArrayPtr = optionsArray;
|
||||
options = 3;
|
||||
optionsArray[options] = "Compass Calibrate";
|
||||
optionsEnumArray[options++] = CompassCalibrate;
|
||||
}
|
||||
BannerOverlayOptions bannerOptions;
|
||||
bannerOptions.message = "Position Action";
|
||||
bannerOptions.optionsArrayPtr = optionsArrayPtr;
|
||||
bannerOptions.optionsArrayPtr = optionsArray;
|
||||
bannerOptions.optionsEnumPtr = optionsEnumArray;
|
||||
bannerOptions.optionsCount = options;
|
||||
bannerOptions.bannerCallback = [](int selected) -> void {
|
||||
if (selected == 1) {
|
||||
#if MESHTASTIC_EXCLUDE_GPS
|
||||
menuQueue = menu_none;
|
||||
#else
|
||||
if (selected == GPSToggle) {
|
||||
menuQueue = gps_toggle_menu;
|
||||
screen->runNow();
|
||||
#endif
|
||||
} else if (selected == 2) {
|
||||
} else if (selected == CompassMenu) {
|
||||
menuQueue = compass_point_north_menu;
|
||||
screen->runNow();
|
||||
} else if (selected == 3) {
|
||||
} else if (selected == CompassCalibrate) {
|
||||
accelerometerThread->calibrate(30);
|
||||
}
|
||||
};
|
||||
@ -486,16 +466,20 @@ void menuHandler::positionBaseMenu()
|
||||
|
||||
void menuHandler::nodeListMenu()
|
||||
{
|
||||
static const char *optionsArray[] = {"Back", "Add Favorite", "Reset NodeDB"};
|
||||
enum optionsNumbers { Back, Favorite, Verify, Reset };
|
||||
static const char *optionsArray[] = {"Back", "Add Favorite", "Key Verification", "Reset NodeDB"};
|
||||
BannerOverlayOptions bannerOptions;
|
||||
bannerOptions.message = "Node Action";
|
||||
bannerOptions.optionsArrayPtr = optionsArray;
|
||||
bannerOptions.optionsCount = 3;
|
||||
bannerOptions.optionsCount = 4;
|
||||
bannerOptions.bannerCallback = [](int selected) -> void {
|
||||
if (selected == 1) {
|
||||
if (selected == Favorite) {
|
||||
menuQueue = add_favorite;
|
||||
screen->runNow();
|
||||
} else if (selected == 2) {
|
||||
} else if (selected == Verify) {
|
||||
menuQueue = key_verification_init;
|
||||
screen->runNow();
|
||||
} else if (selected == Reset) {
|
||||
menuQueue = reset_node_db_menu;
|
||||
screen->runNow();
|
||||
}
|
||||
@ -523,6 +507,7 @@ void menuHandler::resetNodeDBMenu()
|
||||
|
||||
void menuHandler::compassNorthMenu()
|
||||
{
|
||||
enum optionsNumbers { Back, Dynamic, Fixed, Freeze };
|
||||
static const char *optionsArray[] = {"Back", "Dynamic", "Fixed Ring", "Freeze Heading"};
|
||||
BannerOverlayOptions bannerOptions;
|
||||
bannerOptions.message = "North Directions?";
|
||||
@ -530,28 +515,25 @@ void menuHandler::compassNorthMenu()
|
||||
bannerOptions.optionsCount = 4;
|
||||
bannerOptions.InitialSelected = uiconfig.compass_mode + 1;
|
||||
bannerOptions.bannerCallback = [](int selected) -> void {
|
||||
if (selected == 1) {
|
||||
if (selected == Dynamic) {
|
||||
if (uiconfig.compass_mode != meshtastic_CompassMode_DYNAMIC) {
|
||||
uiconfig.compass_mode = meshtastic_CompassMode_DYNAMIC;
|
||||
nodeDB->saveProto("/prefs/uiconfig.proto", meshtastic_DeviceUIConfig_size, &meshtastic_DeviceUIConfig_msg,
|
||||
&uiconfig);
|
||||
saveUIConfig();
|
||||
screen->setFrames(graphics::Screen::FOCUS_PRESERVE);
|
||||
}
|
||||
} else if (selected == 2) {
|
||||
} else if (selected == Fixed) {
|
||||
if (uiconfig.compass_mode != meshtastic_CompassMode_FIXED_RING) {
|
||||
uiconfig.compass_mode = meshtastic_CompassMode_FIXED_RING;
|
||||
nodeDB->saveProto("/prefs/uiconfig.proto", meshtastic_DeviceUIConfig_size, &meshtastic_DeviceUIConfig_msg,
|
||||
&uiconfig);
|
||||
saveUIConfig();
|
||||
screen->setFrames(graphics::Screen::FOCUS_PRESERVE);
|
||||
}
|
||||
} else if (selected == 3) {
|
||||
} else if (selected == Freeze) {
|
||||
if (uiconfig.compass_mode != meshtastic_CompassMode_FREEZE_HEADING) {
|
||||
uiconfig.compass_mode = meshtastic_CompassMode_FREEZE_HEADING;
|
||||
nodeDB->saveProto("/prefs/uiconfig.proto", meshtastic_DeviceUIConfig_size, &meshtastic_DeviceUIConfig_msg,
|
||||
&uiconfig);
|
||||
saveUIConfig();
|
||||
screen->setFrames(graphics::Screen::FOCUS_PRESERVE);
|
||||
}
|
||||
} else if (selected == 0) {
|
||||
} else if (selected == Back) {
|
||||
menuQueue = position_base_menu;
|
||||
screen->runNow();
|
||||
}
|
||||
@ -562,6 +544,7 @@ void menuHandler::compassNorthMenu()
|
||||
#if !MESHTASTIC_EXCLUDE_GPS
|
||||
void menuHandler::GPSToggleMenu()
|
||||
{
|
||||
|
||||
static const char *optionsArray[] = {"Back", "Enabled", "Disabled"};
|
||||
BannerOverlayOptions bannerOptions;
|
||||
bannerOptions.message = "Toggle GPS";
|
||||
@ -609,7 +592,7 @@ void menuHandler::BuzzerModeMenu()
|
||||
{
|
||||
static const char *optionsArray[] = {"All Enabled", "Disabled", "Notifications", "System Only"};
|
||||
BannerOverlayOptions bannerOptions;
|
||||
bannerOptions.message = "Beep Action";
|
||||
bannerOptions.message = "Buzzer Mode";
|
||||
bannerOptions.optionsArrayPtr = optionsArray;
|
||||
bannerOptions.optionsCount = 4;
|
||||
bannerOptions.bannerCallback = [](int selected) -> void {
|
||||
@ -659,7 +642,7 @@ void menuHandler::BrightnessPickerMenu()
|
||||
#endif
|
||||
|
||||
// Save to device
|
||||
nodeDB->saveProto("/prefs/uiconfig.proto", meshtastic_DeviceUIConfig_size, &meshtastic_DeviceUIConfig_msg, &uiconfig);
|
||||
saveUIConfig();
|
||||
|
||||
LOG_INFO("Screen brightness set to %d", uiconfig.screen_brightness);
|
||||
}
|
||||
@ -670,13 +653,13 @@ void menuHandler::BrightnessPickerMenu()
|
||||
|
||||
void menuHandler::switchToMUIMenu()
|
||||
{
|
||||
static const char *optionsArray[] = {"Yes", "No"};
|
||||
static const char *optionsArray[] = {"No", "Yes"};
|
||||
BannerOverlayOptions bannerOptions;
|
||||
bannerOptions.message = "Switch to MUI?";
|
||||
bannerOptions.optionsArrayPtr = optionsArray;
|
||||
bannerOptions.optionsCount = 2;
|
||||
bannerOptions.bannerCallback = [](int selected) -> void {
|
||||
if (selected == 0) {
|
||||
if (selected == 1) {
|
||||
config.display.displaymode = meshtastic_Config_DisplayConfig_DisplayMode_COLOR;
|
||||
config.bluetooth.enabled = false;
|
||||
service->reloadConfig(SEGMENT_CONFIG);
|
||||
@ -741,6 +724,9 @@ void menuHandler::TFTColorPickerMenu(OLEDDisplay *display)
|
||||
TFT_MESH_r = 255;
|
||||
TFT_MESH_g = 255;
|
||||
TFT_MESH_b = 255;
|
||||
} else {
|
||||
menuQueue = system_base_menu;
|
||||
screen->runNow();
|
||||
}
|
||||
|
||||
#if defined(HELTEC_MESH_NODE_T114) || defined(HELTEC_VISION_MASTER_T190) || HAS_TFT
|
||||
@ -770,7 +756,7 @@ void menuHandler::TFTColorPickerMenu(OLEDDisplay *display)
|
||||
uiconfig.screen_rgb_color = (TFT_MESH_r << 16) | (TFT_MESH_g << 8) | TFT_MESH_b;
|
||||
}
|
||||
LOG_INFO("Storing Value of %d to uiconfig.screen_rgb_color", uiconfig.screen_rgb_color);
|
||||
nodeDB->saveProto("/prefs/uiconfig.proto", meshtastic_DeviceUIConfig_size, &meshtastic_DeviceUIConfig_msg, &uiconfig);
|
||||
saveUIConfig();
|
||||
}
|
||||
#endif
|
||||
};
|
||||
@ -789,6 +775,29 @@ void menuHandler::rebootMenu()
|
||||
IF_SCREEN(screen->showSimpleBanner("Rebooting...", 0));
|
||||
nodeDB->saveToDisk();
|
||||
rebootAtMsec = millis() + DEFAULT_REBOOT_SECONDS * 1000;
|
||||
} else {
|
||||
menuQueue = power_menu;
|
||||
screen->runNow();
|
||||
}
|
||||
};
|
||||
screen->showOverlayBanner(bannerOptions);
|
||||
}
|
||||
|
||||
void menuHandler::shutdownMenu()
|
||||
{
|
||||
static const char *optionsArray[] = {"Back", "Confirm"};
|
||||
BannerOverlayOptions bannerOptions;
|
||||
bannerOptions.message = "Shutdown Device?";
|
||||
bannerOptions.optionsArrayPtr = optionsArray;
|
||||
bannerOptions.optionsCount = 2;
|
||||
bannerOptions.bannerCallback = [](int selected) -> void {
|
||||
if (selected == 1) {
|
||||
IF_SCREEN(screen->showSimpleBanner("Shutting Down...", 0));
|
||||
nodeDB->saveToDisk();
|
||||
power->shutdown();
|
||||
} else {
|
||||
menuQueue = power_menu;
|
||||
screen->runNow();
|
||||
}
|
||||
};
|
||||
screen->showOverlayBanner(bannerOptions);
|
||||
@ -796,7 +805,7 @@ void menuHandler::rebootMenu()
|
||||
|
||||
void menuHandler::addFavoriteMenu()
|
||||
{
|
||||
screen->showNodePicker("Node To Favorite", 30000, [](int nodenum) -> void {
|
||||
screen->showNodePicker("Node To Favorite", 30000, [](uint32_t nodenum) -> void {
|
||||
LOG_WARN("Nodenum: %u", nodenum);
|
||||
nodeDB->set_favorite(true, nodenum);
|
||||
screen->setFrames(graphics::Screen::FOCUS_PRESERVE);
|
||||
@ -887,6 +896,148 @@ void menuHandler::wifiToggleMenu()
|
||||
screen->showOverlayBanner(bannerOptions);
|
||||
}
|
||||
|
||||
void menuHandler::notificationsMenu()
|
||||
{
|
||||
enum optionsNumbers { Back, BuzzerActions };
|
||||
static const char *optionsArray[] = {"Back", "Buzzer Actions"};
|
||||
static int optionsEnumArray[] = {Back, BuzzerActions};
|
||||
int options = 2;
|
||||
|
||||
BannerOverlayOptions bannerOptions;
|
||||
bannerOptions.message = "Notifications";
|
||||
bannerOptions.optionsArrayPtr = optionsArray;
|
||||
bannerOptions.optionsCount = options;
|
||||
bannerOptions.optionsEnumPtr = optionsEnumArray;
|
||||
bannerOptions.bannerCallback = [](int selected) -> void {
|
||||
if (selected == BuzzerActions) {
|
||||
menuHandler::menuQueue = menuHandler::buzzermodemenupicker;
|
||||
screen->runNow();
|
||||
} else {
|
||||
menuQueue = system_base_menu;
|
||||
screen->runNow();
|
||||
}
|
||||
};
|
||||
screen->showOverlayBanner(bannerOptions);
|
||||
}
|
||||
|
||||
void menuHandler::screenOptionsMenu()
|
||||
{
|
||||
// Check if brightness is supported
|
||||
bool hasSupportBrightness = false;
|
||||
#if defined(ST7789_CS) || defined(USE_OLED) || defined(USE_SSD1306) || defined(USE_SH1106) || defined(USE_SH1107) || HAS_TFT
|
||||
hasSupportBrightness = true;
|
||||
#endif
|
||||
|
||||
enum optionsNumbers { Back, Brightness, ScreenColor };
|
||||
static const char *optionsArray[4] = {"Back"};
|
||||
static int optionsEnumArray[4] = {Back};
|
||||
int options = 1;
|
||||
|
||||
// Only show brightness for B&W displays
|
||||
if (hasSupportBrightness && !HAS_TFT) {
|
||||
optionsArray[options] = "Brightness";
|
||||
optionsEnumArray[options++] = Brightness;
|
||||
}
|
||||
|
||||
// Only show screen color for TFT displays
|
||||
#if defined(HELTEC_MESH_NODE_T114) || defined(HELTEC_VISION_MASTER_T190) || HAS_TFT
|
||||
optionsArray[options] = "Screen Color";
|
||||
optionsEnumArray[options++] = ScreenColor;
|
||||
#endif
|
||||
|
||||
BannerOverlayOptions bannerOptions;
|
||||
bannerOptions.message = "Screen Options";
|
||||
bannerOptions.optionsArrayPtr = optionsArray;
|
||||
bannerOptions.optionsCount = options;
|
||||
bannerOptions.optionsEnumPtr = optionsEnumArray;
|
||||
bannerOptions.bannerCallback = [](int selected) -> void {
|
||||
if (selected == Brightness) {
|
||||
menuHandler::menuQueue = menuHandler::brightness_picker;
|
||||
screen->runNow();
|
||||
} else if (selected == ScreenColor) {
|
||||
menuHandler::menuQueue = menuHandler::tftcolormenupicker;
|
||||
screen->runNow();
|
||||
} else {
|
||||
menuQueue = system_base_menu;
|
||||
screen->runNow();
|
||||
}
|
||||
};
|
||||
screen->showOverlayBanner(bannerOptions);
|
||||
}
|
||||
|
||||
void menuHandler::powerMenu()
|
||||
{
|
||||
|
||||
enum optionsNumbers { Back, Reboot, Shutdown, MUI };
|
||||
static const char *optionsArray[4] = {"Back"};
|
||||
static int optionsEnumArray[4] = {Back};
|
||||
int options = 1;
|
||||
|
||||
optionsArray[options] = "Reboot";
|
||||
optionsEnumArray[options++] = Reboot;
|
||||
|
||||
optionsArray[options] = "Shutdown";
|
||||
optionsEnumArray[options++] = Shutdown;
|
||||
|
||||
#if HAS_TFT
|
||||
optionsArray[options] = "Switch to MUI";
|
||||
optionsEnumArray[options++] = MUI;
|
||||
#endif
|
||||
|
||||
BannerOverlayOptions bannerOptions;
|
||||
bannerOptions.message = "Reboot / Shutdown";
|
||||
bannerOptions.optionsArrayPtr = optionsArray;
|
||||
bannerOptions.optionsCount = options;
|
||||
bannerOptions.optionsEnumPtr = optionsEnumArray;
|
||||
bannerOptions.bannerCallback = [](int selected) -> void {
|
||||
if (selected == Reboot) {
|
||||
menuHandler::menuQueue = menuHandler::reboot_menu;
|
||||
screen->runNow();
|
||||
} else if (selected == Shutdown) {
|
||||
menuHandler::menuQueue = menuHandler::shutdown_menu;
|
||||
screen->runNow();
|
||||
} else if (selected == MUI) {
|
||||
menuHandler::menuQueue = menuHandler::mui_picker;
|
||||
screen->runNow();
|
||||
} else {
|
||||
menuQueue = system_base_menu;
|
||||
screen->runNow();
|
||||
}
|
||||
};
|
||||
screen->showOverlayBanner(bannerOptions);
|
||||
}
|
||||
|
||||
void menuHandler::keyVerificationInitMenu()
|
||||
{
|
||||
screen->showNodePicker("Node to Verify", 30000,
|
||||
[](uint32_t selected) -> void { keyVerificationModule->sendInitialRequest(selected); });
|
||||
}
|
||||
|
||||
void menuHandler::keyVerificationFinalPrompt()
|
||||
{
|
||||
char message[40] = {0};
|
||||
memset(message, 0, sizeof(message));
|
||||
sprintf(message, "Verification: \n");
|
||||
keyVerificationModule->generateVerificationCode(message + 15); // send the toPhone packet
|
||||
|
||||
if (screen) {
|
||||
static const char *optionsArray[] = {"Reject", "Accept"};
|
||||
graphics::BannerOverlayOptions options;
|
||||
options.message = message;
|
||||
options.durationMs = 30000;
|
||||
options.optionsArrayPtr = optionsArray;
|
||||
options.optionsCount = 2;
|
||||
options.notificationType = graphics::notificationTypeEnum::selection_picker;
|
||||
options.bannerCallback = [=](int selected) {
|
||||
if (selected == 1) {
|
||||
auto remoteNodePtr = nodeDB->getMeshNode(keyVerificationModule->getCurrentRemoteNode());
|
||||
remoteNodePtr->bitfield |= NODEINFO_BITFIELD_IS_KEY_MANUALLY_VERIFIED_MASK;
|
||||
}
|
||||
};
|
||||
screen->showOverlayBanner(options);
|
||||
}
|
||||
}
|
||||
|
||||
void menuHandler::handleMenuSwitch(OLEDDisplay *display)
|
||||
{
|
||||
if (menuQueue != menu_none)
|
||||
@ -909,6 +1060,9 @@ void menuHandler::handleMenuSwitch(OLEDDisplay *display)
|
||||
case clock_menu:
|
||||
clockMenu();
|
||||
break;
|
||||
case system_base_menu:
|
||||
systemBaseMenu();
|
||||
break;
|
||||
case position_base_menu:
|
||||
positionBaseMenu();
|
||||
break;
|
||||
@ -938,6 +1092,9 @@ void menuHandler::handleMenuSwitch(OLEDDisplay *display)
|
||||
case reboot_menu:
|
||||
rebootMenu();
|
||||
break;
|
||||
case shutdown_menu:
|
||||
shutdownMenu();
|
||||
break;
|
||||
case add_favorite:
|
||||
addFavoriteMenu();
|
||||
break;
|
||||
@ -953,13 +1110,36 @@ void menuHandler::handleMenuSwitch(OLEDDisplay *display)
|
||||
case wifi_toggle_menu:
|
||||
wifiToggleMenu();
|
||||
break;
|
||||
case key_verification_init:
|
||||
keyVerificationInitMenu();
|
||||
break;
|
||||
case key_verification_final_prompt:
|
||||
keyVerificationFinalPrompt();
|
||||
break;
|
||||
case bluetooth_toggle_menu:
|
||||
BluetoothToggleMenu();
|
||||
break;
|
||||
case notifications_menu:
|
||||
notificationsMenu();
|
||||
break;
|
||||
case screen_options_menu:
|
||||
screenOptionsMenu();
|
||||
break;
|
||||
case power_menu:
|
||||
powerMenu();
|
||||
break;
|
||||
case throttle_message:
|
||||
screen->showSimpleBanner("Too Many Attempts\nTry again in 60 seconds.", 5000);
|
||||
break;
|
||||
}
|
||||
menuQueue = menu_none;
|
||||
}
|
||||
|
||||
void menuHandler::saveUIConfig()
|
||||
{
|
||||
nodeDB->saveProto("/prefs/uiconfig.proto", meshtastic_DeviceUIConfig_size, &meshtastic_DeviceUIConfig_msg, &uiconfig);
|
||||
}
|
||||
|
||||
} // namespace graphics
|
||||
|
||||
#endif
|
@ -1,3 +1,5 @@
|
||||
#pragma once
|
||||
#if HAS_SCREEN
|
||||
#include "configuration.h"
|
||||
namespace graphics
|
||||
{
|
||||
@ -21,12 +23,20 @@ class menuHandler
|
||||
tftcolormenupicker,
|
||||
brightness_picker,
|
||||
reboot_menu,
|
||||
shutdown_menu,
|
||||
add_favorite,
|
||||
remove_favorite,
|
||||
test_menu,
|
||||
number_test,
|
||||
wifi_toggle_menu,
|
||||
bluetooth_toggle_menu
|
||||
bluetooth_toggle_menu,
|
||||
notifications_menu,
|
||||
screen_options_menu,
|
||||
power_menu,
|
||||
system_base_menu,
|
||||
key_verification_init,
|
||||
key_verification_final_prompt,
|
||||
throttle_message
|
||||
};
|
||||
static screenMenus menuQueue;
|
||||
|
||||
@ -50,13 +60,23 @@ class menuHandler
|
||||
static void resetNodeDBMenu();
|
||||
static void BrightnessPickerMenu();
|
||||
static void rebootMenu();
|
||||
static void shutdownMenu();
|
||||
static void addFavoriteMenu();
|
||||
static void removeFavoriteMenu();
|
||||
static void testMenu();
|
||||
static void numberTest();
|
||||
static void wifiBaseMenu();
|
||||
static void wifiToggleMenu();
|
||||
static void notificationsMenu();
|
||||
static void screenOptionsMenu();
|
||||
static void powerMenu();
|
||||
|
||||
private:
|
||||
static void saveUIConfig();
|
||||
static void keyVerificationInitMenu();
|
||||
static void keyVerificationFinalPrompt();
|
||||
static void BluetoothToggleMenu();
|
||||
};
|
||||
|
||||
} // namespace graphics
|
||||
} // namespace graphics
|
||||
#endif
|
@ -26,7 +26,7 @@ extern bool hasUnreadMessage;
|
||||
namespace graphics
|
||||
{
|
||||
|
||||
char NotificationRenderer::inEvent = INPUT_BROKER_NONE;
|
||||
InputEvent NotificationRenderer::inEvent;
|
||||
int8_t NotificationRenderer::curSelected = 0;
|
||||
char NotificationRenderer::alertBannerMessage[256] = {0};
|
||||
uint32_t NotificationRenderer::alertBannerUntil = 0; // 0 is a special case meaning forever
|
||||
@ -72,11 +72,25 @@ void NotificationRenderer::resetBanner()
|
||||
{
|
||||
alertBannerMessage[0] = '\0';
|
||||
current_notification_type = notificationTypeEnum::none;
|
||||
|
||||
inEvent.inputEvent = INPUT_BROKER_NONE;
|
||||
inEvent.kbchar = 0;
|
||||
curSelected = 0;
|
||||
alertBannerOptions = 0; // last x lines are seelctable options
|
||||
optionsArrayPtr = nullptr;
|
||||
optionsEnumPtr = nullptr;
|
||||
alertBannerCallback = NULL;
|
||||
pauseBanner = false;
|
||||
numDigits = 0;
|
||||
currentNumber = 0;
|
||||
|
||||
nodeDB->pause_sort(false);
|
||||
}
|
||||
|
||||
void NotificationRenderer::drawBannercallback(OLEDDisplay *display, OLEDDisplayUiState *state)
|
||||
{
|
||||
if (!isOverlayBannerShowing() && alertBannerMessage[0] != '\0')
|
||||
resetBanner();
|
||||
if (!isOverlayBannerShowing() || pauseBanner)
|
||||
return;
|
||||
switch (current_notification_type) {
|
||||
@ -115,31 +129,40 @@ void NotificationRenderer::drawNumberPicker(OLEDDisplay *display, OLEDDisplayUiS
|
||||
// modulo to extract
|
||||
uint8_t this_digit = (currentNumber % (pow_of_10(numDigits - curSelected))) / (pow_of_10(numDigits - curSelected - 1));
|
||||
// Handle input
|
||||
if (inEvent == INPUT_BROKER_UP || inEvent == INPUT_BROKER_ALT_PRESS) {
|
||||
if (inEvent.inputEvent == INPUT_BROKER_UP || inEvent.inputEvent == INPUT_BROKER_ALT_PRESS) {
|
||||
if (this_digit == 9) {
|
||||
currentNumber -= 9 * (pow_of_10(numDigits - curSelected - 1));
|
||||
} else {
|
||||
currentNumber += (pow_of_10(numDigits - curSelected - 1));
|
||||
}
|
||||
} else if (inEvent == INPUT_BROKER_DOWN || inEvent == INPUT_BROKER_USER_PRESS) {
|
||||
} else if (inEvent.inputEvent == INPUT_BROKER_DOWN || inEvent.inputEvent == INPUT_BROKER_USER_PRESS) {
|
||||
if (this_digit == 0) {
|
||||
currentNumber += 9 * (pow_of_10(numDigits - curSelected - 1));
|
||||
} else {
|
||||
currentNumber -= (pow_of_10(numDigits - curSelected - 1));
|
||||
}
|
||||
} else if (inEvent == INPUT_BROKER_SELECT || inEvent == INPUT_BROKER_RIGHT) {
|
||||
} else if (inEvent.inputEvent == INPUT_BROKER_ANYKEY) {
|
||||
if (inEvent.kbchar > 47 && inEvent.kbchar < 58) { // have a digit
|
||||
currentNumber -= this_digit * (pow_of_10(numDigits - curSelected - 1));
|
||||
currentNumber += (inEvent.kbchar - 48) * (pow_of_10(numDigits - curSelected - 1));
|
||||
curSelected++;
|
||||
}
|
||||
} else if (inEvent.inputEvent == INPUT_BROKER_SELECT || inEvent.inputEvent == INPUT_BROKER_RIGHT) {
|
||||
curSelected++;
|
||||
} else if (inEvent == INPUT_BROKER_LEFT) {
|
||||
} else if (inEvent.inputEvent == INPUT_BROKER_LEFT) {
|
||||
curSelected--;
|
||||
} else if ((inEvent == INPUT_BROKER_CANCEL || inEvent == INPUT_BROKER_ALT_LONG) && alertBannerUntil != 0) {
|
||||
} else if ((inEvent.inputEvent == INPUT_BROKER_CANCEL || inEvent.inputEvent == INPUT_BROKER_ALT_LONG) &&
|
||||
alertBannerUntil != 0) {
|
||||
resetBanner();
|
||||
return;
|
||||
}
|
||||
if (curSelected == numDigits) {
|
||||
resetBanner();
|
||||
alertBannerCallback(currentNumber);
|
||||
resetBanner();
|
||||
return;
|
||||
}
|
||||
|
||||
inEvent = INPUT_BROKER_NONE;
|
||||
inEvent.inputEvent = INPUT_BROKER_NONE;
|
||||
if (alertBannerMessage[0] == '\0')
|
||||
return;
|
||||
|
||||
@ -193,16 +216,18 @@ void NotificationRenderer::drawNodePicker(OLEDDisplay *display, OLEDDisplayUiSta
|
||||
}
|
||||
|
||||
// Handle input
|
||||
if (inEvent == INPUT_BROKER_UP || inEvent == INPUT_BROKER_ALT_PRESS) {
|
||||
if (inEvent.inputEvent == INPUT_BROKER_UP || inEvent.inputEvent == INPUT_BROKER_ALT_PRESS) {
|
||||
curSelected--;
|
||||
} else if (inEvent == INPUT_BROKER_DOWN || inEvent == INPUT_BROKER_USER_PRESS) {
|
||||
} else if (inEvent.inputEvent == INPUT_BROKER_DOWN || inEvent.inputEvent == INPUT_BROKER_USER_PRESS) {
|
||||
curSelected++;
|
||||
} else if (inEvent == INPUT_BROKER_SELECT) {
|
||||
resetBanner();
|
||||
} else if (inEvent.inputEvent == INPUT_BROKER_SELECT) {
|
||||
alertBannerCallback(selectedNodenum);
|
||||
|
||||
} else if ((inEvent == INPUT_BROKER_CANCEL || inEvent == INPUT_BROKER_ALT_LONG) && alertBannerUntil != 0) {
|
||||
resetBanner();
|
||||
return;
|
||||
} else if ((inEvent.inputEvent == INPUT_BROKER_CANCEL || inEvent.inputEvent == INPUT_BROKER_ALT_LONG) &&
|
||||
alertBannerUntil != 0) {
|
||||
resetBanner();
|
||||
return;
|
||||
}
|
||||
|
||||
if (curSelected == -1)
|
||||
@ -210,7 +235,7 @@ void NotificationRenderer::drawNodePicker(OLEDDisplay *display, OLEDDisplayUiSta
|
||||
if (curSelected == alertBannerOptions)
|
||||
curSelected = 0;
|
||||
|
||||
inEvent = INPUT_BROKER_NONE;
|
||||
inEvent.inputEvent = INPUT_BROKER_NONE;
|
||||
if (alertBannerMessage[0] == '\0')
|
||||
return;
|
||||
|
||||
@ -308,11 +333,11 @@ void NotificationRenderer::drawAlertBannerOverlay(OLEDDisplay *display, OLEDDisp
|
||||
|
||||
// Handle input
|
||||
if (alertBannerOptions > 0) {
|
||||
if (inEvent == INPUT_BROKER_UP || inEvent == INPUT_BROKER_ALT_PRESS) {
|
||||
if (inEvent.inputEvent == INPUT_BROKER_UP || inEvent.inputEvent == INPUT_BROKER_ALT_PRESS) {
|
||||
curSelected--;
|
||||
} else if (inEvent == INPUT_BROKER_DOWN || inEvent == INPUT_BROKER_USER_PRESS) {
|
||||
} else if (inEvent.inputEvent == INPUT_BROKER_DOWN || inEvent.inputEvent == INPUT_BROKER_USER_PRESS) {
|
||||
curSelected++;
|
||||
} else if (inEvent == INPUT_BROKER_SELECT) {
|
||||
} else if (inEvent.inputEvent == INPUT_BROKER_SELECT) {
|
||||
if (optionsEnumPtr != nullptr) {
|
||||
alertBannerCallback(optionsEnumPtr[curSelected]);
|
||||
optionsEnumPtr = nullptr;
|
||||
@ -320,8 +345,11 @@ void NotificationRenderer::drawAlertBannerOverlay(OLEDDisplay *display, OLEDDisp
|
||||
alertBannerCallback(curSelected);
|
||||
}
|
||||
resetBanner();
|
||||
} else if ((inEvent == INPUT_BROKER_CANCEL || inEvent == INPUT_BROKER_ALT_LONG) && alertBannerUntil != 0) {
|
||||
return;
|
||||
} else if ((inEvent.inputEvent == INPUT_BROKER_CANCEL || inEvent.inputEvent == INPUT_BROKER_ALT_LONG) &&
|
||||
alertBannerUntil != 0) {
|
||||
resetBanner();
|
||||
return;
|
||||
}
|
||||
|
||||
if (curSelected == -1)
|
||||
@ -329,12 +357,14 @@ void NotificationRenderer::drawAlertBannerOverlay(OLEDDisplay *display, OLEDDisp
|
||||
if (curSelected == alertBannerOptions)
|
||||
curSelected = 0;
|
||||
} else {
|
||||
if (inEvent == INPUT_BROKER_SELECT || inEvent == INPUT_BROKER_ALT_LONG || inEvent == INPUT_BROKER_CANCEL) {
|
||||
if (inEvent.inputEvent == INPUT_BROKER_SELECT || inEvent.inputEvent == INPUT_BROKER_ALT_LONG ||
|
||||
inEvent.inputEvent == INPUT_BROKER_CANCEL) {
|
||||
resetBanner();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
inEvent = INPUT_BROKER_NONE;
|
||||
inEvent.inputEvent = INPUT_BROKER_NONE;
|
||||
if (alertBannerMessage[0] == '\0')
|
||||
return;
|
||||
|
||||
|
@ -11,7 +11,8 @@ namespace graphics
|
||||
class NotificationRenderer
|
||||
{
|
||||
public:
|
||||
static char inEvent;
|
||||
static InputEvent inEvent;
|
||||
static char inKeypress;
|
||||
static int8_t curSelected;
|
||||
static char alertBannerMessage[256];
|
||||
static uint32_t alertBannerUntil; // 0 is a special case meaning forever
|
||||
|
@ -13,8 +13,9 @@ template <class T> constexpr const T &clamp(const T &v, const T &lo, const T &hi
|
||||
|
||||
#if HAS_SCREEN
|
||||
#define IF_SCREEN(X) \
|
||||
if (screen) \
|
||||
X;
|
||||
if (screen) { \
|
||||
X; \
|
||||
}
|
||||
#else
|
||||
#define IF_SCREEN(...)
|
||||
#endif
|
||||
|
@ -1327,12 +1327,6 @@ void AdminModule::handleSendInputEvent(const meshtastic_AdminMessage_InputEvent
|
||||
LOG_DEBUG("Processing input event: event_code=%u, kb_char=%u, touch_x=%u, touch_y=%u", inputEvent.event_code,
|
||||
inputEvent.kb_char, inputEvent.touch_x, inputEvent.touch_y);
|
||||
|
||||
// Validate input parameters
|
||||
if (inputEvent.event_code > INPUT_BROKER_ANYKEY) {
|
||||
LOG_WARN("Invalid input event code: %u", inputEvent.event_code);
|
||||
return;
|
||||
}
|
||||
|
||||
// Create InputEvent for injection
|
||||
InputEvent event = {.inputEvent = (input_broker_event)inputEvent.event_code,
|
||||
.kbchar = (unsigned char)inputEvent.kb_char,
|
||||
|
@ -716,7 +716,7 @@ bool CannedMessageModule::handleFreeTextInput(const InputEvent *event)
|
||||
}
|
||||
|
||||
// Backspace
|
||||
if (event->inputEvent == INPUT_BROKER_BACK) {
|
||||
if (event->inputEvent == INPUT_BROKER_BACK && this->freetext.length() > 0) {
|
||||
payload = 0x08;
|
||||
lastTouchMillis = millis();
|
||||
runOnce();
|
||||
@ -739,7 +739,8 @@ bool CannedMessageModule::handleFreeTextInput(const InputEvent *event)
|
||||
}
|
||||
|
||||
// Cancel (dismiss freetext screen)
|
||||
if (event->inputEvent == INPUT_BROKER_CANCEL || event->inputEvent == INPUT_BROKER_ALT_LONG) {
|
||||
if (event->inputEvent == INPUT_BROKER_CANCEL || event->inputEvent == INPUT_BROKER_ALT_LONG ||
|
||||
(event->inputEvent == INPUT_BROKER_BACK && this->freetext.length() == 0)) {
|
||||
runState = CANNED_MESSAGE_RUN_STATE_INACTIVE;
|
||||
freetext = "";
|
||||
cursor = 0;
|
||||
@ -989,6 +990,7 @@ int32_t CannedMessageModule::runOnce()
|
||||
}
|
||||
this->cursor--;
|
||||
}
|
||||
} else {
|
||||
}
|
||||
break;
|
||||
case INPUT_BROKER_MSG_TAB: // Tab key: handled by input handler
|
||||
|
@ -2,7 +2,9 @@
|
||||
#include "KeyVerificationModule.h"
|
||||
#include "MeshService.h"
|
||||
#include "RTC.h"
|
||||
#include "graphics/draw/MenuHandler.h"
|
||||
#include "main.h"
|
||||
#include "meshUtils.h"
|
||||
#include "modules/AdminModule.h"
|
||||
#include <SHA256.h>
|
||||
|
||||
@ -48,18 +50,22 @@ AdminMessageHandleResult KeyVerificationModule::handleAdminMessageForModule(cons
|
||||
bool KeyVerificationModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshtastic_KeyVerification *r)
|
||||
{
|
||||
updateState();
|
||||
if (mp.pki_encrypted == false)
|
||||
if (mp.pki_encrypted == false) {
|
||||
return false;
|
||||
if (mp.from != currentRemoteNode) // because the inital connection request is handled in allocReply()
|
||||
}
|
||||
if (mp.from != currentRemoteNode) { // because the inital connection request is handled in allocReply()
|
||||
return false;
|
||||
}
|
||||
if (currentState == KEY_VERIFICATION_IDLE) {
|
||||
return false; // if we're idle, the only acceptable message is an init, which should be handled by allocReply()
|
||||
}
|
||||
|
||||
} else if (currentState == KEY_VERIFICATION_SENDER_HAS_INITIATED && r->nonce == currentNonce && r->hash2.size == 32 &&
|
||||
r->hash1.size == 0) {
|
||||
if (currentState == KEY_VERIFICATION_SENDER_HAS_INITIATED && r->nonce == currentNonce && r->hash2.size == 32 &&
|
||||
r->hash1.size == 0) {
|
||||
memcpy(hash2, r->hash2.bytes, 32);
|
||||
if (screen)
|
||||
screen->showSimpleBanner("Enter Security Number", 30000);
|
||||
IF_SCREEN(screen->showNumberPicker("Enter Security Number", 60000, 6, [](int number_picked) -> void {
|
||||
keyVerificationModule->processSecurityNumber(number_picked);
|
||||
});)
|
||||
|
||||
meshtastic_ClientNotification *cn = clientNotificationPool.allocZeroed();
|
||||
cn->level = meshtastic_LogRecord_Level_WARNING;
|
||||
@ -79,23 +85,20 @@ bool KeyVerificationModule::handleReceivedProtobuf(const meshtastic_MeshPacket &
|
||||
memset(message, 0, sizeof(message));
|
||||
sprintf(message, "Verification: \n");
|
||||
generateVerificationCode(message + 15);
|
||||
static const char *optionsArray[] = {"ACCEPT", "REJECT"};
|
||||
LOG_INFO("Hash1 matches!");
|
||||
if (screen) {
|
||||
graphics::BannerOverlayOptions options;
|
||||
options.message = message;
|
||||
options.durationMs = 30000;
|
||||
options.optionsArrayPtr = optionsArray;
|
||||
options.optionsCount = 2;
|
||||
options.notificationType = graphics::notificationTypeEnum::selection_picker;
|
||||
options.bannerCallback = [=](int selected) {
|
||||
if (selected == 0) {
|
||||
auto remoteNodePtr = nodeDB->getMeshNode(currentRemoteNode);
|
||||
remoteNodePtr->bitfield |= NODEINFO_BITFIELD_IS_KEY_MANUALLY_VERIFIED_MASK;
|
||||
}
|
||||
};
|
||||
screen->showOverlayBanner(options);
|
||||
}
|
||||
static const char *optionsArray[] = {"Reject", "Accept"};
|
||||
// Don't try to put the array definition in the macro. Does not work with curly braces.
|
||||
IF_SCREEN(graphics::BannerOverlayOptions options; options.message = message; options.durationMs = 30000;
|
||||
options.optionsArrayPtr = optionsArray; options.optionsCount = 2;
|
||||
options.notificationType = graphics::notificationTypeEnum::selection_picker;
|
||||
options.bannerCallback =
|
||||
[=](int selected) {
|
||||
if (selected == 1) {
|
||||
auto remoteNodePtr = nodeDB->getMeshNode(currentRemoteNode);
|
||||
remoteNodePtr->bitfield |= NODEINFO_BITFIELD_IS_KEY_MANUALLY_VERIFIED_MASK;
|
||||
}
|
||||
};
|
||||
screen->showOverlayBanner(options);)
|
||||
meshtastic_ClientNotification *cn = clientNotificationPool.allocZeroed();
|
||||
cn->level = meshtastic_LogRecord_Level_WARNING;
|
||||
sprintf(cn->message, "Final confirmation for incoming manual key verification %s", message);
|
||||
@ -120,6 +123,7 @@ bool KeyVerificationModule::sendInitialRequest(NodeNum remoteNode)
|
||||
// generate nonce
|
||||
updateState();
|
||||
if (currentState != KEY_VERIFICATION_IDLE) {
|
||||
IF_SCREEN(graphics::menuHandler::menuQueue = graphics::menuHandler::throttle_message;)
|
||||
return false;
|
||||
}
|
||||
currentNonce = random();
|
||||
@ -190,11 +194,8 @@ meshtastic_MeshPacket *KeyVerificationModule::allocReply()
|
||||
responsePacket = allocDataProtobuf(response);
|
||||
|
||||
responsePacket->pki_encrypted = true;
|
||||
if (screen) {
|
||||
snprintf(message, 25, "Security Number \n%03u %03u", currentSecurityNumber / 1000, currentSecurityNumber % 1000);
|
||||
screen->showSimpleBanner(message, 30000);
|
||||
LOG_WARN("%s", message);
|
||||
}
|
||||
IF_SCREEN(snprintf(message, 25, "Security Number \n%03u %03u", currentSecurityNumber / 1000, currentSecurityNumber % 1000);
|
||||
screen->showSimpleBanner(message, 30000); LOG_WARN("%s", message);)
|
||||
meshtastic_ClientNotification *cn = clientNotificationPool.allocZeroed();
|
||||
cn->level = meshtastic_LogRecord_Level_WARNING;
|
||||
sprintf(cn->message, "Incoming Key Verification.\nSecurity Number\n%03u %03u", currentSecurityNumber / 1000,
|
||||
@ -258,12 +259,7 @@ void KeyVerificationModule::processSecurityNumber(uint32_t incomingNumber)
|
||||
p->priority = meshtastic_MeshPacket_Priority_HIGH;
|
||||
service->sendToMesh(p, RX_SRC_LOCAL, true);
|
||||
currentState = KEY_VERIFICATION_SENDER_AWAITING_USER;
|
||||
memset(message, 0, sizeof(message));
|
||||
sprintf(message, "Verification: \n");
|
||||
generateVerificationCode(message + 15); // send the toPhone packet
|
||||
if (screen) {
|
||||
screen->showSimpleBanner(message, 30000);
|
||||
}
|
||||
IF_SCREEN(screen->requestMenu(graphics::menuHandler::key_verification_final_prompt);)
|
||||
meshtastic_ClientNotification *cn = clientNotificationPool.allocZeroed();
|
||||
cn->level = meshtastic_LogRecord_Level_WARNING;
|
||||
sprintf(cn->message, "Final confirmation for outgoing manual key verification %s", message);
|
||||
@ -282,7 +278,7 @@ void KeyVerificationModule::processSecurityNumber(uint32_t incomingNumber)
|
||||
void KeyVerificationModule::updateState()
|
||||
{
|
||||
if (currentState != KEY_VERIFICATION_IDLE) {
|
||||
// check for the 30 second timeout
|
||||
// check for the 60 second timeout
|
||||
if (currentNonceTimestamp < getTime() - 60) {
|
||||
resetToIdle();
|
||||
} else {
|
||||
|
@ -27,6 +27,8 @@ class KeyVerificationModule : public ProtobufModule<meshtastic_KeyVerification>
|
||||
}*/
|
||||
virtual bool wantUIFrame() { return false; };
|
||||
bool sendInitialRequest(NodeNum remoteNode);
|
||||
void generateVerificationCode(char *); // fills char with the user readable verification code
|
||||
uint32_t getCurrentRemoteNode() { return currentRemoteNode; }
|
||||
|
||||
protected:
|
||||
/* Called to handle a particular incoming message
|
||||
@ -56,9 +58,8 @@ class KeyVerificationModule : public ProtobufModule<meshtastic_KeyVerification>
|
||||
char message[40] = {0};
|
||||
|
||||
void processSecurityNumber(uint32_t);
|
||||
void updateState(); // check the timeouts and maybe reset the state to idle
|
||||
void resetToIdle(); // Zero out module state
|
||||
void generateVerificationCode(char *); // fills char with the user readable verification code
|
||||
void updateState(); // check the timeouts and maybe reset the state to idle
|
||||
void resetToIdle(); // Zero out module state
|
||||
};
|
||||
|
||||
extern KeyVerificationModule *keyVerificationModule;
|
@ -4,6 +4,7 @@
|
||||
#include "PowerFSM.h"
|
||||
#include "buzz.h"
|
||||
#include "configuration.h"
|
||||
#include "graphics/Screen.h"
|
||||
TextMessageModule *textMessageModule;
|
||||
|
||||
ProcessMessage TextMessageModule::handleReceived(const meshtastic_MeshPacket &mp)
|
||||
@ -17,7 +18,11 @@ ProcessMessage TextMessageModule::handleReceived(const meshtastic_MeshPacket &mp
|
||||
devicestate.rx_text_message = mp;
|
||||
devicestate.has_rx_text_message = true;
|
||||
|
||||
powerFSM.trigger(EVENT_RECEIVED_MSG);
|
||||
wake_on_received_message = shouldWakeOnReceivedMessage();
|
||||
// Only trigger screen wake if configuration allows it
|
||||
if (wake_on_received_message) {
|
||||
powerFSM.trigger(EVENT_RECEIVED_MSG);
|
||||
}
|
||||
notifyObservers(&mp);
|
||||
|
||||
return ProcessMessage::CONTINUE; // Let others look at this message also if they want
|
||||
|
@ -1,4 +1,4 @@
|
||||
; Seeed Xiao BLE: https://www.digikey.com/en/products/detail/seeed-technology-co-ltd/102010448/16652893
|
||||
; Seeed Xiao BLE: https://wiki.seeedstudio.com/XIAO_BLE/
|
||||
[env:seeed_xiao_nrf52840_kit]
|
||||
extends = nrf52840_base
|
||||
board = xiao_ble_sense
|
||||
|
@ -68,6 +68,7 @@
|
||||
#define TB_LEFT 1
|
||||
#define TB_RIGHT 2
|
||||
#define TB_PRESS 0 // BUTTON_PIN
|
||||
#define TB_DIRECTION FALLING
|
||||
|
||||
// microphone
|
||||
#define ES7210_SCK 47
|
||||
|
@ -1,4 +1,4 @@
|
||||
[VERSION]
|
||||
major = 2
|
||||
minor = 7
|
||||
build = 2
|
||||
build = 3
|
||||
|
Loading…
Reference in New Issue
Block a user