diff --git a/build-logic/convention/src/main/kotlin/org/meshtastic/buildlogic/DocsTasks.kt b/build-logic/convention/src/main/kotlin/org/meshtastic/buildlogic/DocsTasks.kt index 450b05e3b..15cbdca9e 100644 --- a/build-logic/convention/src/main/kotlin/org/meshtastic/buildlogic/DocsTasks.kt +++ b/build-logic/convention/src/main/kotlin/org/meshtastic/buildlogic/DocsTasks.kt @@ -61,7 +61,7 @@ class DocsTasks : Plugin { bundleDir.set(outputDir.map { it.dir("common") }) schemaFile.set( project.rootProject.layout.projectDirectory - .file("specs/003-app-docs-markdown/contracts/keyword-index-schema.json") + .file("specs/20260507-161858-app-docs-markdown/contracts/keyword-index-schema.json") ) } diff --git a/docs/_config.yml b/docs/_config.yml index 4b76bae58..bd7c6dc00 100644 --- a/docs/_config.yml +++ b/docs/_config.yml @@ -28,12 +28,12 @@ color_scheme: meshtastic # Default front-matter for pages in subdirectories defaults: - scope: - path: "user" + path: "user/" values: parent: User Guide layout: default - scope: - path: "developer" + path: "developer/" values: parent: Developer Guide layout: default @@ -41,229 +41,229 @@ defaults: # They use a dedicated locale layout with a back-link to the English version. # Auto-generated from Android app locales (values-* resource dirs). - scope: - path: "ar" + path: "ar/" values: layout: locale_page locale: ar nav_exclude: true - scope: - path: "be" + path: "be/" values: layout: locale_page locale: be nav_exclude: true - scope: - path: "bg" + path: "bg/" values: layout: locale_page locale: bg nav_exclude: true - scope: - path: "ca" + path: "ca/" values: layout: locale_page locale: ca nav_exclude: true - scope: - path: "cs" + path: "cs/" values: layout: locale_page locale: cs nav_exclude: true - scope: - path: "de" + path: "de/" values: layout: locale_page locale: de nav_exclude: true - scope: - path: "el" + path: "el/" values: layout: locale_page locale: el nav_exclude: true - scope: - path: "es" + path: "es/" values: layout: locale_page locale: es nav_exclude: true - scope: - path: "et" + path: "et/" values: layout: locale_page locale: et nav_exclude: true - scope: - path: "fi" + path: "fi/" values: layout: locale_page locale: fi nav_exclude: true - scope: - path: "fr" + path: "fr/" values: layout: locale_page locale: fr nav_exclude: true - scope: - path: "ga" + path: "ga/" values: layout: locale_page locale: ga nav_exclude: true - scope: - path: "gl" + path: "gl/" values: layout: locale_page locale: gl nav_exclude: true - scope: - path: "he" + path: "he/" values: layout: locale_page locale: he nav_exclude: true - scope: - path: "hr" + path: "hr/" values: layout: locale_page locale: hr nav_exclude: true - scope: - path: "ht" + path: "ht/" values: layout: locale_page locale: ht nav_exclude: true - scope: - path: "hu" + path: "hu/" values: layout: locale_page locale: hu nav_exclude: true - scope: - path: "is" + path: "is/" values: layout: locale_page locale: is nav_exclude: true - scope: - path: "it" + path: "it/" values: layout: locale_page locale: it nav_exclude: true - scope: - path: "ja" + path: "ja/" values: layout: locale_page locale: ja nav_exclude: true - scope: - path: "ko" + path: "ko/" values: layout: locale_page locale: ko nav_exclude: true - scope: - path: "lt" + path: "lt/" values: layout: locale_page locale: lt nav_exclude: true - scope: - path: "nl" + path: "nl/" values: layout: locale_page locale: nl nav_exclude: true - scope: - path: "no" + path: "no/" values: layout: locale_page locale: no nav_exclude: true - scope: - path: "pl" + path: "pl/" values: layout: locale_page locale: pl nav_exclude: true - scope: - path: "pt" + path: "pt/" values: layout: locale_page locale: pt nav_exclude: true - scope: - path: "pt-rBR" + path: "pt-rBR/" values: layout: locale_page locale: pt-rBR nav_exclude: true - scope: - path: "ro" + path: "ro/" values: layout: locale_page locale: ro nav_exclude: true - scope: - path: "ru" + path: "ru/" values: layout: locale_page locale: ru nav_exclude: true - scope: - path: "sk" + path: "sk/" values: layout: locale_page locale: sk nav_exclude: true - scope: - path: "sl" + path: "sl/" values: layout: locale_page locale: sl nav_exclude: true - scope: - path: "sq" + path: "sq/" values: layout: locale_page locale: sq nav_exclude: true - scope: - path: "sr" + path: "sr/" values: layout: locale_page locale: sr nav_exclude: true - scope: - path: "sv" + path: "sv/" values: layout: locale_page locale: sv nav_exclude: true - scope: - path: "tr" + path: "tr/" values: layout: locale_page locale: tr nav_exclude: true - scope: - path: "uk" + path: "uk/" values: layout: locale_page locale: uk nav_exclude: true - scope: - path: "zh-rCN" + path: "zh-rCN/" values: layout: locale_page locale: zh-rCN nav_exclude: true - scope: - path: "zh-rTW" + path: "zh-rTW/" values: layout: locale_page locale: zh-rTW diff --git a/docs/_includes/language_switcher.html b/docs/_includes/language_switcher.html index 1b0cf4238..3fe3bb662 100644 --- a/docs/_includes/language_switcher.html +++ b/docs/_includes/language_switcher.html @@ -15,44 +15,56 @@ {% assign locales = site.data.locales %} {% if locales and current_path %} +{% assign path_parts = current_path | split: "/" %} +{% assign first_segment = path_parts[0] %} + +{% comment %} Build the list of available translations first {% endcomment %} +{% assign has_translations = false %} + +{% if locales[first_segment] %} + {% comment %} We're on a translated page — English link is always available {% endcomment %} + {% assign has_translations = true %} + {% assign remaining_parts = path_parts | slice: 1, path_parts.size %} + {% assign en_path = remaining_parts | join: "/" | replace: ".md", "" %} +{% else %} + {% comment %} Check if any translated version exists {% endcomment %} + {% assign en_relative = current_path | replace: ".md", "" %} + {% for locale in locales %} + {% assign locale_code = locale[0] %} + {% assign locale_file = locale_code | append: "/" | append: en_relative | append: ".md" %} + {% for p in site.pages %} + {% if p.path == locale_file %} + {% assign has_translations = true %} + {% break %} + {% endif %} + {% endfor %} + {% if has_translations %}{% break %}{% endif %} + {% endfor %} +{% endif %} + +{% if has_translations %}
🌐 English
{% endif %} +{% endif %} diff --git a/docs/assets/screenshots/README.md b/docs/assets/screenshots/README.md index 5fb2efe28..edf5ca8d1 100644 --- a/docs/assets/screenshots/README.md +++ b/docs/assets/screenshots/README.md @@ -30,7 +30,6 @@ Then copy the relevant light-mode PNGs from the reference directory. The Examples: - `onboarding_welcome.png` - `connections_bluetooth_scan.png` -- `messages-and-channels_channel_list.png` - `firmware_disclaimer.png` ## Guidelines diff --git a/docs/assets/screenshots/messages-and-channels_channel_list.png b/docs/assets/screenshots/messages-and-channels_channel_list.png deleted file mode 100644 index 4054990a1..000000000 Binary files a/docs/assets/screenshots/messages-and-channels_channel_list.png and /dev/null differ diff --git a/docs/assets/screenshots/messages_channel_info.png b/docs/assets/screenshots/messages_channel_info.png deleted file mode 100644 index 6f022378f..000000000 Binary files a/docs/assets/screenshots/messages_channel_info.png and /dev/null differ diff --git a/docs/assets/screenshots/settings-radio-user_lora_config.png b/docs/assets/screenshots/settings-radio-user_lora_config.png deleted file mode 100644 index aecd2b620..000000000 Binary files a/docs/assets/screenshots/settings-radio-user_lora_config.png and /dev/null differ diff --git a/docs/developer.md b/docs/developer.md index 3802081cd..19785e5c7 100644 --- a/docs/developer.md +++ b/docs/developer.md @@ -3,7 +3,6 @@ title: Developer Guide layout: default nav_order: 2 has_children: true -parent: "" --- # Developer Guide diff --git a/docs/developer/adding-a-feature-module.md b/docs/developer/adding-a-feature-module.md index e2bc3634d..ae290dbda 100644 --- a/docs/developer/adding-a-feature-module.md +++ b/docs/developer/adding-a-feature-module.md @@ -23,15 +23,10 @@ mkdir -p feature/my-feature/src/{commonMain,commonTest,androidMain,jvmMain,iosMa ```kotlin plugins { alias(libs.plugins.meshtastic.kmp.feature) - alias(libs.plugins.meshtastic.kotlinx.serialization) - id("meshtastic.kmp.jvm.android") } kotlin { - android { - namespace = "org.meshtastic.feature.myfeature" - androidResources.enable = false - } + androidLibrary { withHostTest { } } sourceSets { commonMain.dependencies { @@ -82,8 +77,8 @@ class FeatureMyFeatureModule ## 5. Register DI in App/Desktop Add your module to: -- `app/src/main/kotlin/org/meshtastic/app/di/AppKoinModule.kt` -- `desktop/src/main/kotlin/org/meshtastic/desktop/di/DesktopKoinModule.kt` +- `androidApp/src/main/kotlin/org/meshtastic/app/di/AppKoinModule.kt` +- `desktopApp/src/main/kotlin/org/meshtastic/desktop/di/DesktopKoinModule.kt` ## 6. Add Navigation Routes diff --git a/docs/developer/architecture.md b/docs/developer/architecture.md index c3d5395ff..8d1bacc1b 100644 --- a/docs/developer/architecture.md +++ b/docs/developer/architecture.md @@ -16,7 +16,7 @@ The Meshtastic Android/Desktop/iOS application follows a modular Kotlin Multipla ``` ┌─────────────────────────────────────────────┐ -│ app / desktop │ Platform entry points +│ androidApp / desktopApp │ Platform entry points ├─────────────────────────────────────────────┤ │ feature/* modules │ UI + Business Logic ├─────────────────────────────────────────────┤ @@ -28,7 +28,7 @@ The Meshtastic Android/Desktop/iOS application follows a modular Kotlin Multipla ## Module Categories -### `app/` — Android Application +### `androidApp/` — Android Application The Android application entry point: - Activity, Application, and Manifest definitions @@ -36,13 +36,13 @@ The Android application entry point: - Flavor-specific bindings (`google/`, `fdroid/`) - Android-only integrations (widgets, services) -### `desktop/` — Desktop JVM Application +### `desktopApp/` — Desktop JVM Application The Desktop (Linux/macOS/Windows) entry point: - Compose Desktop window management - Desktop-specific DI (`DesktopKoinModule`) - Platform stubs for Android-only capabilities -- Serial transport implementation +- BLE (Kable), Serial, and TCP transport implementations ### `feature/*` — Feature Modules diff --git a/docs/developer/codebase.md b/docs/developer/codebase.md index 045223dfa..9f67c6e97 100644 --- a/docs/developer/codebase.md +++ b/docs/developer/codebase.md @@ -16,11 +16,11 @@ Repository layout, namespacing conventions, and build system overview. ``` Meshtastic-Android/ -├── app/ # Android application module +├── androidApp/ # Android application module │ ├── src/main/ # Shared Android code │ ├── src/google/ # Google Play flavor (Gemini, proprietary) │ └── src/fdroid/ # F-Droid flavor (FOSS-only) -├── desktop/ # Desktop JVM application +├── desktopApp/ # Desktop JVM application ├── feature/ # Feature modules (KMP) │ ├── intro/ │ ├── messaging/ @@ -33,22 +33,27 @@ Meshtastic-Android/ │ ├── wifi-provision/ │ └── widget/ ├── core/ # Core infrastructure modules (KMP) +│ ├── api/ +│ ├── barcode/ +│ ├── ble/ │ ├── common/ -│ ├── navigation/ -│ ├── ui/ -│ ├── resources/ -│ ├── model/ │ ├── data/ │ ├── database/ │ ├── datastore/ -│ ├── prefs/ -│ ├── repository/ -│ ├── service/ │ ├── di/ +│ ├── domain/ +│ ├── model/ +│ ├── navigation/ │ ├── network/ -│ ├── ble/ +│ ├── nfc/ +│ ├── prefs/ │ ├── proto/ -│ └── testing/ +│ ├── repository/ +│ ├── resources/ +│ ├── service/ +│ ├── takserver/ +│ ├── testing/ +│ └── ui/ ├── build-logic/ # Convention plugins and build helpers │ └── convention/ ├── docs/ # Documentation source (markdown) @@ -115,7 +120,7 @@ Located in `build-logic/convention/src/main/kotlin/org/meshtastic/buildlogic/`: ./gradlew assembleGoogleDebug assembleFdroidDebug # Desktop run -./gradlew :desktop:run +./gradlew :desktopApp:run ``` ## Version Catalog Highlights diff --git a/docs/developer/contributing.md b/docs/developer/contributing.md index 7783fac1a..f5d0bbaf6 100644 --- a/docs/developer/contributing.md +++ b/docs/developer/contributing.md @@ -14,15 +14,26 @@ Guidelines for contributing to the Meshtastic Android/Desktop/iOS project. ## Branch Naming -Feature branches follow the pattern: -``` -{issue-number}-{short-description} -``` +Branches use conventional-commit style prefixes: + +| Prefix | Use for | +|--------|---------| +| `feat/` | New user-visible behavior | +| `fix/` | Bug fixes | +| `refactor/` | Code structure changes | +| `chore/` | Tooling, deps, CI, cleanup | +| `docs/` | Documentation only | +| `build/` | Build system changes | +| `ci/` | CI workflow changes | +| `test/` | Test additions or fixes | +| `deps/` | Dependency updates | + +Numeric spec prefixes (e.g., `003-app-docs-markdown`) are also valid for spec-driven work. Examples: -- `003-app-docs-markdown` -- `001-local-mesh-discovery` +- `feat/desktop-ble-transport` - `fix/bluetooth-reconnect` +- `003-app-docs-markdown` ## Development Workflow diff --git a/docs/developer/navigation-and-deep-links.md b/docs/developer/navigation-and-deep-links.md index 08622c60a..c86afdf85 100644 --- a/docs/developer/navigation-and-deep-links.md +++ b/docs/developer/navigation-and-deep-links.md @@ -24,7 +24,7 @@ interface Graph : Route // Graph roots for navigation hierarchies @Serializable sealed interface SettingsRoute : Route { - @Serializable data class SettingsGraph(val destNum: Int?) : SettingsRoute, Graph + @Serializable data class Settings(val destNum: Int? = null) : SettingsRoute, Graph @Serializable data object DeviceConfiguration : SettingsRoute @Serializable data object HelpDocs : SettingsRoute @Serializable data class HelpDocPage(val pageId: String) : SettingsRoute @@ -54,7 +54,7 @@ meshtastic://meshtastic/{path} | URI Path | Route | Notes | |----------|-------|-------| -| `/settings` | `SettingsRoute.SettingsGraph(null)` | Settings root | +| `/settings` | `SettingsRoute.Settings(null)` | Settings root | | `/settings/helpDocs` | `SettingsRoute.HelpDocs` | Docs browser | | `/settings/helpDocs/{pageId}` | `SettingsRoute.HelpDocPage(pageId)` | Specific doc page | | `/settings/help-docs` | `SettingsRoute.HelpDocs` | Compatibility alias | @@ -70,7 +70,7 @@ Deep links synthesize a full backstack, not just the target screen: ```kotlin // /settings/helpDocs/messages-and-channels produces: listOf( - SettingsRoute.SettingsGraph(null), + SettingsRoute.Settings(null), SettingsRoute.HelpDocs, SettingsRoute.HelpDocPage("messages-and-channels"), ) @@ -90,9 +90,9 @@ This ensures the user can navigate "up" correctly. Each feature module provides entries via an extension function: ```kotlin -fun EntryProviderScope<*>.docsEntries(backStack: NavBackStack) { +fun EntryProviderScope.docsEntries(backStack: NavBackStack) { entry { DocsBrowserScreen(backStack) } - entry { DocsPageRouteScreen(it.pageId, backStack) } + entry { route -> DocsPageRouteScreen(route.pageId, backStack) } } ``` @@ -105,17 +105,5 @@ Deep link routing is tested in: core/navigation/src/commonTest/kotlin/org/meshtastic/core/navigation/DeepLinkRouterTest.kt ``` -Example: -```kotlin -@Test -fun `help docs deep link routes correctly`() { - val result = DeepLinkRouter.route(CommonUri.parse("meshtastic://meshtastic/settings/helpDocs")) - assertEquals( - listOf(SettingsRoute.SettingsGraph(null), SettingsRoute.HelpDocs), - result, - ) -} -``` - --- diff --git a/docs/developer/persistence.md b/docs/developer/persistence.md index 9b42538e9..4f1f1f472 100644 --- a/docs/developer/persistence.md +++ b/docs/developer/persistence.md @@ -35,20 +35,30 @@ The primary structured data store: | Entity | Description | |--------|-------------| -| Nodes | All known mesh nodes and metadata | -| Messages | Message history (channel and direct) | -| Waypoints | Shared geographic points | -| Telemetry | Device, environment, power metrics | -| Channels | Channel configurations | +| `NodeEntity` | All known mesh nodes and their metadata | +| `MyNodeEntity` | The local node's own info | +| `Packet` | Message history (channel and direct), waypoints, and telemetry data | +| `ContactSettings` | Per-contact mute and read-state | +| `ReactionEntity` | Emoji reactions on messages | +| `MeshLog` | Raw mesh protocol logs | +| `MetadataEntity` | Device metadata (firmware version, hardware model) | +| `QuickChatAction` | User-configured quick-chat messages | +| `DeviceHardwareEntity` | Cached device hardware catalog | +| `FirmwareReleaseEntity` | Cached firmware release info | +| `TracerouteNodePositionEntity` | Traceroute hop position data | + +> 💡 **Note:** Waypoints, telemetry, and channel data are stored within the `Packet` entity (using the `port_num` field to distinguish packet types) rather than in separate tables. ## DataStore Preferences **Module:** `core:datastore` For lightweight key-value preferences: -- Connection state -- Last connected device -- UI preferences +- Local radio configuration (LocalConfig proto) +- Module configuration (ModuleConfig proto) +- Channel set data +- Local statistics +- Recently connected device addresses ## Core Prefs diff --git a/docs/developer/testing.md b/docs/developer/testing.md index 816ab6527..d1ed344fc 100644 --- a/docs/developer/testing.md +++ b/docs/developer/testing.md @@ -55,12 +55,12 @@ Located in `commonTest` or `jvmTest` source sets. ### Screenshot Tests -Preferred: **Roborazzi** (Gradle-native, Ubuntu CI compatible) -Fallback: **Paparazzi** (Android-view-centric) +Uses Android Gradle Plugin's native screenshot testing framework: ```bash -./gradlew recordDocsScreenshots # Record golden images -./gradlew verifyDocsScreenshots # Compare against goldens +./gradlew :screenshot-tests:updateDebugScreenshotTest # Record golden images +./gradlew :screenshot-tests:validateDebugScreenshotTest # Compare against goldens +./gradlew :screenshot-tests:copyDocsScreenshots # Copy reference images to docs pipeline ``` ## Test Organization diff --git a/docs/developer/transport.md b/docs/developer/transport.md index 15467f0ba..82faaa269 100644 --- a/docs/developer/transport.md +++ b/docs/developer/transport.md @@ -24,9 +24,9 @@ App ← RadioController → Transport (BLE | Serial | TCP) ## Bluetooth Low Energy (BLE) **Module:** `core:ble` -**Platforms:** Android, (planned: iOS) +**Platforms:** Android, Desktop (JVM via Kable), iOS (planned) -The primary transport for Android mobile devices: +The primary transport for mobile devices and also available on desktop: - Service discovery for Meshtastic GATT services - Characteristic-based read/write for protobuf packets - Connection state management and automatic reconnection @@ -35,7 +35,7 @@ The primary transport for Android mobile devices: ### Key Classes - `core/ble/` — BLE scanning, connection, and GATT operations -- Platform-specific implementations in `androidMain` +- Platform-specific implementations in `androidMain` and `jvmMain` (Kable) ## USB Serial @@ -51,7 +51,7 @@ Serial communication over USB: ### Key Classes - Serial prober and transport factory in `core/network` -- Desktop-specific serial in `desktop/src/main/kotlin/.../radio/` +- Desktop-specific serial in `desktopApp/src/main/kotlin/.../radio/` ## TCP/IP @@ -70,13 +70,17 @@ The `RadioTransportFactory` interface abstracts transport creation: ```kotlin interface RadioTransportFactory { - fun createTransport(config: TransportConfig): RadioTransport + val supportedDeviceTypes: List + fun createTransport(address: String, service: RadioInterfaceService): RadioTransport + fun isMockTransport(): Boolean + fun isAddressValid(address: String?): Boolean + fun toInterfaceAddress(interfaceId: InterfaceId, rest: String): String } ``` Platform-specific implementations: - **Android:** Supports BLE + USB + TCP -- **Desktop:** Supports USB + TCP (no BLE) +- **Desktop:** Supports BLE (Kable) + USB + TCP - **iOS:** Planned BLE + TCP ## Connection Lifecycle diff --git a/docs/translations.md b/docs/translations.md index 347d70d00..feda7aaa5 100644 --- a/docs/translations.md +++ b/docs/translations.md @@ -10,14 +10,17 @@ This documentation is translated by the community via [Crowdin](https://crowdin. ## Available Languages +{% assign any_locale_exists = false %} {% for locale in site.data.locales %} {% assign locale_code = locale[0] %} {% assign locale_info = locale[1] %} -{% assign locale_index = locale_code | append: "/index.md" %} +{% assign locale_prefix = locale_code | append: "/" %} {% assign has_content = false %} {% for p in site.pages %} - {% if p.path contains locale_code %} + {% assign page_path_check = p.path | slice: 0, locale_prefix.size %} + {% if page_path_check == locale_prefix %} {% assign has_content = true %} + {% assign any_locale_exists = true %} {% break %} {% endif %} {% endfor %} @@ -27,19 +30,6 @@ This documentation is translated by the community via [Crowdin](https://crowdin. {% endif %} {% endfor %} -{% comment %} Show notice if no translations exist yet {% endcomment %} -{% assign any_locale_exists = false %} -{% for locale in site.data.locales %} - {% assign locale_code = locale[0] %} - {% for p in site.pages %} - {% if p.path contains locale_code %} - {% assign any_locale_exists = true %} - {% break %} - {% endif %} - {% endfor %} - {% if any_locale_exists %}{% break %}{% endif %} -{% endfor %} - {% unless any_locale_exists %} > No translations available yet. Want to help? [Join our Crowdin project →](https://crowdin.com/project/meshtastic-android) {% endunless %} diff --git a/docs/user.md b/docs/user.md index 72fcfbf5b..1b88fac94 100644 --- a/docs/user.md +++ b/docs/user.md @@ -3,7 +3,6 @@ title: User Guide layout: default nav_order: 1 has_children: true -parent: "" --- # User Guide diff --git a/docs/user/connections.md b/docs/user/connections.md index 574433b50..0f69adb81 100644 --- a/docs/user/connections.md +++ b/docs/user/connections.md @@ -80,25 +80,36 @@ Some Meshtastic radios support WiFi connectivity, allowing TCP-based connections 3. Enter the radio's IP address and port (default: 4403). 4. Tap **Connect**. +![WiFi scanning for devices](/assets/screenshots/connections_wifi_scanning.png) + +When a device is found, it appears in the connection list: + +![WiFi device found](/assets/screenshots/connections_wifi_device_found.png) + +A successful connection is confirmed with a status indicator: + +![WiFi connection success](/assets/screenshots/connections_wifi_success.png) + ### When to Use TCP - Radio is on the same local network - Testing with a simulated radio - Environments where Bluetooth has interference issues -## Connection Priority +## Reconnection Behavior -The app attempts connections in this order: -1. Last successful Bluetooth device -2. USB (if detected) -3. Manual TCP (if configured) +The app reconnects to the **last selected device** on startup. You can manually switch transports from the connections screen at any time. + +To disconnect from a radio, use the disconnect button on the connections screen: + +![Disconnect from radio](/assets/screenshots/connections_disconnect.png) ## Desktop Connections On Desktop (Linux/macOS/Windows), the app supports: -- **USB Serial** — primary connection method +- **Bluetooth (BLE)** — via the Kable library; works on macOS, Linux, and Windows +- **USB Serial** — primary wired connection method - **TCP/IP** — for network-connected radios -- Bluetooth is **not** currently supported on Desktop See [Desktop App](desktop) for platform-specific details and keyboard shortcuts. diff --git a/docs/user/desktop.md b/docs/user/desktop.md index 2734b9ab3..8921fcb3f 100644 --- a/docs/user/desktop.md +++ b/docs/user/desktop.md @@ -24,7 +24,7 @@ The Desktop app shares its core codebase with the Android app through Kotlin Mul ### Linux - Download the `.deb` or `.AppImage` package from the releases page -- Or build from source using `./gradlew :desktop:run` +- Or build from source using `./gradlew :desktopApp:run` ### macOS @@ -53,9 +53,13 @@ For network-connected radios: 1. Enter the radio's IP address and port (default: 4403). 2. Click **Connect**. -### Bluetooth +### Bluetooth (BLE) -> ⚠️ **Note:** Bluetooth is not currently supported on the Desktop app. Use USB or TCP connections. +Bluetooth Low Energy is supported on Desktop via the [Kable](https://github.com/JuulLabs/kable) library: + +1. Ensure your system has a Bluetooth adapter. +2. The app scans for nearby Meshtastic radios automatically. +3. Select your device from the connections screen. ## Feature Parity @@ -65,7 +69,7 @@ For network-connected radios: | Node List | ✓ | ✓ | Full parity | | Map | ✓ | ✓ | Full parity | | Settings | ✓ | ✓ | Full parity | -| Bluetooth | ✓ | ✗ | USB/TCP on desktop | +| Bluetooth (BLE) | ✓ | ✓ | Via Kable on desktop | | Firmware Update OTA | ✓ | ✗ | Use web flasher | | Notifications | ✓ | ✓ | Native OS notifications | | Widgets | ✓ | ✗ | Android-only | @@ -99,13 +103,27 @@ The Desktop app uses the same Compose Multiplatform UI with adaptations for larg The Desktop app provides in-app toggles for controlling which notifications are shown — messages, new nodes, and low battery alerts. Access these from **Settings → Notifications** within the app. +## Built-in Documentation Browser + +The Desktop app includes a built-in documentation browser for quick access to help content without leaving the application. + +![Docs browser with table of contents](/assets/screenshots/docs-browser_toc.png) + +The browser supports full-text search across all documentation: + +![Searching the docs browser](/assets/screenshots/docs-browser_search.png) + +Individual doc pages render with full formatting: + +![A documentation page](/assets/screenshots/docs-browser_page.png) + ## Building from Source ```bash git clone https://github.com/meshtastic/Meshtastic-Android.git cd Meshtastic-Android git submodule update --init -./gradlew :desktop:run +./gradlew :desktopApp:run ``` Requirements: @@ -114,10 +132,10 @@ Requirements: ## Known Limitations -- No Bluetooth support - No OTA firmware updates (use web flasher) - Some Android-specific features (widgets, specific notification channels) are unavailable - Performance may vary on low-spec hardware running Compose Desktop +- BLE bonding is not yet supported on desktop (pairing works without bonding) ## Related Topics diff --git a/docs/user/discovery.md b/docs/user/discovery.md index 55c5d8d34..7a0fba364 100644 --- a/docs/user/discovery.md +++ b/docs/user/discovery.md @@ -96,7 +96,7 @@ The node list itself is a powerful discovery tool when you use its filtering and ### Infrastructure Audit -- Disable **Exclude infrastructure** to see Router, Repeater, and Router Client nodes. +- Disable **Exclude infrastructure** to see Router, Repeater, Router Late, and Client Base nodes. - Check their signal quality and last-heard times to verify your infrastructure nodes are healthy. See [Nodes](nodes) for full details on filtering and sorting options. diff --git a/docs/user/firmware.md b/docs/user/firmware.md index 05fc04169..0b0697ca4 100644 --- a/docs/user/firmware.md +++ b/docs/user/firmware.md @@ -78,6 +78,8 @@ If the update appears frozen: - If truly stuck, power-cycle the radio - Attempt the update again +![Firmware update error](/assets/screenshots/firmware_error.png) + ### Device Won't Boot After Update If your device fails to boot: diff --git a/docs/user/messages-and-channels.md b/docs/user/messages-and-channels.md index 80a7195e5..0f8ed3175 100644 --- a/docs/user/messages-and-channels.md +++ b/docs/user/messages-and-channels.md @@ -42,6 +42,8 @@ Channels support multiple encryption levels: 3. Configure the channel name and encryption key. 4. Share the channel URL/QR code with others who need access. +Tapping a channel shows its details and sharing options. + ## Direct Messages Direct messages (DMs) are point-to-point encrypted communications between two specific nodes. @@ -57,8 +59,11 @@ Direct messages (DMs) are point-to-point encrypted communications between two sp | State | Icon | Meaning | |-------|------|---------| | Queued | ⏳ | Message waiting to be sent | -| Sent | ✓ | Message transmitted to mesh | +| En route | ✓ | Delivered to the radio, awaiting acknowledgment | | Delivered | ✓✓ | Acknowledgment received from recipient | +| Received | ✓ | Message received from the mesh (incoming) | +| S&F Routing | 🔗 | Store & Forward: message being routed through an S&F node | +| S&F Confirmed | 🔗 | Store & Forward: delivery confirmed via S&F node | | Error | ✗ | Delivery failed after retries | ### Delivery Errors @@ -92,9 +97,7 @@ Pre-configured messages for rapid communication: ![Quick chat option](/assets/screenshots/messages_quick_chat.png) -The channel list shows each channel with its latest message preview: - -![Channel list item showing channel name and last message](/assets/screenshots/messages-and-channels_channel_list.png) +The channel list shows each channel with its latest message preview. ### Message Bubbles diff --git a/docs/user/mqtt.md b/docs/user/mqtt.md index f79bffc3e..94c4c9db1 100644 --- a/docs/user/mqtt.md +++ b/docs/user/mqtt.md @@ -2,7 +2,7 @@ title: MQTT nav_order: 11 last_updated: 2026-05-13 -description: Bridge your mesh to the internet — MQTT broker setup, encryption layers, JSON output, and map reporting. +description: Bridge your mesh to the internet — MQTT broker setup, encryption layers, and map reporting. aliases: - mqtt - internet-bridge @@ -46,7 +46,7 @@ A gateway node with internet access (WiFi or Ethernet) publishes mesh messages t | Password | Broker authentication | large4cats | | Root Topic | Base topic for messages | msh | | Encryption | Encrypt MQTT payload | Enabled | -| JSON Output | Publish JSON alongside protobuf | Disabled | +| ~~JSON Output~~ | ⚠️ **Deprecated** — JSON packet support has been removed from firmware; this field is ignored | Disabled | | TLS | Secure connection to broker | Disabled | | Map Reporting | Report position to public map | Disabled | @@ -83,14 +83,13 @@ Configure per-channel which directions are active to control message flow and ai ## Message Formats -MQTT supports two message formats: +MQTT uses protobuf message format: | Format | Description | Use case | |--------|-------------|----------| -| **Protobuf** (default) | Binary Meshtastic protobuf encoding | Node-to-node mesh bridging | -| **JSON** | Human-readable JSON encoding | Home automation, logging, custom integrations | +| **Protobuf** | Binary Meshtastic protobuf encoding | Node-to-node mesh bridging | -When **JSON Output** is enabled, the gateway publishes both protobuf and JSON versions of each message to separate topics. +> ⚠️ **Note:** JSON output support was removed from firmware. The `json_enabled` setting is still visible in the app for legacy compatibility but has no effect on current firmware versions. ## Encryption & Privacy diff --git a/docs/user/nodes.md b/docs/user/nodes.md index b66a94b6e..ec5f2ebd9 100644 --- a/docs/user/nodes.md +++ b/docs/user/nodes.md @@ -39,11 +39,13 @@ Nodes can be configured with different roles that affect their mesh behavior: | Role | Description | |------|-------------| | Client | Standard end-user device | +| Client Base | Treats favorited-node traffic as Router Late priority; all other traffic as Client | | Client Mute | Receives but doesn't retransmit | | Client Hidden | Like Client Mute, plus hides from node list | | Router | Prioritizes message forwarding; stays awake to relay | -| Router Client | Routes and operates as a client | -| Repeater | Retransmits only; no user interface | +| Router Late | Infrastructure node that rebroadcasts once, but only after all other modes (provides supplemental coverage) | +| ~~Router Client~~ | ⚠️ **Deprecated** (removed in firmware 2.3.15) — no longer selectable; use Router or Client instead | +| ~~Repeater~~ | ⚠️ **Deprecated** (removed in firmware 2.7.11) — no longer selectable; use Router instead | | Tracker | Optimized for position reporting at regular intervals | | Sensor | Optimized for telemetry reporting | | TAK | Interoperates with TAK systems (sends/receives CoT) | @@ -55,9 +57,9 @@ Nodes can be configured with different roles that affect their mesh behavior: Most users should keep the default **Client** role. Consider a different role when: - **Router** — You have a node in a fixed, elevated location with reliable power (rooftop, hilltop). Routers stay awake continuously to relay messages for others and are essential for extending mesh coverage. Don't use Router on battery-powered handheld devices. -- **Router Client** — Like Router, but the device is also used as a personal client. Good for a home base station that you also send messages from. +- **Router Late** — An infrastructure node that always rebroadcasts packets once but only after all other routing modes have had their turn. Provides supplemental coverage for local clusters without competing with primary routers. +- **Client Base** — Treats traffic from/to your favorited nodes with Router Late priority (ensuring those messages get extra relay coverage) while handling everything else as a normal Client. - **Client Mute** — You want to receive mesh traffic but not contribute to relaying. Useful for monitoring-only devices or to reduce congestion in dense areas. -- **Repeater** — A dedicated relay node with no screen or user interaction. Optimized purely for forwarding; lowest power consumption of the relay roles. - **Tracker** — An unattended device whose sole purpose is broadcasting its GPS position (e.g., a vehicle, pet, or asset). Sleeps between broadcasts to conserve battery. - **Sensor** — An unattended device reporting environmental telemetry (temperature, humidity, air quality). Similar power profile to Tracker. - **TAK / TAK Tracker** — Only needed if interoperating with ATAK/WinTAK systems. See [TAK Integration](tak) for details. @@ -100,7 +102,7 @@ Type in the search field to filter nodes by name or short name. The filter updat | **Only online** | Show only nodes heard within the last 15 minutes | | **Only direct** | Show only nodes with direct (non-relayed) connections | | **Include unknown** | Show nodes that haven't sent user info yet | -| **Exclude infrastructure** | Hide infrastructure-role nodes (Router, Repeater, Router Client) | +| **Exclude infrastructure** | Hide infrastructure-role nodes (Router, Repeater, Router Late, Client Base) | | **Exclude MQTT** | Hide nodes heard only via MQTT internet bridge | | **Show ignored** | Show nodes you've previously dismissed or muted | diff --git a/docs/user/settings-module-admin.md b/docs/user/settings-module-admin.md index c866ef08f..5cd31e7a7 100644 --- a/docs/user/settings-module-admin.md +++ b/docs/user/settings-module-admin.md @@ -21,6 +21,10 @@ Module settings use a card-based layout with toggle switches, dropdowns, text fi ![Dropdown selector](/assets/screenshots/settings_dropdown.png) +![Text field](/assets/screenshots/settings_text_field.png) + +![Settings card layout](/assets/screenshots/settings_titled_card.png) + ## Module Configuration ### MQTT Module @@ -34,7 +38,7 @@ Bridges mesh messages to and from an MQTT broker for internet connectivity. This | Username | Authentication username | | Password | Authentication password | | Encryption | Encrypt MQTT payloads | -| JSON Output | Also publish in JSON format | +| ~~JSON Output~~ | ⚠️ **Deprecated** — JSON support removed from firmware; field is ignored | | TLS | Use secure connection | | Root Topic | Base MQTT topic path | | Map Report | Publish position for public map | @@ -112,7 +116,7 @@ Pre-configured messages accessible from the device's physical buttons (for radio | Setting | Description | |---------|-------------| -| Enabled | Activate canned messages | +| ~~Enabled~~ | ⚠️ **Deprecated** — current firmware may ignore this toggle | | Messages | Newline-separated list of messages | | Send Bell | Play bell sound on send | | Rotary Encoder | Enable rotary encoder input | diff --git a/docs/user/settings-radio-user.md b/docs/user/settings-radio-user.md index 7bed4a4d8..f2bd9e2d8 100644 --- a/docs/user/settings-radio-user.md +++ b/docs/user/settings-radio-user.md @@ -28,8 +28,6 @@ Configure your radio hardware and user identity parameters. After modifying settings, tap **Save** to write the configuration to your radio. The device may reboot to apply changes. -![Settings appearance section](/assets/screenshots/settings-radio-user_lora_config.png) - ## Radio Configuration ### Device Config @@ -37,8 +35,6 @@ After modifying settings, tap **Save** to write the configuration to your radio. | Setting | Description | Default | |---------|-------------|---------| | Role | Node behavior (Client, Router, etc.) | Client | -| Serial Output | Enable serial console output | Disabled | -| Debug Log | Enable verbose debug logging | Disabled | | Rebroadcast Mode | How the node retransmits messages | All | | Node Info Broadcast (s) | Interval for broadcasting node info | 10800 | | Double-tap Button | Action for double-tap button press | Disabled | @@ -62,11 +58,18 @@ After modifying settings, tap **Save** to write the configuration to your radio. |--------|-------|-------|-----------|----------| | Short Turbo | ~1 km | 21.9 kbps | −5 dB | Dense urban with line-of-sight; data-heavy applications | | Short Fast | ~3 km | 10.9 kbps | −7.5 dB | Urban neighborhoods; buildings within a few blocks | +| Short Slow | ~5 km | 5.5 kbps | −10 dB | Suburban short-range; moderate building density | | Medium Fast | ~5 km | 5.5 kbps | −10 dB | Suburban areas; moderate building density | +| Medium Slow | ~8 km | 1.1 kbps | −12.5 dB | Suburban/rural; moderate range with slower speed | +| Long Turbo | ~10 km | 4.4 kbps | −10 dB | Similar range to Long Fast but with 500 kHz bandwidth; faster throughput | | Long Fast | ~10 km | 1.1 kbps | −12.5 dB | **General use (default)** — balanced range and speed | | Long Moderate | ~20 km | 0.34 kbps | −15 dB | Rural with some terrain; occasional use | -| Long Slow | ~30 km | 0.18 kbps | −17.5 dB | Sparse rural; maximum reliable range | -| Very Long Slow | ~40+ km | 0.09 kbps | −20 dB | Extreme range experiments; very slow throughput | +| Lite Fast | ~5 km | 5.5 kbps | −10 dB | EU 866 MHz SRD band (125 kHz BW); comparable to Medium Fast | +| Lite Slow | ~10 km | 1.1 kbps | −12.5 dB | EU 866 MHz SRD band (125 kHz BW); comparable to Long Fast | +| Narrow Fast | ~5 km | 2.7 kbps | −10 dB | EU 868 MHz band (62.5 kHz BW); avoids interference with other devices | +| Narrow Slow | ~10 km | 1.1 kbps | −12.5 dB | EU 868 MHz band (62.5 kHz BW); comparable to Long Fast | +| ~~Long Slow~~ | ~30 km | 0.18 kbps | −17.5 dB | ⚠️ **Deprecated** — still selectable but may be removed in a future firmware release | +| ~~Very Long Slow~~ | ~40+ km | 0.09 kbps | −20 dB | ⚠️ **Deprecated** — still selectable but may be removed in a future firmware release | #### Choosing a Modem Preset @@ -78,8 +81,9 @@ The modem preset controls the fundamental tradeoff between **range** and **data **Practical guidance:** - **Urban mesh (many nodes, short distances):** Use **Long Fast** (default) or **Short Fast**. Higher speed means less airtime congestion when many nodes share the channel. -- **Rural/sparse mesh (few nodes, long distances):** Use **Long Moderate** or **Long Slow**. Range matters more than speed when nodes are far apart. -- **Fixed infrastructure links:** Use **Short Turbo** for dedicated point-to-point links with good antennas and line-of-sight. +- **Rural/sparse mesh (few nodes, long distances):** Use **Long Moderate**. Range matters more than speed when nodes are far apart. +- **EU 866/868 MHz regulatory compliance:** Use **Lite Fast**, **Lite Slow**, **Narrow Fast**, or **Narrow Slow** — these are optimized for the EU SRD/868 MHz bands with narrower bandwidths. +- **Fixed infrastructure links:** Use **Short Turbo** or **Long Turbo** for dedicated point-to-point links with good antennas and line-of-sight. - **Mixed environments:** Stick with **Long Fast** — it's the community default and ensures compatibility with others in your area. > ⚠️ **Important:** All nodes on the same channel **must** use the same modem preset. Nodes with mismatched presets cannot communicate even if they share the same frequency and encryption key. @@ -92,9 +96,9 @@ The modem preset controls the fundamental tradeoff between **range** and **data |---------|-------------| | Screen Timeout | Time before display sleeps | | Display Units | Metric or Imperial | -| GPS Format | DMS, Decimal, UTM, MGRS, OLC | | OLED Type | Auto, SSD1306, SH1106, SH1107 | -| Compass North | True North or Magnetic North | +| Compass Orientation | Rotation offset for compass display (0°, 90°, 180°, 270°) | +| ~~Compass North~~ | ⚠️ **Deprecated** — replaced by Compass Orientation; still visible in older firmware | ### Position Config @@ -126,6 +130,8 @@ The modem preset controls the fundamental tradeoff between **range** and **data | NTP Server | Time synchronization server | | Syslog Server | Remote logging server | +![IP address field](/assets/screenshots/settings_ipv4_field.png) + ### Bluetooth Config | Setting | Description | @@ -142,8 +148,12 @@ The modem preset controls the fundamental tradeoff between **range** and **data | Admin Key | Key for remote administration | | Private Key | Your node's private key (handle securely) | | Admin Channel Enabled | Allow admin commands via channel | +| Debug Log | Output live debug logging over serial/bluetooth | +| Serial Enabled | Enable serial console access (moved from Device Config) | | Managed Mode | Restrict non-admin channel changes | +![Password field](/assets/screenshots/settings_password_field.png) + Settings use standard preference controls — dropdowns, toggles, and sliders: | Control | Screenshot | diff --git a/docs/user/signal-meter.md b/docs/user/signal-meter.md index 3f573ca4d..ce2d0a66d 100644 --- a/docs/user/signal-meter.md +++ b/docs/user/signal-meter.md @@ -52,10 +52,10 @@ Here is exactly how the app decides how many bars (or what color) to show you: | Level | Bars | Criteria | Meaning | |-------|------|----------|---------| -| Good | 3 | RSSI better than `-115 dBm` **AND** SNR above the baseline limit for your preset | Signal is both loud and clear — healthy connection. | -| Fair | 2 | Falls between Good and Bad | Signal getting quieter or noisier, but the radio understands the message fine. | -| Bad | 1 | RSSI drops to `-120 dBm` or worse, **OR** SNR within `5.5 dB` of your preset's absolute breaking point | Barely hanging on — at the edge of range or heavy interference. | -| None | 0 | RSSI worse than `-126 dBm` **AND** SNR has fallen `7.5 dB` below the ideal limit | Transmission completely buried in static. | +| Good | 3 | RSSI better than `-115 dBm` **AND** SNR better than `-7 dB` | Signal is both loud and clear — healthy connection. | +| Fair | 2 | RSSI better than `-126 dBm` with good SNR, **OR** SNR better than `-15 dB` with good RSSI | Signal getting quieter or noisier, but still decodable. | +| Bad | 1 | Falls between Fair and None thresholds | At the edge of range or experiencing interference. | +| None | 0 | RSSI worse than `-126 dBm` **AND** SNR worse than `-15 dB` | Transmission completely buried in noise. | ---