Enhance documentation and features

- Updated supported languages to include Hungarian, Japanese, and Korean.
- Added new on-device message translation feature with detailed usage instructions.
- Introduced emoji reactions in chat with user interface and functionality details.
- Implemented linkification for automatic detection of URLs and meshcore URIs in messages.
- Added GPX export functionality for contacts with GPS coordinates.
- Enabled pinch-to-zoom for chat text scaling.
- Documented background service for Android to maintain BLE connection in the background.
- Revised BLE protocol documentation to reflect changes in connection state machine and command codes.
- Updated channels documentation to clarify message display and interaction options.
- Enhanced chat and messaging documentation with new translation button and message metadata.
- Clarified contact actions in contacts documentation.
- Adjusted map and location documentation for improved node name visibility and filter options.
- Revised navigation documentation to streamline disconnection process.
- Improved notification documentation to specify batch notification behavior.
- Updated repeater management documentation to reflect new features and settings.
- Enhanced scanner and connection documentation for device filtering and connection timeout.
- Expanded settings documentation to include new translation options.
- Removed jni plugin references from generated plugin files for Linux and Windows.
chore/offband-rebrand
zjs81 2 months ago
parent b472ea8c70
commit c229b0369e

@ -1,6 +1,6 @@
# MeshCore Open - Flutter Client # MeshCore Open - Flutter Client
Open-source Flutter client for MeshCore LoRa mesh networking devices. Open-source Flutter client for MeshCore LoRa mesh networking devices. Connects to MeshCore-compatible radios over **BLE, TCP, or USB serial** and provides direct/channel chat, contact and channel management, on-map node tracking, repeater administration, and on-device message translation.
## Build Commands ## Build Commands
@ -17,6 +17,9 @@ Open-source Flutter client for MeshCore LoRa mesh networking devices.
# Build iOS # Build iOS
~/flutter/bin/flutter build ios ~/flutter/bin/flutter build ios
# Build versioned web release (uses build_pipe)
~/flutter/bin/dart run build_pipe
# Run static analysis # Run static analysis
~/flutter/bin/flutter analyze ~/flutter/bin/flutter analyze
@ -28,43 +31,132 @@ Open-source Flutter client for MeshCore LoRa mesh networking devices.
``` ```
lib/ lib/
├── main.dart # App entry point, MaterialApp setup with Provider ├── main.dart # Entry point: MultiProvider wiring, locale + theme, initial route
├── connector/ ├── connector/ # Unified BLE/TCP/USB transport layer
│ └── meshcore_connector.dart # BLE communication layer (MeshCoreConnector) │ ├── meshcore_connector.dart # Central state holder + ChangeNotifier (all transports)
├── screens/ │ ├── meshcore_connector_tcp.dart # TCP transport helper
│ ├── scanner_screen.dart # BLE device scanning (home screen) │ ├── meshcore_connector_usb.dart # USB serial transport helper
│ ├── device_screen.dart # Connected device hub with navigation │ ├── meshcore_protocol.dart # Frame size + version constants
│ ├── chat_screen.dart # Chat interface (placeholder) │ └── meshcore_uuids.dart # Nordic UART UUIDs + scan name prefixes
│ ├── contacts_screen.dart # Contacts list (placeholder) ├── models/ # Plain data classes (Contact, Channel, Message, Community, …)
│ └── settings_screen.dart # Device info and app settings ├── services/ # ChangeNotifier services + IO services (retry, translation, ML, …)
└── widgets/ ├── storage/ # SharedPreferences-backed stores, scoped per device key
└── device_tile.dart # Device list item with signal strength ├── helpers/ # Pure utilities (Smaz compression, GIF parsing, scroll helpers)
├── utils/ # Platform / IO / UX utilities (logger, GPX export, dialogs)
├── theme/ # MeshPalette (defined, not yet wired in main.dart)
├── l10n/ # ARB localization for 18 locales
├── icons/ # Custom icon widgets
├── widgets/ # Reusable widgets (AppBar, BatteryUi, QR, jump-to-bottom, …)
└── screens/ # ~26 screens — see Screens section below
``` ```
## Screens
All screens are fully implemented (no remaining placeholders).
### Connection / Scanning
| Screen | Purpose |
|---|---|
| `scanner_screen.dart` | BLE device scan and connect — main entry point |
| `tcp_screen.dart` | Connect to a MeshCore device over TCP/IP |
| `usb_screen.dart` | Connect to a MeshCore device over USB serial |
| `discovery_screen.dart` | Browse all discovered (non-contact) mesh nodes |
| `chrome_required_screen.dart` | Web gate for non-Chrome browsers (BLE unavailable) |
### Chat / Messaging
| Screen | Purpose |
|---|---|
| `chat_screen.dart` | Direct (private) messaging with a contact |
| `channel_chat_screen.dart` | Group messaging inside a named channel |
| `channels_screen.dart` | List and manage channels (add/edit/delete) |
| `channel_message_path_screen.dart` | Hop-by-hop route a channel message took, with map overlay |
### Contacts / Neighbors
| Screen | Purpose |
|---|---|
| `contacts_screen.dart` | Full contacts list with previews and management |
| `neighbors_screen.dart` | Nodes directly heard by the connected radio (one-hop) |
### Repeater Management
| Screen | Purpose |
|---|---|
| `repeater_hub_screen.dart` | Top-level repeater hub; navigates to sub-screens |
| `repeater_status_screen.dart` | Live status of a managed repeater node |
| `repeater_cli_screen.dart` | Raw command-line interface to a repeater |
| `repeater_settings_screen.dart` | Full radio/node settings editor for a repeater |
### Map / Location
| Screen | Purpose |
|---|---|
| `map_screen.dart` | Main map view of contacts/nodes with live GPS positions |
| `line_of_sight_map_screen.dart` | Terrain LOS analysis between configurable endpoints |
| `path_trace_map.dart` | Animates the hop path a direct message traveled |
| `map_cache_screen.dart` | Download/clear offline map tile cache |
| `community_qr_scanner_screen.dart` | Scan QR to join a mesh community/channel |
### Settings / Debug / Diagnostics
| Screen | Purpose |
|---|---|
| `settings_screen.dart` | Connected device settings: radio params, identity, GPS |
| `app_settings_screen.dart` | App preferences: theme, units, map source, notifications |
| `app_debug_log_screen.dart` | In-app log viewer (app-layer messages) |
| `ble_debug_log_screen.dart` | In-app log viewer (raw BLE frame traffic) |
| `companion_radio_stats_screen.dart` | RF stats (RSSI, SNR, packet counts) for paired radio |
| `telemetry_screen.dart` | Battery / sensor / environmental telemetry for a contact |
## Architecture ## Architecture
### State Management ### State Management
- **Provider** with `ChangeNotifier` pattern
- `MeshCoreConnector` is the central state holder for BLE connection `Provider` with `ChangeNotifier`. `main.dart` wires a `MultiProvider` with the following:
- Screens use `Consumer<MeshCoreConnector>` for reactive UI updates
| Provider | Role |
|---|---|
| `MeshCoreConnector` | Active transport (BLE/TCP/USB), connection state, frame I/O |
| `MessageRetryService` | ACK tracking and retry scheduling with backoff |
| `PathHistoryService` | Per-contact routing history (LRU cache, 50 contacts) |
| `AppSettingsService` | App preferences (theme, units, locale, notifications) |
| `BleDebugLogService` | Raw BLE frame log buffer |
| `AppDebugLogService` | Structured app log buffer |
| `ChatTextScaleService` | Pinch-to-zoom text scale for chat screens |
| `TranslationService` | On-device LLM translation (llamadart) |
| `UiViewStateService` | Contacts/channels sort/filter/search state |
| `TimeoutPredictionService` | ML linear regression for ACK timeout prediction |
| `StorageService` | Path history + delivery observation persistence |
| `MapTileCacheService` | OSM tile pre-cache |
Screens consume these via `Consumer<T>` (or `context.watch<T>()` / `context.read<T>()`) for reactive UI.
### Storage / Persistence
All stores in `lib/storage/` use `PrefsManager` (a `SharedPreferences` singleton initialized in `main()`). Most stores **scope keys by the first 10 hex chars of the connected device's public key**, so per-radio data is isolated.
| Store | Persists |
|---|---|
| `message_store`, `channel_message_store` | Direct + channel messages |
| `contact_store`, `contact_discovery_store` | Known + discovered contacts |
| `channel_store`, `channel_order_store`, `channel_settings_store` | Channels, display order, per-channel Smaz toggle |
| `community_store` | Communities (32-byte shared secrets) |
| `contact_group_store`, `contact_settings_store` | Groups, per-contact Smaz toggle |
| `unread_store` | Per-contact unread counts (debounced writes) |
GGUF translation models are stored as files (not SharedPreferences) via `translation_file_store`.
### Theming ### Theming
- Material 3 design (`useMaterial3: true`) - Material 3 design (`useMaterial3: true`)
- System-based dark/light mode (`ThemeMode.system`) - System-based dark/light mode (`ThemeMode.system`)
- Blue color scheme seed - Blue color scheme seed
- `lib/theme/mesh_theme.dart` defines a warm-dark `MeshPalette` (phosphor-green accents) but is **not currently wired** in `main.dart` — available for a future redesign
## BLE Protocol ### Localization
### Nordic UART Service (NUS) 18 locales supported via Flutter's standard ARB pipeline (`lib/l10n/`): en, de, es, fr, it, pt, ru, uk, bg, hu, ja, ko, nl, pl, sk, sl, sv, zh. Language override comes from `AppSettingsService.settings.languageOverride`. Use the `context.l10n` extension (`lib/l10n/l10n.dart`) for translated strings; contact-type names live in `contact_localization.dart`.
- **Service UUID**: `6e400001-b5a3-f393-e0a9-e50e24dcca9e`
- **RX Characteristic**: `6e400002-b5a3-f393-e0a9-e50e24dcca9e` (Write to device)
- **TX Characteristic**: `6e400003-b5a3-f393-e0a9-e50e24dcca9e` (Notify from device)
### Device Discovery ## Transports
- Scans for devices with known name prefixes
- Filters by `platformName` or `advertisementData.advName`
### Connection States `MeshCoreConnector` unifies all three transports under one `ChangeNotifier`. There is **no shared base class** — selection is via the `MeshCoreTransportType { bluetooth, usb, tcp }` enum, and BLE/TCP/USB share the same connection-state enum, send/receive API, and frame protocol.
### Connection State
```dart ```dart
enum MeshCoreConnectionState { enum MeshCoreConnectionState {
disconnected, disconnected,
@ -75,28 +167,136 @@ enum MeshCoreConnectionState {
} }
``` ```
### Frame I/O ### Frame I/O (all transports)
- **Send**: `MeshCoreConnector.sendFrame(Uint8List data)` - **Send**: `MeshCoreConnector.sendFrame(Uint8List data, {String? channelSendQueueId, bool expectsGenericAck})`
- **Receive**: `MeshCoreConnector.receivedFrames` stream of `Uint8List` - **Receive**: `Stream<Uint8List> get receivedFrames`
- **Protocol constants** (`meshcore_protocol.dart`): `maxFrameSize = 172`, `maxTextPayloadBytes = 160`, `appProtocolVersion = 4`
### BLE — Nordic UART Service (NUS)
- **Service UUID**: `6e400001-b5a3-f393-e0a9-e50e24dcca9e`
- **RX Characteristic** (write to device): `6e400002-b5a3-f393-e0a9-e50e24dcca9e`
- **TX Characteristic** (notify from device): `6e400003-b5a3-f393-e0a9-e50e24dcca9e`
- **Discovery**: scans for devices whose name starts with `MeshCore-`, `Whisper-`, `WisCore-`, `Seeed`, `Lilygo`, `HT-`, or `LowMesh_MC_` (filters on both `platformName` and `advertisementData.advName`)
- **Linux**: `linux_ble_pairing_service.dart` falls back to `bluetoothctl` when BlueZ agent prompts fail
### TCP
- Manual host/port entry, persisted via `AppSettingsService` (`tcpServerAddress`, `tcpServerPort`)
- UI hint: `192.168.40.10` / port `5000`
- Disabled on web (`PlatformInfo.isWeb`)
- API: `MeshCoreConnector.connectTcp(host: ..., port: ...)`
### USB Serial (flserial)
- Default baud rate: `115200`
- Port enumeration: `MeshCoreConnector.listUsbPorts()`
- COBS-framed packets via `usb_serial_frame_codec.dart`
- macOS device-name resolution via `ioreg` (`utils/macos_usb_device_names.dart`)
- API: `MeshCoreConnector.connectUsb(portName: ..., baudRate: 115200)`
## Dependencies ## Dependencies
App version: `8.0.0+11` — Dart SDK constraint: `^3.9.2`
**Connectivity**
| Package | Version | Purpose |
|---------|---------|---------|
| flutter_blue_plus | ^2.1.0 | BLE scanning, connecting, and UART data transfer |
| flutter_blue_plus_platform_interface | ^8.2.1 | Platform-interface layer required by flutter_blue_plus |
| flserial | git (MeshEnvy fork) | USB serial transport for wired device connections (TODO: upstream pending) |
**State / Storage**
| Package | Version | Purpose |
|---------|---------|---------|
| provider | ^6.1.5+1 | ChangeNotifier-based state management across screens |
| shared_preferences | ^2.2.2 | Persistent key-value storage for user settings |
| path_provider | ^2.1.5 | Locates platform-appropriate directories for file I/O |
**Crypto**
| Package | Version | Purpose |
|---------|---------|---------|
| crypto | ^3.0.3 | SHA/HMAC hashing used in message authentication |
| pointycastle | ^4.0.0 | AES encryption/decryption for channel and direct messages |
| uuid | ^4.3.3 | Generates UUIDs for message and contact identity |
**Maps & Location**
| Package | Version | Purpose |
|---------|---------|---------|
| flutter_map | ^8.2.2 | Interactive tile map for node positions and path traces |
| latlong2 | ^0.9.1 | LatLng coordinate type used throughout map and GPS code |
| gpx | ^2.3.0 | Export node paths as GPX track files |
**UI**
| Package | Version | Purpose |
|---------|---------|---------|
| material_symbols_icons | ^4.2906.0 | Extended Material Symbols icon set (line-of-sight, etc.) |
| flutter_svg | ^2.0.10+1 | Renders SVG assets (custom icons such as LoS indicator) |
| cached_network_image | ^3.4.1 | Caches map tile images downloaded over the network |
| flutter_cache_manager | ^3.4.1 | Underlying cache manager used by cached_network_image |
| flutter_linkify | ^6.0.0 | Auto-detects and makes URLs tappable in chat messages |
| mobile_scanner | ^7.1.4 | QR/barcode scanning for contact and channel import |
| qr_flutter | ^4.1.0 | Generates QR codes for sharing contacts and channels |
| cupertino_icons | ^1.0.8 | iOS-style icon font (bundled for completeness) |
| characters | ^1.4.0 | Unicode-aware string operations for message text handling |
**Notifications / Background**
| Package | Version | Purpose | | Package | Version | Purpose |
|---------|---------|---------| |---------|---------|---------|
| flutter_blue_plus | ^2.1.0 | BLE communication | | flutter_local_notifications | ^20.1.0 | Shows local push notifications for incoming messages |
| provider | ^6.1.5+1 | State management | | flutter_foreground_task | ^9.2.0 | Keeps the app alive in background to maintain BLE/USB connection |
| cupertino_icons | ^1.0.8 | iOS-style icons |
**ML / AI**
| Package | Version | Purpose |
|---------|---------|---------|
| ml_algo | ^16.0.0 | OLS regression used in `timeout_prediction_service.dart` to predict message ACK timeouts |
| ml_dataframe | ^1.0.0 | DataFrame input format required by ml_algo |
| llamadart | >=0.6.8 <0.7.0 | On-device LLM inference used in `translation_service.dart` for message translation |
**Misc**
| Package | Version | Purpose |
|---------|---------|---------|
| http | ^1.2.0 | Fetches tile URLs and any remote API calls |
| url_launcher | ^6.3.0 | Opens URLs in the system browser from linkified chat text |
| share_plus | ^12.0.1 | Shares files (e.g. exported GPX tracks) via the system share sheet |
| package_info_plus | ^9.0.0 | Reads app version/build number displayed in settings |
| web | ^1.1.1 | Web-platform APIs for USB serial and browser detection on Flutter Web |
| intl | any | Internationalization and locale formatting (required by flutter_localizations) |
| build_pipe | ^0.3.1 | CI/CD build pipeline configuration (web release builds with versioned assets) |
## Platform Configuration ## Platform Configuration
### Android (`android/app/src/main/AndroidManifest.xml`) ### Android (`android/app/src/main/AndroidManifest.xml`)
- `BLUETOOTH`, `BLUETOOTH_ADMIN` (API 30 and below) - `INTERNET` (map tiles, translation model downloads)
- `BLUETOOTH_SCAN`, `BLUETOOTH_CONNECT`, `BLUETOOTH_ADVERTISE` (API 31+) - `BLUETOOTH`, `BLUETOOTH_ADMIN` (API ≤ 30)
- `ACCESS_FINE_LOCATION`, `ACCESS_COARSE_LOCATION` (for BLE scanning) - `BLUETOOTH_SCAN` (with `neverForLocation`), `BLUETOOTH_CONNECT`, `BLUETOOTH_ADVERTISE` (API 31+)
- `ACCESS_FINE_LOCATION`, `ACCESS_COARSE_LOCATION` (BLE scanning on API ≤ 30)
- `POST_NOTIFICATIONS` (API 33+)
- `FOREGROUND_SERVICE`, `FOREGROUND_SERVICE_CONNECTED_DEVICE` (background BLE/USB connection)
- `WAKE_LOCK`
- `CAMERA` (QR scanning, declared as optional feature)
- USB host hardware feature (optional)
`flutter_foreground_task` registers a `ForegroundService` with `foregroundServiceType="connectedDevice"` and `stopWithTask="false"`.
**Build config (`android/app/build.gradle.kts`)**: `applicationId = com.meshcore.meshcore_open`, NDK `29.0.14206865`, Java 8 core-library desugaring (`desugar_jdk_libs:2.1.4`), release signing via `key.properties` (debug fallback).
### iOS (`ios/Runner/Info.plist`) ### iOS (`ios/Runner/Info.plist`)
- `NSBluetoothAlwaysUsageDescription` - `NSBluetoothAlwaysUsageDescription`, `NSBluetoothPeripheralUsageDescription`
- `NSBluetoothPeripheralUsageDescription` - `NSCameraUsageDescription` (QR scanning to join communities)
- Background modes: `bluetooth-central`
- `LSApplicationQueriesSchemes`: `http`, `https`
### Web (`web/`)
PWA scaffold present but boilerplate (`manifest.json` and `index.html` are unmodified Flutter defaults). BLE is unsupported in browsers; TCP and Web Serial USB may work in Chrome only. `ChromeRequiredScreen` gates non-Chrome web users. Versioned releases are produced via `build_pipe` (`?v=<pubspec version>` cache busting, no service worker).
### Desktop
`linux/`, `windows/`, and `macos/` directories are present as Flutter scaffolds. No app-specific native config has been added; BLE on desktop has not been validated.
## Coding Conventions ## Coding Conventions
@ -123,14 +323,14 @@ enum MeshCoreConnectionState {
| File | Purpose | | File | Purpose |
|------|---------| |------|---------|
| `lib/connector/meshcore_connector.dart` | All BLE logic - scanning, connecting, data transfer | | `lib/main.dart` | App configuration, MultiProvider setup, theme, locale, initial route |
| `lib/screens/scanner_screen.dart` | Entry point UI, device list | | `lib/connector/meshcore_connector.dart` | Unified BLE/TCP/USB transport state holder |
| `lib/main.dart` | App configuration, theme, Provider setup | | `lib/connector/meshcore_protocol.dart` | Frame size limits and protocol version |
| `pubspec.yaml` | Dependencies and project metadata | | `lib/connector/meshcore_uuids.dart` | NUS UUIDs and BLE scan name prefixes |
| `lib/services/app_settings_service.dart` | App-wide settings (`AppSettings` JSON in SharedPreferences) |
## Placeholder Screens | `lib/services/storage_service.dart` | Path history + delivery observation persistence |
| `lib/services/message_retry_service.dart` | ACK tracking + retry scheduling |
The following screens are implemented as placeholders and need full implementation: | `lib/services/translation_service.dart` | On-device LLM translation (llamadart) |
- `chat_screen.dart` - Mesh chat functionality | `lib/storage/prefs_manager.dart` | SharedPreferences singleton initialized in `main()` |
- `contacts_screen.dart` - Contact management | `lib/screens/scanner_screen.dart` | Home screen — BLE scan and connect |
- `settings_screen.dart` - Radio settings, node identity, location (partially implemented) | `pubspec.yaml` | Dependencies and project metadata (current version `8.0.0+11`) |

@ -25,8 +25,8 @@ A bottom sheet with a search field and a grid of GIF thumbnails.
### How to Access ### How to Access
App Settings → Appearance → Language App Settings → Appearance → Language
### Supported Languages (15) ### Supported Languages (18)
English, French, Spanish, German, Polish, Slovenian, Portuguese, Italian, Chinese, Swedish, Dutch, Slovak, Bulgarian, Russian, Ukrainian English, French, Spanish, German, Polish, Slovenian, Portuguese, Italian, Chinese, Swedish, Dutch, Slovak, Bulgarian, Russian, Ukrainian, Hungarian, Japanese, Korean
### How It Works ### How It Works
- All UI strings go through Flutter's ARB localization system - All UI strings go through Flutter's ARB localization system
@ -185,3 +185,77 @@ An ML-based service that predicts expected delivery timeouts:
- Blends per-contact statistics with ML predictions - Blends per-contact statistics with ML predictions
- Falls back to `3000 + 3000 × pathLength` ms when insufficient data - Falls back to `3000 + 3000 × pathLength` ms when insufficient data
- Observations are persisted to storage via a 2-second debounced timer (observations within 2s of app termination may be lost) - Observations are persisted to storage via a 2-second debounced timer (observations within 2s of app termination may be lost)
---
## On-Device Message Translation
### What It Is
An optional on-device translation service powered by an embedded LLM (llamadart, running GGUF models). Translation runs entirely on-device — no data leaves the app.
### How to Access
Tap the translate button on any received message. On first use, the GGUF model file is downloaded and cached locally.
### How It Works
- Model files are managed by `TranslationFileStore`; download progress is shown in-place
- Translation runs via `TranslationService` using the llamadart CPU backend (arm64 and x64 on Android)
- Translated text is shown in `TranslatedMessageContent` as an inline overlay on the original message bubble
- Each translation is cached; re-tapping shows the cached result without re-running inference
---
## Emoji Reactions
### How to Access
Long-press a message bubble in any direct or channel chat, then select a reaction emoji.
### What the User Sees
An emoji picker inline with common reactions. Selected reactions appear below the message bubble with a count.
### How It Works
- Implemented via `emoji_picker.dart` and `reaction_helper.dart`
- Reactions are transmitted as a special message type visible to all participants with MeshCore Open
---
## Linkification
### What It Does
URLs and `meshcore://` URIs in received messages are automatically detected and rendered as tappable links.
### How It Works
- Powered by the `flutter_linkify` package via `link_handler.dart`
- Tapping a URL opens the system browser; tapping a `meshcore://` URI imports the contact
---
## GPX Export
### How to Access
Settings → Export section (three options: Export Repeaters, Export Contacts, Export All).
### What It Does
Exports contacts with GPS coordinates to a `.gpx` file via the OS share sheet. Not available on web.
---
## Pinch-to-Zoom Chat Text
### What It Does
Users can pinch to scale all chat text up or down within a session.
### How It Works
- Implemented via `ChatTextScaleService` and `ChatZoomWrapper`
- Scale range: 0.8× to 1.8×
- The chosen scale persists across the session via the service
---
## Background Service (Android)
### What It Does
On Android, a foreground service (`background_service.dart`) keeps the BLE connection and message handling alive when the app is in the background. On other platforms this is a no-op.
### User Impact
- A persistent notification appears while the service is running
- Messages are received and retry logic continues even when the app is not in the foreground

@ -33,7 +33,6 @@ RX (device → host): [0x3E][len_lo][len_hi][payload...]
- Length: 2-byte little-endian, payload only - Length: 2-byte little-endian, payload only
- Max payload: 172 bytes - Max payload: 172 bytes
- TCP: `tcpNoDelay: true` (Nagle disabled), writes serialized to prevent interleaving - TCP: `tcpNoDelay: true` (Nagle disabled), writes serialized to prevent interleaving
- USB: 10ms post-write delay between frames
## Connection State Machine ## Connection State Machine
@ -53,9 +52,11 @@ enum MeshCoreConnectionState {
- `MeshCore-` - `MeshCore-`
- `Whisper-` - `Whisper-`
- `WisCore-` - `WisCore-`
- `Seeed`
- `Lilygo`
- `HT-` - `HT-`
- `LowMesh_MC_` - `LowMesh_MC_`
2. **Connect** with 15-second timeout 2. **Connect** with 15-second timeout (6 seconds on Linux)
3. **Request MTU** 185 bytes (non-web only) 3. **Request MTU** 185 bytes (non-web only)
4. **Discover services** and locate NUS 4. **Discover services** and locate NUS
5. **Enable TX notifications** (up to 3 attempts on native) 5. **Enable TX notifications** (up to 3 attempts on native)
@ -79,7 +80,7 @@ On unexpected disconnection, auto-reconnect with exponential backoff:
| Max path size | 64 bytes | Maximum path data | | Max path size | 64 bytes | Maximum path data |
| Max name size | 32 bytes | Maximum node name | | Max name size | 32 bytes | Maximum node name |
| Max text payload | 160 bytes | Firmware `MAX_TEXT_LEN` | | Max text payload | 160 bytes | Firmware `MAX_TEXT_LEN` |
| App protocol version | 3 | Sent in device query | | App protocol version | 4 | Sent in device query |
| Contact frame size | 148 bytes | Fixed-size contact record | | Contact frame size | 148 bytes | Fixed-size contact record |
## Command Codes (App → Device) ## Command Codes (App → Device)

@ -28,7 +28,6 @@ QuickSwitchBar tab 1 (middle) from any main screen.
- **Scrollable list of channel cards**, each showing: - **Scrollable list of channel cards**, each showing:
- Type icon with color coding (purple badge overlay for community channels) - Type icon with color coding (purple badge overlay for community channels)
- Channel name (or "Channel N" if unnamed) - Channel name (or "Channel N" if unnamed)
- Subtitle: "Public channel", "Hashtag channel", "Private channel", or "Community channel - {name}"
- Unread badge (if messages are unread) - Unread badge (if messages are unread)
- Drag handle (when manual sort is active) - Drag handle (when manual sort is active)
- **"+" FAB** to add a new channel - **"+" FAB** to add a new channel
@ -70,9 +69,9 @@ Tap a channel card to open the channel chat screen.
### App Bar ### App Bar
- Type icon (public/private/hashtag) - Type icon: globe for public channels, tag (#) for all other channel types
- Channel name - Channel name
- Subtitle: "{type} - {N} unread" - Subtitle: "{Public|Private} • {N} unread" (e.g., "Public • 3 unread")
### Message Display ### Message Display
@ -113,17 +112,9 @@ Tap a channel card to open the channel chat screen.
| Path | Desktop only | Opens message path view | | Path | Desktop only | Opens message path view |
| Add Reaction | Incoming messages only | Opens emoji picker (cannot react to your own messages) | | Add Reaction | Incoming messages only | Opens emoji picker (cannot react to your own messages) |
| Copy | All messages | Copies text to clipboard | | Copy | All messages | Copies text to clipboard |
| Mark as Unread | Incoming messages only | Marks this message and all subsequent incoming messages as unread |
| Delete | All messages | Removes locally (not from mesh) | | Delete | All messages | Removes locally (not from mesh) |
### Message Path Viewing
Tap a message bubble to open the Channel Message Path Screen, which shows:
- Each hop in the path as a visual chain
- Known contacts identified by name at each hop
- Observed vs. declared hop counts
- Alternative path variants (if received via multiple paths)
- Map view buttons for geographic path visualization
## Communities ## Communities
Communities are a layer above channels that provide a private namespace. Communities are a layer above channels that provide a private namespace.

@ -37,6 +37,7 @@ From the Contacts screen, tap any Chat-type contact to open the ChatScreen.
### Input Bar ### Input Bar
- **GIF button** (left): Opens GIF picker bottom sheet - **GIF button** (left): Opens GIF picker bottom sheet
- **Translation button** (optional, between GIF and text field): Shown only when translation is enabled in App Settings. Tap to configure outgoing-message translation language and on/off toggle.
- **Text field** (center): Auto-capitalization, enforces UTF-8 byte limit in real-time - **Text field** (center): Auto-capitalization, enforces UTF-8 byte limit in real-time
- **Send button** (right): Submits the message - **Send button** (right): Submits the message
- On desktop: Enter/Numpad Enter also submits - On desktop: Enter/Numpad Enter also submits
@ -66,8 +67,8 @@ Outgoing messages display a status indicator:
When enabled in App Settings, additional metadata appears inside each bubble: When enabled in App Settings, additional metadata appears inside each bubble:
- Timestamp (HH:MM) - Timestamp (HH:MM)
- Retry count (e.g., "Retry 2 of 4") - Retry count (e.g., "Retry 2 of 4") — only shown for outgoing messages where at least one retry has occurred
- Status icon - Status icon (outgoing only)
- Round-trip time in seconds (if delivered) - Round-trip time in seconds (if delivered)
## Message Length Limits ## Message Length Limits
@ -86,7 +87,7 @@ When a direct message is sent:
1. The app computes an expected ACK hash: `SHA256([timestamp][attempt][text][selfPubKey])[0:4]` — matching the firmware's hash calculation. If SMAZ compression is enabled, the compressed text (not the original) is hashed 1. The app computes an expected ACK hash: `SHA256([timestamp][attempt][text][selfPubKey])[0:4]` — matching the firmware's hash calculation. If SMAZ compression is enabled, the compressed text (not the original) is hashed
2. On device acknowledgment (`RESP_CODE_SENT`), the message transitions to "sent" and a timeout timer starts 2. On device acknowledgment (`RESP_CODE_SENT`), the message transitions to "sent" and a timeout timer starts
3. **Timeout duration**: Preferably from the ML timeout prediction service; otherwise `3000 + 3000 × path_length` milliseconds (15000ms for flood) 3. **Timeout duration**: Preferably from the ML timeout prediction service; otherwise calculated from LoRa airtime physics: `500 + (airtime × 6 + 250) × (pathLength + 1)` ms for direct paths, `500 + 16 × airtime` ms for flood (airtime is estimated from the radio's current spreading factor, bandwidth, and coding rate)
4. On timeout, the message is retried with **exponential backoff**: `1000 × 2^retryCount` ms (1s, 2s, 4s, 8s, 16s...) 4. On timeout, the message is retried with **exponential backoff**: `1000 × 2^retryCount` ms (1s, 2s, 4s, 8s, 16s...)
5. **Max retries**: Configurable (default 5, range 210) 5. **Max retries**: Configurable (default 5, range 210)
6. After max retries, the message is marked "failed" — but a **30-second grace window** remains during which a late ACK can still resolve the message to "delivered" 6. After max retries, the message is marked "failed" — but a **30-second grace window** remains during which a late ACK can still resolve the message to "delivered"
@ -115,6 +116,7 @@ Add emoji reactions to incoming messages (not your own):
| Add reaction | Incoming messages only | Opens emoji picker | | Add reaction | Incoming messages only | Opens emoji picker |
| View path | Mobile: tap bubble directly; Desktop: long-press/right-click menu | Shows message routing path | | View path | Mobile: tap bubble directly; Desktop: long-press/right-click menu | Shows message routing path |
| Copy | All messages | Copies text to clipboard | | Copy | All messages | Copies text to clipboard |
| Mark as Unread | Incoming messages only | Marks this message and all subsequent incoming messages as unread |
| Delete | All messages | Removes locally (not from mesh) | | Delete | All messages | Removes locally (not from mesh) |
| Retry | Failed outgoing messages | Re-sends the message | | Retry | Failed outgoing messages | Re-sends the message |
| Open chat with sender | Room server chats | Opens 1:1 chat with the message sender | | Open chat with sender | Room server chats | Opens 1:1 chat with the message sender |

@ -73,7 +73,8 @@ Groups are stored per radio identity (scoped by public key).
| Action | Availability | Description | | Action | Availability | Description |
|---|---|---| |---|---|---|
| Path Trace / Ping | Repeaters, Rooms (always); Chat if `pathLength > 0` | Opens PathTraceMapScreen. Label shows "Ping" when no path bytes are known, "Path Trace" otherwise | | Ping | Repeaters (always) | Opens PathTraceMapScreen targeting the repeater |
| Path Trace | Rooms (always); Chat/Sensor if `pathLength > 0` | Opens PathTraceMapScreen. For rooms, label shows "Ping" when no path bytes are known, "Path Trace" when path bytes are available |
| Manage Repeater | Repeaters only | Login dialog → RepeaterHubScreen | | Manage Repeater | Repeaters only | Login dialog → RepeaterHubScreen |
| Room Login | Rooms only | Login dialog → ChatScreen | | Room Login | Rooms only | Login dialog → ChatScreen |
| Room Management | Rooms only | Login dialog → RepeaterHubScreen (management mode) | | Room Management | Rooms only | Login dialog → RepeaterHubScreen (management mode) |

@ -25,7 +25,7 @@ All contacts with known GPS coordinates are plotted:
| Room | Purple | Meeting room | | Room | Purple | Meeting room |
| Sensor | Orange | Sensors | | Sensor | Orange | Sensors |
Node name labels appear automatically at zoom level 12 and above. Node name labels appear automatically at zoom level 14 and above.
### Shared Map Pins (Flag Icons) ### Shared Map Pins (Flag Icons)
Location pins shared in chat messages are displayed as flags: Location pins shared in chat messages are displayed as flags:
@ -88,10 +88,10 @@ Shows a bottom sheet with:
- **Set as my location**: Updates your device's advertised location - **Set as my location**: Updates your device's advertised location
### Filter Dialog (FAB) ### Filter Dialog (FAB)
Toggle visibility of: chat nodes, repeaters, other nodes, guessed locations, discovery contacts. Toggle visibility of: chat nodes, repeaters, other nodes, guessed locations, discovery contacts, overlapping markers (stacked markers at similar coordinates), and shared map pins (flag markers).
Additional filters: Additional filters:
- **Key prefix filter**: Show only contacts whose public key starts with a given prefix - **Key prefix filter**: Show only contacts whose public key starts with a given prefix
- **Last-seen time slider**: From 1 hour to "all time" - **Last-seen time slider**: Exponential scale from near-zero to 6 months, with "all time" at the top end
### Legend Card (Top-Right) ### Legend Card (Top-Right)
Shows node count and pin count. Tappable to expand a legend of all marker types. Shows node count and pin count. Tappable to expand a legend of all marker types.

@ -22,31 +22,16 @@ The QuickSwitchBar is a Material 3 `NavigationBar` with a frosted-glass visual t
Tapping a tab replaces the current screen with a subtle fade + slight horizontal nudge transition (220ms forward, 200ms reverse). The back button is suppressed on all three main screens — navigation between them is flat, not stacked. All icons use outline variants (`people_outline`, `tag`, `map_outlined`) following Material 3 conventions. Tapping a tab replaces the current screen with a subtle fade + slight horizontal nudge transition (220ms forward, 200ms reverse). The back button is suppressed on all three main screens — navigation between them is flat, not stacked. All icons use outline variants (`people_outline`, `tag`, `map_outlined`) following Material 3 conventions.
## Device Screen ## Disconnection
The Device Screen is a transitional hub that shows after connection. In practice, the app navigates directly to Contacts after connecting, but the Device Screen is reachable via the QuickSwitchBar. - The disconnect button (available in the Settings screen and other main screens) shows a confirmation dialog before disconnecting
### What the User Sees
**App Bar**:
- Left: Battery indicator chip (tappable — toggles between percentage and voltage display). Icon changes based on level: `battery_unknown` when data unavailable, `battery_alert` (orange) at 15% or below, `battery_full` otherwise
- Left-aligned title (`centerTitle: false`): Two-line layout — small grey "MeshCore" label above the device name in bold
- Right: Disconnect button (`bluetooth_disabled` crossed-out icon) and Settings button (tune icon)
**Body**:
- **Connection Card**: Device avatar, device name, device ID, "Connected" chip, and battery chip
- **Quick Switch** section: The QuickSwitchBar widget for navigating to Contacts/Channels/Map
### Disconnection
- The disconnect button shows a confirmation dialog before disconnecting
- If the device disconnects unexpectedly, the app automatically navigates back to the Scanner screen (fires after the current frame completes via a post-frame callback) - If the device disconnects unexpectedly, the app automatically navigates back to the Scanner screen (fires after the current frame completes via a post-frame callback)
- This auto-navigation behavior (`DisconnectNavigationMixin`) is shared across all main screens - This auto-navigation behavior (`DisconnectNavigationMixin`) is shared across all main screens
## Theme and Locale ## Theme and Locale
- **Theme mode** is user-configurable in App Settings (System / Light / Dark) — not locked to system - **Theme mode** is user-configurable in App Settings (System / Light / Dark) — not locked to system
- **Language** can be overridden to one of 15 supported languages, or follow the system locale - **Language** can be overridden to one of 18 supported languages, or follow the system locale
- On web, if a non-Chromium browser is detected, the app shows a `ChromeRequiredScreen` instead of the Scanner (Web Bluetooth requires Chromium) - On web, if a non-Chromium browser is detected, the app shows a `ChromeRequiredScreen` instead of the Scanner (Web Bluetooth requires Chromium)
## Full Navigation Graph ## Full Navigation Graph

@ -73,7 +73,7 @@ There is no per-contact muting.
The notification system prevents notification storms: The notification system prevents notification storms:
- **Minimum interval**: 3 seconds between individual notifications - **Minimum interval**: 3 seconds between individual notifications
- **Batch window**: If multiple notifications arrive within 5 seconds, they are combined into a single summary notification on a fourth Android channel (`batch_summary`): "MeshCore Activity — 2 messages, 1 channel message, 3 new nodes". Note: batch summaries are Android-only; on Apple platforms individual notifications are shown - **Batch window**: If multiple notifications arrive within 5 seconds, they are combined into a single summary notification on a fourth Android channel (`batch_summary`). The title is "MeshCore Activity" and the body lists the grouped counts (e.g., "2 messages, 1 channel message, 3 new nodes"). Batch summaries are Android-only; queued notifications that overflow the batch window are silently dropped on other platforms
## Notification Clearing ## Notification Clearing

@ -17,8 +17,8 @@ From the Contacts screen:
- Password field with show/hide toggle - Password field with show/hide toggle
- "Save password" checkbox (persists for future logins). If a saved password exists, it is pre-filled and the checkbox is pre-checked, making login one-tap - "Save password" checkbox (persists for future logins). If a saved password exists, it is pre-filled and the checkbox is pre-checked, making login one-tap
- Routing mode selector and "Manage Paths" link are available directly in the dialog (configure routing before login) - Routing mode selector and "Manage Paths" link are available directly in the dialog (configure routing before login)
- Auto-retries up to 5 times on timeout, showing progress ("Attempt 2 of 5"). A wrong password stops immediately after the first attempt — only timeouts trigger retries - Auto-retries up to 5 times on timeout, showing progress ("Attempt 2 of 5"). A wrong password (explicit failure response) stops immediately — only timeouts trigger retries
- After 5 failed attempts, further login attempts are blocked - If auto-clock-sync is enabled for this repeater (configured in Repeater Settings), a `clock sync` command is sent automatically on successful login
--- ---
@ -28,15 +28,17 @@ The central management screen showing:
- **Header card**: Repeater name, short public key, path label, GPS coordinates (if known) - **Header card**: Repeater name, short public key, path label, GPS coordinates (if known)
- **Battery chemistry selector**: NMC / LiFePO4 / LiPo (saved per repeater) - **Battery chemistry selector**: NMC / LiFePO4 / LiPo (saved per repeater)
- **Management tool cards** (full-width cards with chevron arrows, not a grid). Title dynamically shows "Repeater Management" or "Room Management" based on contact type: - **Management tool cards** (full-width cards with chevron arrows, not a grid). Title dynamically shows "Repeater Management" or "Room Management" (admin) or "Repeater Guest" / "Room Guest" (guest) based on contact type and login result:
| Card | Destination | | Card | Destination | Visibility |
|---|---| |---|---|---|
| Status | Repeater Status Screen | | Status | Repeater Status Screen | All users |
| Telemetry | Telemetry Screen | | Telemetry | Telemetry Screen | All users |
| CLI | Repeater CLI Screen | | CLI | Repeater CLI Screen | Admin only |
| Neighbors | Neighbors Screen | | Neighbors | Neighbors Screen | All users |
| Settings | Repeater Settings Screen | | Settings | Repeater Settings Screen | Admin only |
The battery chemistry selector and CLI/Settings cards are hidden from guest users.
--- ---
@ -47,26 +49,28 @@ The central management screen showing:
Three information cards: Three information cards:
**System Information**: **System Information**:
- Battery percentage - Battery percentage and voltage (e.g. "85% / 3.95V"), using the battery chemistry set in the hub screen
- Uptime
- Queue length
- Error flags
- Clock at login time - Clock at login time
- Uptime (days/hours/minutes/seconds)
- Queue length
- Debug flags (error event count)
**Radio Statistics**: **Radio Statistics**:
- Last RSSI and SNR - Last RSSI and SNR
- Noise floor - Noise floor
- TX and RX airtime - TX airtime and RX airtime
**Packet Statistics**: **Packet Statistics**:
- Packets sent, received, and duplicates - Packets sent and received, each broken down by flood vs. direct
- Broken down by flood vs. direct - Duplicates, broken down by flood vs. direct
- Channel utilization (% of uptime used by TX + RX)
### Key Interactions ### Key Interactions
- Auto-queries the repeater on open; shows a loading spinner until data arrives - Auto-queries the repeater on open; shows a loading spinner until data arrives
- On timeout: red snackbar error. On success: data appears with a green snackbar confirmation - On timeout: red snackbar error. On success: data appears in-place (no extra snackbar)
- Pull-to-refresh or refresh button to re-query - Pull-to-refresh or refresh button in the app bar to re-query
- Routing mode popup and path management dialog in app bar (these controls appear on **all** management sub-screens, not just Status) - Routing mode popup and path management dialog in app bar (these controls appear on **all** management sub-screens, not just Status)
- Accepts both binary `RESP_CODE_STATUS_RESPONSE` frames and legacy JSON text responses
--- ---
@ -76,7 +80,7 @@ A terminal-style interface for sending commands directly to the repeater.
### What the User Sees ### What the User Sees
- **Quick-command bar** (horizontal scroll): Shortcut buttons for common commands (get name, get radio, get tx, neighbors, ver, advert, clock) - **Quick-command bar** (horizontal scroll): Shortcut buttons for 9 common commands (advert, get name, get radio, get tx, discover.neighbors, neighbors, ver, clock, clock sync)
- **Command history list**: Sent commands in primary color, responses in secondary color - **Command history list**: Sent commands in primary color, responses in secondary color
- **Input bar**: Up/down history arrows, monospace text field with `> ` prefix, send button - **Input bar**: Up/down history arrows, monospace text field with `> ` prefix, send button
@ -92,9 +96,13 @@ A terminal-style interface for sending commands directly to the repeater.
### Available CLI Commands ### Available CLI Commands
**General**: `advert`, `reboot`, `clock`, `password`, `ver`, `clear stats` The in-app help reference (help icon) documents all known commands. Categories:
**General**: `advert`, `advert.zerohop`, `reboot`, `clock`, `clock sync`, `password`, `ver`, `clear stats`, `erase`, `poweroff`, `shutdown`, `clkreboot`, `start ota`, `time`, `board`, `discover.neighbors`, `powersaving`, `stats-packets`, `stats-radio`, `stats-core`
**Get**: `get name`, `get role`, `get public.key`, `get prv.key`, `get repeat`, `get tx`, `get freq`, `get radio`, `get radio.rxgain`, `get af`, `get dutycycle`, `get int.thresh`, `get agc.reset.interval`, `get multi.acks`, `get allow.read.only`, `get advert.interval`, `get flood.advert.interval`, `get guest.password`, `get lat`, `get lon`, `get rxdelay`, `get txdelay`, `get direct.txdelay`, `get flood.max`, `get owner.info`, `get path.hash.mode`, `get loop.detect`, `get acl`, `get bridge.*`, `get adc.multiplier`, `get bootloader.ver`
**Settings**: `set name`, `set af`, `set tx`, `set repeat`, `set allow.read.only`, `set flood.max`, `set int.thresh`, `set agc.reset.interval`, `set multi.acks`, `set advert.interval`, `set flood.advert.interval`, `set guest.password`, `set lat`, `set lon`, `set radio`, `set rxdelay`, `set txdelay`, `set direct.txdelay`, `set bridge.*`, `set adc.multiplier`, `tempradio`, `setperm` **Set**: `set name`, `set af`, `set tx`, `set repeat`, `set allow.read.only`, `set flood.max`, `set int.thresh`, `set agc.reset.interval`, `set multi.acks`, `set advert.interval`, `set flood.advert.interval`, `set guest.password`, `set lat`, `set lon`, `set freq`, `set radio`, `set rxdelay`, `set txdelay`, `set direct.txdelay`, `set radio.rxgain`, `set dutycycle`, `set loop.detect`, `set path.hash.mode`, `set owner.info`, `set prv.key`, `set bridge.*`, `set adc.multiplier`, `tempradio`, `setperm`
**Bridge**: `get bridge.type` **Bridge**: `get bridge.type`
@ -102,9 +110,11 @@ A terminal-style interface for sending commands directly to the repeater.
**Neighbors**: `neighbors`, `neighbor.remove` **Neighbors**: `neighbors`, `neighbor.remove`
**Region Management**: `region`, `region load/get/put/remove/allowf/denyf/home/save` **Power Management**: `get pwrmgt.support`, `get pwrmgt.source`, `get pwrmgt.bootreason`, `get pwrmgt.bootmv`
**GPS**: `gps`, `gps on/off/sync/setloc/advert` **Sensors**: `sensor get {key}`
**Region Management**: `region`, `region load`, `region get`, `region put`, `region remove`, `region allowf`, `region denyf`, `region home`, `region save`, `region default`, `region list allowed`, `region list denied`
--- ---
@ -147,40 +157,63 @@ A card titled "Repeater's Neighbors - N" listing each neighbor as:
### What the User Sees ### What the User Sees
Five configuration cards: Nine configuration cards, each with its own per-field refresh button(s):
**1. Basic Settings** **1. Basic Settings**
- Name field - Name field
- Admin password field - Admin password field (write-only; always sent when non-empty)
- Guest password field - Guest password field (write-only; always sent when non-empty)
**2. Radio Settings** **2. Radio Settings**
- Frequency (MHz) - Frequency (MHz)
- TX Power (dBm) - TX Power (dBm) — has its own independent refresh button
- Bandwidth dropdown (kHz) - Bandwidth dropdown (kHz)
- Spreading Factor (SF5SF12) - Spreading Factor (SF5SF12)
- Coding Rate (4/54/8) - Coding Rate (4/54/8)
- RX Gain boost toggle
**3. Location Settings** **3. Location Settings**
- Latitude and longitude fields - Latitude and longitude fields, each with an independent refresh button
**4. Features** **4. Features**
- Packet forwarding toggle - Packet forwarding toggle (`set repeat`)
- Guest access toggle - Guest access toggle (`set allow.read.only`)
- Multi-ACKs toggle (`set multi.acks`)
- Auto clock sync after login toggle (local app setting only, not sent to repeater)
**5. Network Health**
- Loop detection dropdown (off / minimal / moderate / strict; `set loop.detect`)
- Duty cycle slider (1100%; `set dutycycle`)
**5. Advertisement Settings** **6. Advertisement Settings**
- Local advert interval slider (60240 minutes) with enable/disable toggle - Local advert interval slider (60240 minutes) with enable/disable toggle
- Flood advert interval slider (3168 hours) with enable/disable toggle - Flood advert interval slider (3168 hours) with enable/disable toggle
- Flood max hops slider (064; `set flood.max`)
**7. Owner Info**
- Multi-line text field for operator contact info (`set owner.info`); newlines sent as `|`
**8. Actions** (one-tap, no save needed)
- Send Advertisement (`advert`)
- Send Zero-Hop Advertisement (`advert.zerohop`)
- Clock Sync (`clock sync`)
**9. Advanced** (collapsed by default)
- Path hash mode dropdown (02; `set path.hash.mode`)
- TX delay field (`set txdelay`)
- Direct TX delay field (`set direct.txdelay`)
- Interference threshold field (`set int.thresh`)
- AGC reset interval slider (0240s in multiples of 4; `set agc.reset.interval`)
**6. Danger Zone** (red-styled card) **Danger Zone** (red-styled card)
- Reboot repeater - Reboot repeater (sends `reboot` with confirmation dialog)
- Erase filesystem (serial-only warning) - Erase filesystem (serial-only; shows informational snackbar only — no command is sent over the air)
### Key Interactions ### Key Interactions
- **Settings are NOT auto-fetched on open**. Only name and location are pre-filled from locally cached contact data. You must tap each section's refresh button to fetch live values from the repeater - **Settings are NOT auto-fetched on open**. Name is pre-filled from cached contact data. Each section has its own refresh button to fetch live values from the repeater
- TX Power has its own separate refresh button, independent from the main Radio Settings refresh - TX Power, RX Gain, latitude, longitude, and advanced fields each have independent inline refresh buttons
- Save button appears when changes are detected - Save button in app bar appears when any change is detected; failed commands keep those fields dirty for retry
- Settings are sent sequentially with 200ms delays between commands (fire-and-forget, no per-command acknowledgment wait) - Settings are sent sequentially with 200ms delays between commands; firmware responses are checked and partial failures are reported in a snackbar
- Validation prevents invalid values (e.g., frequency range, LoRa parameter compatibility) - Some changes (e.g. radio frequency) require a reboot; the firmware response triggers an orange "reboot needed" snackbar
- Advertisement interval sliders reset to defaults when re-enabled (local: 60 min, flood: 3 hours) - Advertisement interval sliders reset to defaults when re-enabled (local: 60 min, flood: 3 hours)
- **Erase Filesystem** does NOT send any command over the air — tapping it only shows a snackbar explaining the operation requires physical serial access. It is effectively non-functional when connected wirelessly - **Erase Filesystem** does NOT send any command over the air — tapping it only shows a snackbar explaining the operation requires physical serial access

@ -51,7 +51,7 @@ Note: The weak (-80 to -90 dBm) and poor (< -90 dBm) tiers share the same icon s
### How Scanning Works ### How Scanning Works
- Filters for devices with names starting with `MeshCore-` or `Whisper-` - Filters for devices with names starting with one of the known prefixes: `MeshCore-`, `Whisper-`, `WisCore-`, `Seeed`, `Lilygo`, `HT-`, `LowMesh_MC_`
- Uses low-latency scan mode on Android - Uses low-latency scan mode on Android
- Scans for 10 seconds then auto-stops - Scans for 10 seconds then auto-stops
- On iOS/macOS, waits for BLE adapter initialization before starting - On iOS/macOS, waits for BLE adapter initialization before starting
@ -61,7 +61,7 @@ Note: The weak (-80 to -90 dBm) and poor (< -90 dBm) tiers share the same icon s
Tap a device tile or its Connect button: Tap a device tile or its Connect button:
1. The connector stops scanning and transitions to "connecting" 1. The connector stops scanning and transitions to "connecting"
2. Connects to the device with a 15-second timeout 2. Connects to the device with a 15-second timeout (6 seconds on Linux)
3. Requests MTU 185 bytes for optimal throughput 3. Requests MTU 185 bytes for optimal throughput
4. Discovers BLE services and locates the Nordic UART Service 4. Discovers BLE services and locates the Nordic UART Service
5. Subscribes to TX notifications for receiving data 5. Subscribes to TX notifications for receiving data

@ -46,7 +46,7 @@ A dedicated sub-screen for app-level preferences (nothing here is sent to the de
### Appearance ### Appearance
- **Theme**: System / Light / Dark - **Theme**: System / Light / Dark
- **Language**: System default or one of 15 languages (English, French, Spanish, German, Polish, Slovenian, Portuguese, Italian, Chinese, Swedish, Dutch, Slovak, Bulgarian, Russian, Ukrainian) - **Language**: System default or one of 18 languages (English, French, Spanish, German, Polish, Slovenian, Portuguese, Italian, Chinese, Swedish, Dutch, Slovak, Bulgarian, Russian, Ukrainian, Hungarian, Japanese, Korean)
- **Enable Message Tracing**: Shows path trace overlays and extra metadata on messages - **Enable Message Tracing**: Shows path trace overlays and extra metadata on messages
### Notifications ### Notifications
@ -57,6 +57,7 @@ A dedicated sub-screen for app-level preferences (nothing here is sent to the de
### Messaging ### Messaging
- **Clear Path on Max Retry**: Erases the stored routing path after all retries fail - **Clear Path on Max Retry**: Erases the stored routing path after all retries fail
- **Jump to Oldest Unread**: When opening a chat, scrolls to the oldest unread message instead of the newest
- **Auto Route Rotation**: Enables weighted routing algorithm. When enabled, expands to show five slider sub-settings (hidden when off): - **Auto Route Rotation**: Enables weighted routing algorithm. When enabled, expands to show five slider sub-settings (hidden when off):
- Max Route Weight (110, default 5, integer steps) - Max Route Weight (110, default 5, integer steps)
- Initial Route Weight (0.55.0, default 3.0) - Initial Route Weight (0.55.0, default 3.0)
@ -67,6 +68,15 @@ A dedicated sub-screen for app-level preferences (nothing here is sent to the de
### Battery ### Battery
- **Battery Chemistry**: NMC / LiFePO4 / LiPo (per device, used to calibrate percentage from voltage) - **Battery Chemistry**: NMC / LiFePO4 / LiPo (per device, used to calibrate percentage from voltage)
### Translation
Not shown on web. Controls on-device message translation powered by a locally-downloaded ML model:
- **Enable Translation**: Translates incoming messages into the selected target language
- **Translate Composer**: Translates outgoing messages from the target language back before sending
- **Target Language**: Language to translate into (searchable list; defaults to the app language)
- **Downloaded Model**: Dropdown to select among already-downloaded translation models
- **Preset Model**: Download a curated preset model with one tap
- **Custom Model URL**: Enter a URL to download a custom GGUF-format model; shows download progress and a cancel button
### Map Display ### Map Display
- **Show Repeaters**: Toggle repeater markers on map - **Show Repeaters**: Toggle repeater markers on map
- **Show Chat Nodes**: Toggle chat node markers - **Show Chat Nodes**: Toggle chat node markers
@ -91,7 +101,7 @@ These settings are sent directly to the connected device firmware.
### Radio Settings ### Radio Settings
Opens a dialog pre-populated with the device's current radio settings. Contains: Opens a dialog pre-populated with the device's current radio settings. Contains:
- **Preset dropdown**: 19 regional presets — selecting a preset immediately fills all fields below. Full list: Australia, Australia (Narrow), Australia SA/WA/QLD, Czech Republic, EU 433MHz, EU/UK (Long Range), EU/UK (Medium Range), EU/UK (Narrow), New Zealand, New Zealand (Narrow), Portugal 433, Portugal 869, Switzerland, USA Arizona, USA/Canada, Vietnam, Off-Grid 433, Off-Grid 869, Off-Grid 918 - **Preset dropdown**: 19 regional presets — selecting a preset immediately fills all fields below. Full list: Australia, Australia (Narrow), Australia SA, WA, QLD, Czech Republic, EU 433MHz, EU/UK (Long Range), EU/UK (Medium Range), EU/UK (Narrow), New Zealand, New Zealand (Narrow), Portugal 433, Portugal 869, Switzerland, USA Arizona, USA/Canada, Vietnam, Off-Grid 433, Off-Grid 869, Off-Grid 918
- **Frequency** (MHz): Free text, validated 3002500 MHz - **Frequency** (MHz): Free text, validated 3002500 MHz
- **Bandwidth**: Dropdown (7.8 / 10.4 / 15.6 / 20.8 / 31.25 / 41.7 / 62.5 / 125 / 250 / 500 kHz) - **Bandwidth**: Dropdown (7.8 / 10.4 / 15.6 / 20.8 / 31.25 / 41.7 / 62.5 / 125 / 250 / 500 kHz)
- **Spreading Factor**: SF5SF12 - **Spreading Factor**: SF5SF12

@ -8,7 +8,6 @@ list(APPEND FLUTTER_PLUGIN_LIST
list(APPEND FLUTTER_FFI_PLUGIN_LIST list(APPEND FLUTTER_FFI_PLUGIN_LIST
flserial flserial
jni
) )
set(PLUGIN_BUNDLED_LIBRARIES) set(PLUGIN_BUNDLED_LIBRARIES)

@ -11,7 +11,6 @@ list(APPEND FLUTTER_PLUGIN_LIST
list(APPEND FLUTTER_FFI_PLUGIN_LIST list(APPEND FLUTTER_FFI_PLUGIN_LIST
flserial flserial
flutter_local_notifications_windows flutter_local_notifications_windows
jni
) )
set(PLUGIN_BUNDLED_LIBRARIES) set(PLUGIN_BUNDLED_LIBRARIES)

Loading…
Cancel
Save

Powered by TurnKey Linux.