From 46e6bb7b7185f18b3115b2a839166e29f125aca2 Mon Sep 17 00:00:00 2001 From: mikecarper Date: Mon, 5 Oct 2026 18:33:56 -0700 Subject: [PATCH] Document channel-nine-only notification example --- docs/notifications.md | 52 +++++++++++++++++++ .../test_companion_notifications.cpp | 41 +++++++++++++++ 2 files changed, 93 insertions(+) diff --git a/docs/notifications.md b/docs/notifications.md index 4b7fabb7..dce53ce0 100644 --- a/docs/notifications.md +++ b/docs/notifications.md @@ -240,3 +240,55 @@ notification strings. Receiver master switches apply even while connected. and vibration. Add a connected profile with sound/vibration off if the phone should handle notifications while attached. Permission to send notification strings is separate from a VIP's ordinary message rule. + +### Only channel 9 alerts; all other channels silent + +This example makes message alerts silent by default, then plays one short +melody for channel 9. It works both with and without a connected companion +client and requires a device with a buzzer. Messages still arrive normally; +this changes the node's alerts, not the phone app's notification settings. + +`channel:9` means the firmware's **zero-based slot 9** (the tenth slot), not +a channel named `ch9`. Configure that slot before running these commands. +The rule follows its saved channel key; recreate it if you replace the channel +in that slot. + +Start with no conflicting custom rules. Use `get notify.rules`, then +`get notify.rules ` to inspect existing selectors. Remove only +unwanted rules with `notify.delete `: existing state-specific and +contact/channel rules can override the silent default below. + +```text +set notify.enabled on +set notify.vibration all off +set notify.sound all off +set notify.led all off +set notify.screen all off +set notify.gpio all off +set notify.repeat all 1 +set notify.gap all 500 +set notify.stop all button +set notify.sound on +set notify.sound channel:9 ch9:d=8,o=5,b=180:c,e,g +``` + +Keep the sound **master** on: `set notify.sound all off` silences the default +rule, while `set notify.sound off` would also block the channel 9 exception. +The other outputs remain silent, including screen wake-ups for messages. +To add vibration for channel 9 on a device with a vibration motor: + +```text +set notify.vibration on +set notify.vibration channel:9 100,100,300 +``` + +Test the saved exception with `notify.test channel:9`; stop it with +`notify.stop`. Send a message on another configured channel to verify that +it stays quiet. + +**Scope:** there is no channels-only wildcard: `all` also silences ordinary +DM and room-message alerts unless a more specific rule enables them. +Previously authorized contact/room `!notify` strings can still request alerts. +If those should stay quiet too, revoke their permission with +`set notify.remote contact:PUBLIC_KEY off` or +`set notify.remote room:ROOM_PUBLIC_KEY off`, using the full sender/server key. diff --git a/test/test_companion_notifications/test_companion_notifications.cpp b/test/test_companion_notifications/test_companion_notifications.cpp index 80628d9a..751ef38c 100644 --- a/test/test_companion_notifications/test_companion_notifications.cpp +++ b/test/test_companion_notifications/test_companion_notifications.cpp @@ -67,6 +67,47 @@ TEST_F(Notifications, ContactAndChannelOverrideEachOutputAndConditionIndependent uint8_t channel[16];memset(channel,2,sizeof(channel)); c.message(nullptr,channel,false,0);EXPECT_TRUE(sink.led);EXPECT_TRUE(sink.vibration); } +TEST_F(Notifications, DocumentedChannelNineExceptionKeepsOtherMessagesQuiet) { + // notifications.md: numeric slots are resolved to channel keys by MyMesh. + // Exercise the same recipe with slot 9's already-resolved key. + for (const char* command : { + "set notify.enabled on", + "set notify.vibration all off", + "set notify.sound all off", + "set notify.led all off", + "set notify.screen all off", + "set notify.gpio all off", + "set notify.repeat all 1", + "set notify.gap all 500", + "set notify.stop all button", + "set notify.sound on", + "set notify.sound channel:09090909090909090909090909090909 ch9:d=8,o=5,b=180:c,e,g"}) { + EXPECT_EQ("OK", cmd(command)) << command; + } + unsigned rules = 0; + for (const auto& rule : c.settings().rules) if (rule.used) ++rules; + EXPECT_EQ(2U, rules); + for (bool connected : {false, true}) { + for (unsigned slot = 0; slot < 40; ++slot) { + uint8_t channel[16];memset(channel, slot, sizeof(channel)); + ASSERT_TRUE(c.message(nullptr, channel, connected, 0)); + EXPECT_EQ(slot == 9 ? "ch9:d=8,o=5,b=180:c,e,g" : "", sink.tune); + EXPECT_FALSE(sink.vibration);EXPECT_FALSE(sink.led);EXPECT_FALSE(sink.gpio); + EXPECT_EQ(0, sink.screen_state); + EXPECT_TRUE(c.overridesScreen()); + c.loop(1000, connected);EXPECT_FALSE(c.active()); + } + uint8_t contact[32];memset(contact, 1, sizeof(contact)); + ASSERT_TRUE(c.message(contact, nullptr, connected, 0)); + EXPECT_TRUE(sink.tune.empty());EXPECT_FALSE(sink.vibration);EXPECT_FALSE(sink.led); + EXPECT_EQ(0, sink.screen_state); + c.stop(); + } + EXPECT_EQ("OK", cmd("set notify.sound off")); + uint8_t channel[16];memset(channel, 9, sizeof(channel)); + ASSERT_TRUE(c.message(nullptr, channel, false, 0)); + EXPECT_TRUE(sink.tune.empty()); // A rule cannot bypass its master switch. +} TEST_F(Notifications, ButtonConnectedAndNeverStopPolicies) { cmd("set notify.led all 50");cmd("set notify.repeat all forever"); c.loop(0,false);c.message(nullptr,nullptr,false,0);EXPECT_TRUE(c.button());EXPECT_FALSE(c.active());