diff --git a/docs/_javascript/notification_builder.js b/docs/_javascript/notification_builder.js index 9b9ba55b7..48d05bcc3 100644 --- a/docs/_javascript/notification_builder.js +++ b/docs/_javascript/notification_builder.js @@ -75,7 +75,10 @@ for (const name of ["vibration", "sound", "led", "screen"]) if (config[name] !== "inherit") parts.push(name + "=" + config[name]); for (const name of ["repeat", "gap"]) parts.push(name + "=" + config[name]); const text = "!notify " + parts.join(" "); - if (text.length > 159) throw new Error("Notification text exceeds 159 characters. Shorten the patterns or melody."); + // Room forwarding reserves an author prefix and currently stores 150 text + // characters. Reject before a truncated post could lose a field's value. + const maximum = config.kind === "room" ? 150 : 159; + if (text.length > maximum) throw new Error("Notification text exceeds " + maximum + " characters for " + (config.kind === "room" ? "a room post" : "a DM") + ". Shorten the patterns or melody."); return text; } function level(pattern, elapsed) { diff --git a/docs/notifications.md b/docs/notifications.md index cd73eb113..c4bd5e980 100644 --- a/docs/notifications.md +++ b/docs/notifications.md @@ -84,7 +84,7 @@ stop policy. USB testing uses Binary Companion mode at 115200 baud.

The recipient must first grant your contact or room permission. Send the string as an ordinary DM or room post; it contains no GPIO action. Long patterns may need - shortening to fit a 159-character message.

+ shortening to fit 159 characters for a DM, or 150 for a room post.

Try it on your device

diff --git a/examples/companion_radio/MyMesh.cpp b/examples/companion_radio/MyMesh.cpp index 49b4ed831..4180522ca 100644 --- a/examples/companion_radio/MyMesh.cpp +++ b/examples/companion_radio/MyMesh.cpp @@ -2413,7 +2413,7 @@ void MyMesh::begin(bool has_display, bool radio_available) { notification_settings = mesh::notify::Settings(); } - #endif +#endif // load persisted prefs const bool prefs_ready = diff --git a/platformio.ini b/platformio.ini index 73afa4ec7..e31d8daa5 100644 --- a/platformio.ini +++ b/platformio.ini @@ -185,7 +185,6 @@ build_flags = ${arduino_base.build_flags} ; PendSV reaches this FreeRTOS function from assembly, outside the LTO graph. -Wl,--undefined=vTaskSwitchContext -D NRF52_PLATFORM - -D USER_GPIO_CONTROL=1 -D LFS_NO_ASSERT=1 -D EXTRAFS=1 -D MESH_NRF52_LOOP_STACK_WORDS=2048 diff --git a/src/helpers/NRF52Board.cpp b/src/helpers/NRF52Board.cpp index c4051febd..9e869f253 100644 --- a/src/helpers/NRF52Board.cpp +++ b/src/helpers/NRF52Board.cpp @@ -5,9 +5,7 @@ #include "Nrf52BootloaderVersion.h" #include "nrf52/SoftDeviceState.h" #include -#ifdef USER_GPIO_CONTROL #include "UserGpioPinPolicy.h" -#endif #include #include "ble_gap.h" @@ -52,7 +50,6 @@ static void format_ota_reply(char reply[]) { mac_addr[2], mac_addr[1], mac_addr[0]); } -#ifdef USER_GPIO_CONTROL namespace { bool isExposedNrf52UserGpio(uint8_t pin) { @@ -86,10 +83,10 @@ bool isExposedNrf52UserGpio(uint8_t pin) { } } // namespace -#endif bool NRF52Board::isUserGpioAvailable(uint8_t pin) const { -#ifdef USER_GPIO_CONTROL + // This board approval query also serves Companion alert outputs. CommonCLI's + // separate USER_GPIO_CONTROL flag still controls its GPIO command exposure. if (pin >= PINS_COUNT || digitalPinToPinName(pin) == 0xFF) return false; #if defined(RAK_3401) || defined(RAK_4631) // Sensor startup can toggle these WisBlock slot pins while detecting GPS, @@ -97,10 +94,6 @@ bool NRF52Board::isUserGpioAvailable(uint8_t pin) const { if (pin == WB_IO2 || pin == WB_IO4 || pin == WB_IO5) return false; #endif return isExposedNrf52UserGpio(pin) && !UserGpioPinPolicy::isFirmwareReserved(pin); -#else - (void)pin; - return false; -#endif } bool NRF52Board::rebootToUf2Bootloader() { diff --git a/test/test_local_cli_access.py b/test/test_local_cli_access.py index 6cc76bc03..77599261a 100644 --- a/test/test_local_cli_access.py +++ b/test/test_local_cli_access.py @@ -143,6 +143,9 @@ struct MyMesh { bool hasOutbound(){return false;} void markConnectionActive(const ContactInfo&){} void queueMessage(const ContactInfo&,int,mesh::Packet*,uint32_t,const void*,int,const char*){} + // This harness exercises radio/local identity dispatch. Notification parsing + // and permissions run against the real controller in their native suite. + bool handleNotificationCommand(const char*,char*,size_t){return false;} }; ''' diff --git a/test/test_message_navigation.py b/test/test_message_navigation.py index 41c6a3f79..79b4bc7c3 100644 --- a/test/test_message_navigation.py +++ b/test/test_message_navigation.py @@ -46,6 +46,7 @@ struct StrHelper { }; struct ChannelDetails { char name[32]; }; struct Mesh { + bool notificationButton() { return false; } bool dual_radio_active=false; bool isDualRadioActive() const { return dual_radio_active; } bool getChannel(int channel,ChannelDetails& details) const { diff --git a/test/test_notification_builder.js b/test/test_notification_builder.js index 7cda3b23e..395794b71 100644 --- a/test/test_notification_builder.js +++ b/test/test_notification_builder.js @@ -22,6 +22,9 @@ const vip = {...builder.EXAMPLES.vip, id: "01".repeat(32), remote: "on"}; assert.ok(builder.commands(vip).includes("set notify.remote contact:" + vip.id + " on")); const room = {...vip, kind: "room"}; assert.ok(builder.commands(room).includes("set notify.remote room:" + room.id + " on")); +const longPost = {...vip, led: Array(9).fill("60000").join(","), repeat: "65535"}; +assert.equal(builder.notificationText(longPost).length, 151); +assert.throws(() => builder.notificationText({...longPost, kind: "room"}), /150 characters for a room post/); assert.throws(() => builder.commands({...builder.EXAMPLES.food, remote: "on"})); assert.throws(() => builder.commands({...vip, id: "1234"})); const encoded = builder.encodeCommand("notify.stop", "A7");