diff --git a/docs/solo/README.md b/docs/solo/README.md index 0973e098..4a7729c4 100644 --- a/docs/solo/README.md +++ b/docs/solo/README.md @@ -19,8 +19,9 @@ still connects as with the stock firmware, over Bluetooth or USB. - **Nearby nodes**: who's around, signal and distance, ping, and remote admin of your repeaters and rooms. - **Tools**: clock with alarm, timer and stopwatch, a remote bot that answers - commands over the mesh, repeater mode, a ringtone editor, and a status - screen with the radio, GPS, battery and mesh counters. + commands over the mesh, repeater mode, a ringtone editor, a status screen + with the radio, GPS, battery and mesh counters, and + [SOS](./tools.md#sos) (experimental). - **Screen lock** with an optional PIN. - **On the Wio Tracker L2**: an offline map, message history on the SD card, the SD card as a USB drive, updates over WiFi. diff --git a/docs/solo/developer/feature-matrix.md b/docs/solo/developer/feature-matrix.md index 4fe1582e..014f43d7 100644 --- a/docs/solo/developer/feature-matrix.md +++ b/docs/solo/developer/feature-matrix.md @@ -92,6 +92,7 @@ build flag it needs. | Clock tools (alarm, timer, stopwatch) | yes, tenths on the stopwatch | no tenths | no tenths | wide layout | Clock app | | Calendar | Clock tools (tight grid) | Clock tools (tight grid) | + under the clock, on the lock screen | + under the clock | in the Clock app | | Remote bot, repeater, auto-advert | yes | yes | yes | yes | yes | +| SOS (experimental) | Tools › SOS, hold Back | same | same | same | SOS app, hold the top button, then a slider | | Melodies (`PIN_BUZZER`) | Ringtones tool | same | same | wide editor | Settings › Sound | | Status: Radio, GPS, Power, Mesh | yes, 32 min history | yes, 8 h charts | yes, 8 h charts | tiles with charts | Diagnostics app (+ noise chart) | | Sky view (`GPS_SKYVIEW`) | Status › Sky | same | same | + side panel | GPS app: Sky, Signal, Details | diff --git a/docs/solo/developer/ui-core.md b/docs/solo/developer/ui-core.md index 88599806..e682de8c 100644 --- a/docs/solo/developer/ui-core.md +++ b/docs/solo/developer/ui-core.md @@ -35,6 +35,7 @@ interleaved. Classified: **Engines — logic living inside `UITask.cpp` / `UITask.h`** - Unread tracking — DM unread table ✅ (`ui-core/DmUnreadTable.h`), room unread (one counter in `UiCore`). Room logins ✅ `ui-core/RoomSessions.h`. - Notifications ✅ split: the Core decides *what* happened (`MessageArrived` with kind / DM sender / channel slot, `AdvertHeard`); the frontend decides *how* to show it (`showAlert`, `SoundNotifier`, vibration, LED, wake-on-message) — those are platform services. +- SOS ✅ (experimental) `ui-core/SosEngine.h` — countdown, repeats, the ACK word, who answered, the status lines; no radio or clock, so it's tested on the host (`test/test_sos_engine`). `UiCore::startSos()` / `stopSos()` own the GPS and the repeater, `sendSos()` sends a round, `UiCoreHost::onSosRunning()` is what's left to the frontend (the volume, WiFi on the L2). The views are each frontend's, over everything, locked or not: `renderStatus` in ui-new, `lv_layer_top` in ui-lvgl. The message's format is `SosEngine::compose()`, read back by `geo::parseSos()` / `geo::isSosNoGps()`. - Live share ✅ `ui-core/LiveShareEngine.h` — session timer, movement/heartbeat gate, send + scope guard, peers' `LiveTrackStore` + expiry. - Locator ✅ `ui-core/LocatorEngine.h` — active target, geofence state machine, proximity beeper. - Trail ✅ `ui-core/TrailEngine.h` — store, sampling, auto-pause, low-battery auto-save, recording actions (start/stop, reset, save/load `/trail`) used by both frontends. GPS averaging for marks ✅ `ui-core/GpsAverager.h`. Track back ✅ `ui-core/TrackBack.h`. @@ -111,7 +112,7 @@ call the same ones, so e.g. "marking a channel read" has one implementation. ### 5. Declarative settings schema -`ui-core/SettingsSchema.h` holds five pages -- map options (trail, live share, arrival alert; ui-lvgl opens it from the map's tools), advert (auto-advert; in ui-lvgl's advert popup, the quick panel's Advert), display & power (brightness, screen off, wake on message, lock screen, battery display, battery shutdown, GPS power saving, time zone, 12-hour clock, clock seconds, units) messages & contacts (DM resends, contact expiry, favourites first) and sound (volume, the sound for direct messages / channels / adverts, advert sound scope). An entry is a one-byte index by default, or a `values` table over a 1/2/4-byte field (seconds, millivolts, an hour offset, `{1, 0}` for a field stored inverted); side effects run through `UiCore` (`applyGpsInterval()`, `host()->applyDisplayPrefs()`). ui-lvgl renders each page (`schemaRows()` draws a page's rows into any container, a screen or a popup); ui-new puts a schema section's rows into its own accordion sections (`SCHEMA_DISPLAY`, `SCHEMA_SYSTEM`, … in `SettingsScreen.h`), next to the rows it still draws itself (radio, keyboard, home pages, quick messages). Radio settings are not schema rows (preset list, typed frequency, radio side effects): `ui-core/RadioControl.h` applies them (`applyParams`, `applyTxPower`, `applyApc`, presets = built-ins + user slots) for both ui-new's Settings > Radio and ui-lvgl's Radio screen. Channel management is `ui-core/ChannelControl.h` (`chanctl::`): add Public / Hashtag / Private (passphrase or 32-hex key), rename, delete, notifications / favourite / scope, and `onRemoved()` -- the cleanup of every channel-keyed pref, run by `UiCore::onChannelRemoved` whoever deleted the channel (this device or the app); contacts get the same through `UiCore::cleanupContactPrefs`. Favourites dial slots: `ui-core/Favourites.h` (`pinContact` / `pinChannel` move an entry that is already on another slot; the slot's name is `contactctl::favName`). The schema's display page also carries the lock screen (`auto_lock`) and the 12-hour clock, which ui-lvgl implements (a slide-to-unlock card after the screen turns off), and ui-new's lock clock look (`lock_compact`, Big / Compact), which ui-lvgl leaves out of its rows. Remote admin is `ui-core/AdminSession.h` (`UiCore::admin`): login with the saved password (forgotten on a wrong password or a silent timeout), CLI round trips with timeouts, the field table (`admin::`), typed values; results come out one at a time through `take()`. `UiCore` offers login answers and CLI replies to it first; only room logins it wasn't waiting for reach the frontend. ui-new's Tools > Admin and ui-lvgl's Admin screen are views over it. The bot's settings are a table too, `ui-core/BotConfig.h` (`botcfg::`: tabs, rows, the NodePrefs flag / text / hour behind each, reply placeholders); ui-new's Tools > Bot and ui-lvgl's Bot screen render it, the bot itself runs in MyMesh. Room servers: `ui-core/RoomSessions.h` (`UiCore::rooms`) keeps who is logged in this session, the one login in flight (with a timeout) and the saved passwords (saved on success, forgotten on failure); `open(contact)` says whether to go straight in, wait for a background login or ask for a password, and outcomes come out through `take()`. Per-contact helpers are `ui-core/ContactControl.h` (`contactctl::`: DM notification / melody overrides, favourite flag, the `@[name] ` reply prefix, splitting a room post into author and text, naming a message's path hops). ui-new's Messages and ui-lvgl's Chats / thread use both. Diagnostics rows are `ui-core/Diagnostics.h` (`diag::`: live counters, font card, counter reset), shown by ui-new's Home > Status screen (Mesh and System tabs) and ui-lvgl's Home > Diagnostics. Auto-advert is a schema row (`advert_auto_interval_sec`, sent by MyMesh); ui-new keeps its Tools > Auto-Advert screen over the same pref. The compass (ui-lvgl Home > Compass, ui-new Tools > Compass) reads `UiCore::course`. Saving / deleting the user's radio presets is `radioctl::saveUserPreset` / `deleteUserPreset` (ui-new's preset picker and ui-lvgl's Radio > My presets); the scope list is edited through MyMesh (`addScope` / `renameScope` / `removeScope` / `setDefaultScope`), which ui-lvgl's Radio > Scopes and ui-new's Settings > Radio > Scope call directly. The battery percentage (the built-in LiPo curve or the user's `batt_curve_mv`, empty at the shutdown voltage) and the display mode are `ui-core/Battery.h`, used by both status bars; the schema carries Battery display and Clock seconds. The companion's own repeater mode is `ui-core/RepeaterControl.h` (`rptctl::`: on / off with its radio, RX power-save and APC side effects, Current / Custom network with the profile seeded from the chat params, the profile's preset, the filters' ranges and labels, the extra scopes mask); ui-new's Tools > Repeater and ui-lvgl's Home > Repeater use it, MyMesh does the relaying. Sound is `ui-core/SoundControl.h` (`soundctl::`: the On / Off / Auto mode over `buzzer_quiet` + `buzzer_auto`, the volume with its preview, the built-in sounds, the two user melodies as an editable note list) and `ui-core/SoundNotifier.h` (which sound a message / advert plays: global choice, per-contact / per-channel alert and melody overrides); the schema's Sound page holds the volume and the PLAYS FOR choices, the channel melody override is `chanctl::melody`. Both frontends drive a `genericBuzzer`: a PWM piezo on L1, the ES8311 codec on the L2 (`BUZZER_I2S`: an audio task synthesises the notes and counts samples, the board powers the amp through `buzzerAmpPower()`). Outgoing text is `ui-core/MessageText.h` (`msgtext::`: the ten quick messages, the placeholders on offer -- `{loc}`, `{time}`, `{batt}`, live sensor readings -- and expanding them at send time, keeping a reply's `@[name] ` prefix as typed); ui-new's Messages / keyboard and ui-lvgl's compose "+" use it. `UiCore::markAllRead()` clears every unread counter; `MyMesh::advertFlood()` (formerly sim-only) sends the self-advert through repeaters, `advert()` zero-hop. ui-lvgl's screens share their widgets from `UITask.cpp`: `segmented()` (a one-of-N button row; `radioview::rowSegmented` puts one at the right of a settings row), `textField()` (a one-line field padded to exactly one line), `listRow`, `switchRow`, `headerButton`, `centerRow()` (a full-width row of centred items), `pillButton()` (the small "All" / "★ Fav" pill). A popup that asks for one line of text is `UITask::navTextEntry(TextEntry{title, done_cb, ...})` (`NavMap.h`): title, optional start text / hint / password / accepted characters / digits-first keyboard / a byte cap (`onByteCapInsert`, for text stored in a fixed buffer), the field focused over the keyboard; the callback gets the keyboard's READY / CANCEL and reads `_nav_ta`. New text-entry popups use it rather than wiring field and keyboard by hand. +`ui-core/SettingsSchema.h` holds five pages -- map options (trail, live share, arrival alert; ui-lvgl opens it from the map's tools), advert (auto-advert; in ui-lvgl's advert popup, the quick panel's Advert), display & power (brightness, screen off, wake on message, lock screen, battery display, battery shutdown, GPS power saving, time zone, 12-hour clock, clock seconds, units) messages & contacts (DM resends, contact expiry, favourites first) and sound (volume, the sound for direct messages / channels / adverts, advert sound scope). An entry is a one-byte index by default, or a `values` table over a 1/2/4-byte field (seconds, millivolts, an hour offset, `{1, 0}` for a field stored inverted); side effects run through `UiCore` (`applyGpsInterval()`, `host()->applyDisplayPrefs()`). ui-lvgl renders each page (`schemaRows()` draws a page's rows into any container, a screen or a popup); ui-new puts a schema section's rows into its own accordion sections (`SCHEMA_DISPLAY`, `SCHEMA_SYSTEM`, … in `SettingsScreen.h`), next to the rows it still draws itself (radio, keyboard, home pages, quick messages). Radio settings are not schema rows (preset list, typed frequency, radio side effects): `ui-core/RadioControl.h` applies them (`applyParams`, `applyTxPower`, `applyApc`, presets = built-ins + user slots) for both ui-new's Settings > Radio and ui-lvgl's Radio screen. Channel management is `ui-core/ChannelControl.h` (`chanctl::`): add Public / Hashtag / Private (passphrase or 32-hex key), rename, delete, notifications / favourite / scope, and `onRemoved()` -- the cleanup of every channel-keyed pref, run by `UiCore::onChannelRemoved` whoever deleted the channel (this device or the app); contacts get the same through `UiCore::cleanupContactPrefs`. Favourites dial slots: `ui-core/Favourites.h` (`pinContact` / `pinChannel` move an entry that is already on another slot; the slot's name is `contactctl::favName`). The schema's display page also carries the lock screen (`auto_lock`) and the 12-hour clock, which ui-lvgl implements (a slide-to-unlock card after the screen turns off), and ui-new's lock clock look (`lock_compact`, Big / Compact), which ui-lvgl leaves out of its rows. Remote admin is `ui-core/AdminSession.h` (`UiCore::admin`): login with the saved password (forgotten on a wrong password or a silent timeout), CLI round trips with timeouts, the field table (`admin::`), typed values; results come out one at a time through `take()`. `UiCore` offers login answers and CLI replies to it first; only room logins it wasn't waiting for reach the frontend. ui-new's Tools > Admin and ui-lvgl's Admin screen are views over it. The bot's settings are a table too, `ui-core/BotConfig.h` (`botcfg::`: tabs, rows, the NodePrefs flag / text / hour behind each, reply placeholders); ui-new's Tools > Bot and ui-lvgl's Bot screen render it, the bot itself runs in MyMesh. Which `!` commands a target answers is `ui-core/BotCommands.h` (`botcmd::`: the table, a per-target mask of the ones switched off, `!help`'s list; `position()` is the `!loc` switch, which `!status`, `!dist` and `!gps fix` follow; `start()` holds commands to the start of a message). Room servers: `ui-core/RoomSessions.h` (`UiCore::rooms`) keeps who is logged in this session, the one login in flight (with a timeout) and the saved passwords (saved on success, forgotten on failure); `open(contact)` says whether to go straight in, wait for a background login or ask for a password, and outcomes come out through `take()`. Per-contact helpers are `ui-core/ContactControl.h` (`contactctl::`: DM notification / melody overrides, favourite flag, the `@[name] ` reply prefix, splitting a room post into author and text, naming a message's path hops). ui-new's Messages and ui-lvgl's Chats / thread use both. Diagnostics rows are `ui-core/Diagnostics.h` (`diag::`: live counters, font card, counter reset), shown by ui-new's Home > Status screen (Mesh and System tabs) and ui-lvgl's Home > Diagnostics. Auto-advert is a schema row (`advert_auto_interval_sec`, sent by MyMesh); ui-new keeps its Tools > Auto-Advert screen over the same pref. The compass (ui-lvgl Home > Compass, ui-new Tools > Compass) reads `UiCore::course`. Saving / deleting the user's radio presets is `radioctl::saveUserPreset` / `deleteUserPreset` (ui-new's preset picker and ui-lvgl's Radio > My presets); the scope list is edited through MyMesh (`addScope` / `renameScope` / `removeScope` / `setDefaultScope`), which ui-lvgl's Radio > Scopes and ui-new's Settings > Radio > Scope call directly. The battery percentage (the built-in LiPo curve or the user's `batt_curve_mv`, empty at the shutdown voltage) and the display mode are `ui-core/Battery.h`, used by both status bars; the schema carries Battery display and Clock seconds. The companion's own repeater mode is `ui-core/RepeaterControl.h` (`rptctl::`: on / off with its radio, RX power-save and APC side effects, Current / Custom network with the profile seeded from the chat params, the profile's preset, the filters' ranges and labels, the extra scopes mask); ui-new's Tools > Repeater and ui-lvgl's Home > Repeater use it, MyMesh does the relaying. Sound is `ui-core/SoundControl.h` (`soundctl::`: the On / Off / Auto mode over `buzzer_quiet` + `buzzer_auto`, the volume with its preview, the built-in sounds, the two user melodies as an editable note list) and `ui-core/SoundNotifier.h` (which sound a message / advert plays: global choice, per-contact / per-channel alert and melody overrides); the schema's Sound page holds the volume and the PLAYS FOR choices, the channel melody override is `chanctl::melody`. Both frontends drive a `genericBuzzer`: a PWM piezo on L1, the ES8311 codec on the L2 (`BUZZER_I2S`: an audio task synthesises the notes and counts samples, the board powers the amp through `buzzerAmpPower()`). Outgoing text is `ui-core/MessageText.h` (`msgtext::`: the ten quick messages, the placeholders on offer -- `{loc}`, `{time}`, `{batt}`, live sensor readings -- and expanding them at send time, keeping a reply's `@[name] ` prefix as typed); ui-new's Messages / keyboard and ui-lvgl's compose "+" use it. `UiCore::markAllRead()` clears every unread counter; `MyMesh::advertFlood()` (formerly sim-only) sends the self-advert through repeaters, `advert()` zero-hop. ui-lvgl's screens share their widgets from `UITask.cpp`: `segmented()` (a one-of-N button row; `radioview::rowSegmented` puts one at the right of a settings row), `textField()` (a one-line field padded to exactly one line), `listRow`, `switchRow`, `rowPick()` (a value and a chevron, the whole row is the tap), `pickContacts()` (a picker's contacts, favourites first), `slideBar()` / `slidePoll()` (a slide to confirm: the lock screen, SOS), `headerButton`, `centerRow()` (a full-width row of centred items), `pillButton()` (the small "All" / "★ Fav" pill). A popup that asks for one line of text is `UITask::navTextEntry(TextEntry{title, done_cb, ...})` (`NavMap.h`): title, optional start text / hint / password / accepted characters / digits-first keyboard / a byte cap (`onByteCapInsert`, for text stored in a fixed buffer), the field focused over the keyboard; the callback gets the keyboard's READY / CANCEL and reads `_nav_ta`. New text-entry popups use it rather than wiring field and keyboard by hand. Replaces the per-item code in `SettingsScreen`: diff --git a/docs/solo/lock.md b/docs/solo/lock.md index d8c33a1a..b9339718 100644 --- a/docs/solo/lock.md +++ b/docs/solo/lock.md @@ -2,6 +2,7 @@ The lock keeps pocket presses from doing anything. It locks the screen only: messages still arrive, alarms still ring and the phone app still connects. +[SOS](./tools.md#sos), when switched on, also starts from a locked device. ## Locking and unlocking diff --git a/docs/solo/tools.md b/docs/solo/tools.md index 30b17dc4..25d0e4f2 100644 --- a/docs/solo/tools.md +++ b/docs/solo/tools.md @@ -9,7 +9,7 @@ home page. > [!NOTE] > **Wio Tracker L2:** the tools are apps on the home screen: Messages, Nodes, > Settings, Compass, Clock, GPS, Bot, Repeater, Admin, Diagnostics, Flashlight, -> Gallery. The trail, live sharing and the locator are on the map (its tools +> Gallery, SOS. The trail, live sharing and the locator are on the map (its tools > button) and in Settings › Map. ## Clock @@ -35,6 +35,124 @@ time are in Settings. Any key silences the ringing; it stops by itself after a minute. +## SOS + +> [!IMPORTANT] +> **Experimental.** SOS is new and hasn't been proven in the field yet. How it +> starts, its settings and [its message](#the-message-is-open-for-discussion) +> may change from one release to the next. Try it out, and don't make it your +> only way to call for help. + +**Tools › SOS** sends your position to the people you choose, in a few seconds +and without opening a menu. Set it up before you need it: + +| Row | Does | +| --- | ---- | +| **Hold Back** | Lets a hold of **Back** start it. Off by default | +| **Countdown** | 5, 10 or 15 seconds to cancel in | +| **Repeat** | How often it's sent again: every 1, 2, 5 or 10 minutes, or **Off** for once | +| **Beep** | Off, or every 30 seconds, 1 or 5 minutes: `... --- ...` on the buzzer at full volume, so someone nearby can find you if you can't answer | +| **Channels** | Which channels it goes to; **Enter** switches each one | +| **Contact 1-3** | Up to three direct contacts. **Enter** picks one, **Left / Right** empties the row | +| **ACK word** | The word that means "received", `ACK` by default | +| **Note** | Added to the end of the message, for example your blood group | +| **Send SOS** | Starts it from here, on devices without a Back button too | + +**Starting:** hold **Back** for 3 seconds. It works with the screen off and on a +locked device, and needs no PIN. The device beeps every second and counts down, +even with the buzzer off. **Any key** cancels. + +**The message** is short, to reach as far as possible: + +``` +SOS 50.06143,19.93658 ALT 212m sent 14:32 +``` + +The GPS is turned on for it, if it was off, and back off when the SOS ends. +Without a fix it gives the last position from the past hour, with its age +(`SOS last 50.06143,19.93658 12min ago`). An older one could be far from where +you are, so then it says `SOS no GPS`. Either way one more goes as soon as the +GPS gets a fix. The **Note** comes last. The format isn't final, see +[below](#the-message-is-open-for-discussion). + +**Receiving one:** a banner names the sender, also when there's no position. +A position shows on the map and in Nearby as `SOS name`, even with **Track loc** +off. It stays for 24 hours, +or until a restart. **Hold Enter** on the message navigates to it. Other +firmware and the phone app show it as an ordinary message. + +**While it runs**, the status shows how many were sent and when the next goes. +It's sent up to 12 times, then the device goes on listening, with one more +send if the GPS gets its first fix after that. **Beep** goes on, even with the +buzzer off, after an answer too, until you end the SOS. + +To save the battery while it runs, the screen goes off after 30 seconds whatever +your auto-off says. GPS power saving waits until nothing more will be sent, and +then a GPS the SOS turned on goes back off. The repeater (and WiFi on the L2) +rests until the SOS ends, so the message goes out on your own network. During an +SOS the bot refuses `!gps`. + +**Back** hides the status and the SOS carries on, with `SOS` in the top line; +holding **Back** again, or **Send SOS**, brings it back. **Hold Enter** ends it. +A restart ends it too, and doesn't put back what it rested: WiFi stays off, and +so does the repeater if a setting was saved in the meantime. + +**The answer:** when a contact, or anyone on one of the chosen channels, replies +with the ACK word, the buzzer sounds whatever its setting, the repeats stop and +the screen shows **Answered**: who, what they wrote and how long ago. The word +counts on its own and in any case: `ack, on my way` does, `back` doesn't. On a +public channel anyone can send it. + +Whoever has answered needs no ACK word after that: `10 more minutes` shows up +too, with a short sound, and so does anyone else who answers (the L2 shows the +latest two). On the L2 the top bar turns green, and the screen comes back to +this each time it wakes. + +> [!WARNING] +> This is not a rescue service. A message only reaches people whose devices are +> in range of you or of a repeater, and nothing guarantees that anyone is there +> or awake. Agree with the people you pick that they'll answer, and test it +> together, on a private channel, before you rely on it. + +> [!NOTE] +> Other devices without a Back button start it from **Send SOS**. + +### The message is open for discussion + +The text above is a first version, and a change is easy to make while SOS is +experimental. Only its start is fixed for receivers: `SOS` followed by a +position, `SOS last` followed by a position, or `SOS no GPS`. That's what the +map and the banner read, so a change there needs both sides updated. Everything +after it is for people to read. + +Open questions, for [issue #56](https://github.com/MarekZegare4/MeshCore-Solo/issues/56): + +- **Accuracy:** the request asked for `±3m` after the position. It isn't sent + yet. The GPS reports HDOP, which is only a rough guide to metres, and not + every module gives it. Is an estimate worth the extra bytes? +- **Digits:** the position has 5 decimals, about 1 metre. 4 decimals, about + 11 metres, saves 2 bytes. +- **Labels:** `ALT 212m sent 14:32` reads easily. Commas, or dropping `sent`, + would change little in length. Is the time useful at all, given that a + repeat can arrive late? +- **Anything missing:** the battery level would tell a rescuer how long the + device will last, at a few more bytes. +- **Own language:** the note can be anything, but the rest stays in English. + +### On the Wio Tracker L2 + +The **SOS** app has the same settings, with **Top button** in place of Hold +Back; favourites with their star come first in the contact list. **Holding the top button for 5 seconds** beeps and opens **Emergency SOS** +over everything, with the screen off or locked too and without the PIN. **Slide** +the knob to the end to start: a tap or a short slide does nothing, so a button +pressed in a pocket can't send one. **Send SOS** in the app opens the same slider. + +**Cancel** or either button ends the countdown. While it runs, **Hide** puts the +view away, and the status bar shows a red `SOS` and a title bar's clock turns +red; hold the top button again, or tap **Send SOS** in the app, to come back. +**Slide to stop** ends it. A received SOS shows as a short message at the bottom +of the screen and on the map. + ## Remote bot **Tools › Bot** answers messages for you. It watches your direct messages, one @@ -43,7 +161,8 @@ channel and one room, each switched on separately: - **Trigger and reply**: when a message contains the trigger (several can be separated by commas, `*` matches any message), the bot sends the reply. The reply can use placeholders, plus `{name}` (the sender), `{hops}`, `{path}` (the route in, as hop hashes oldest first: `3f:a1:c8`, or `direct`), `{snr}` and `{rssi}` (how its last hop was heard). -- **Commands**: messages starting with `!` get live data back: +- **Commands**: a message starting with `!` gets live data back (a `!word` + further on in the text does nothing, so people can talk about them): | Command | Reply | | ------- | ----- | @@ -57,19 +176,37 @@ channel and one room, each switched on separately: | `!help` | the list of commands | Several in one message get one reply: `!batt !time` → `4.10V | 14:30`. + + **Allowed**, under **Commands** on the Channel, Room and Direct tabs, sets + which of them that place answers, so `!loc` can work in a direct message from + a favourite and not on a channel. All are on by default. A command that's off + gets no reply, as if the word meant nothing, and `!help` leaves it out. + + **`!loc`** is the switch for your position: off, `!status` answers without + it, `!dist` is off too and `!gps fix` answers `GPS: fix off`. It doesn't touch + a **Reply** you wrote yourself: a `{loc}` there still sends it. - **Actions** (off by default) let the command change the device: `!buzz` (sounds the buzzer to find it), `!gps on` / `!gps off`, `!gps fix` (turns - the GPS on, waits for a good fix and sends it), `!advert`. **Actions by = + the GPS on, waits for a good fix and sends it), `!advert`. With **Actions** on, + **Allowed** lists these too. During an + [SOS](#sos) `!gps` is refused (`GPS: on for SOS`). **Actions by = Fav** (Other tab) lets only your favourites run them; on a channel the sender is just a name, so there it's a weak check. On ESP32 boards (L2, Heltec, ...) the **Log** tab lists the bot's last 32 replies: when, who, what they asked and what it answered. Kept until a restart. -**DM allow = Fav** limits the DM bot to your favourites, and **quiet hours** -stop trigger replies at night (commands still answer). Replies are rate -limited so two bots can't answer each other forever. The room bot uses the -room's saved login. +**DM allow = Fav** limits the DM bot to your favourites. **Position = Fav** +(Direct tab) keeps everyone else's commands working but treats them as if +`!loc` were off, so anyone can `!ping` you and only favourites learn where you +are. A direct message's sender is verified, so this one is a firm check. + +**Quiet hours** stop the bot at night, except commands in a direct message: +those are asked for and go to one person. **Reply gap** (Other tab) is how long +it waits between replies on the channel and in the room, 10 seconds to 5 +minutes: each one goes out to everyone in range, and it keeps two bots from +answering each other forever. Direct messages wait 10 seconds per contact. The +room bot uses the room's saved login. ## Auto-advert