diff --git a/core/domain/src/commonTest/kotlin/org/meshtastic/core/domain/usecase/settings/ProcessRadioResponseUseCaseTest.kt b/core/domain/src/commonTest/kotlin/org/meshtastic/core/domain/usecase/settings/ProcessRadioResponseUseCaseTest.kt
index 4bc54ac08..b8fcf6a20 100644
--- a/core/domain/src/commonTest/kotlin/org/meshtastic/core/domain/usecase/settings/ProcessRadioResponseUseCaseTest.kt
+++ b/core/domain/src/commonTest/kotlin/org/meshtastic/core/domain/usecase/settings/ProcessRadioResponseUseCaseTest.kt
@@ -77,7 +77,7 @@ class ProcessRadioResponseUseCaseTest {
// Assert
assertTrue(result is RadioResponseResult.Metadata)
- assertEquals("2.5.0", (result as RadioResponseResult.Metadata).metadata.firmware_version)
+ assertEquals("2.5.0", result.metadata.firmware_version)
}
@Test
@@ -99,7 +99,7 @@ class ProcessRadioResponseUseCaseTest {
// Assert
assertTrue(result is RadioResponseResult.CannedMessages)
- assertEquals("Hello World", (result as RadioResponseResult.CannedMessages).messages)
+ assertEquals("Hello World", result.messages)
}
@Test
@@ -133,7 +133,7 @@ class ProcessRadioResponseUseCaseTest {
)
val result = useCase(packet, 123, setOf(42))
assertTrue(result is RadioResponseResult.Owner)
- assertEquals("Owner", (result as RadioResponseResult.Owner).user.long_name)
+ assertEquals("Owner", result.user.long_name)
}
@Test
@@ -186,7 +186,7 @@ class ProcessRadioResponseUseCaseTest {
)
val result = useCase(packet, 123, setOf(42))
assertTrue(result is RadioResponseResult.ChannelResponse)
- assertEquals("Main", (result as RadioResponseResult.ChannelResponse).channel.settings?.name)
+ assertEquals("Main", result.channel.settings?.name)
}
private fun ByteArray.toByteString() = okio.ByteString.of(*this)
diff --git a/core/resources/src/commonMain/composeResources/drawable/img_mpwrd_logo.png b/core/resources/src/commonMain/composeResources/drawable/img_mpwrd_logo.png
new file mode 100644
index 000000000..224c5add3
Binary files /dev/null and b/core/resources/src/commonMain/composeResources/drawable/img_mpwrd_logo.png differ
diff --git a/core/resources/src/commonMain/composeResources/values/strings.xml b/core/resources/src/commonMain/composeResources/values/strings.xml
index 692ddcb37..9b8c6d7aa 100644
--- a/core/resources/src/commonMain/composeResources/values/strings.xml
+++ b/core/resources/src/commonMain/composeResources/values/strings.xml
@@ -940,7 +940,7 @@
PAX Metrics
PAX
No PAX metrics available.
- WiFi Devices
+ Wi-Fi Provisioning for mPWRD-OS
Bluetooth Devices
Paired devices
Connected Device
@@ -1327,8 +1327,9 @@
Connect
Done
- WiFi Provisioning
- Provision WiFi credentials to your Meshtastic device via Bluetooth.
+ Wi-Fi Provisioning for mPWRD-OS
+ Provision Wi-Fi credentials to your mPWRD-OS device via Bluetooth.
+ Learn more about the mPWRD-OS project\nhttps://github.com/mPWRD-OS
Searching for device…
Device found
Ready to scan for WiFi networks.
diff --git a/core/takserver/src/commonTest/kotlin/org/meshtastic/core/takserver/CoTXmlTest.kt b/core/takserver/src/commonTest/kotlin/org/meshtastic/core/takserver/CoTXmlTest.kt
index 2c7669319..7b6aa0ecd 100644
--- a/core/takserver/src/commonTest/kotlin/org/meshtastic/core/takserver/CoTXmlTest.kt
+++ b/core/takserver/src/commonTest/kotlin/org/meshtastic/core/takserver/CoTXmlTest.kt
@@ -81,8 +81,8 @@ class CoTXmlTest {
assertEquals("b-t-f", roundTripped.type)
assertNotNull(roundTripped.chat)
- assertEquals("Hello World", roundTripped.chat?.message)
- assertEquals("Alice", roundTripped.chat?.senderCallsign)
+ assertEquals("Hello World", roundTripped.chat.message)
+ assertEquals("Alice", roundTripped.chat.senderCallsign)
}
// ── XML escaping ─────────────────────────────────────────────────────────
diff --git a/core/takserver/src/commonTest/kotlin/org/meshtastic/core/takserver/TAKPacketConversionTest.kt b/core/takserver/src/commonTest/kotlin/org/meshtastic/core/takserver/TAKPacketConversionTest.kt
index 9bab59c03..771f10cfe 100644
--- a/core/takserver/src/commonTest/kotlin/org/meshtastic/core/takserver/TAKPacketConversionTest.kt
+++ b/core/takserver/src/commonTest/kotlin/org/meshtastic/core/takserver/TAKPacketConversionTest.kt
@@ -92,8 +92,8 @@ class TAKPacketConversionTest {
assertEquals(85, cot.status?.battery)
assertNotNull(cot.track)
- assertEquals(5.0, cot.track?.speed)
- assertEquals(90.0, cot.track?.course)
+ assertEquals(5.0, cot.track.speed)
+ assertEquals(90.0, cot.track.course)
}
@Test
diff --git a/feature/wifi-provision/README.md b/feature/wifi-provision/README.md
index 4e61464a0..1403c6d79 100644
--- a/feature/wifi-provision/README.md
+++ b/feature/wifi-provision/README.md
@@ -23,9 +23,9 @@ classDef unknown fill:#FFADAD,stroke:#000,stroke-width:2px,color:#000;
```
-## WiFi Provisioning System
+## WiFi Provisioning System — for mPWRD-OS
-The `:feature:wifi-provision` module provides BLE-based WiFi provisioning for Meshtastic devices using the Nymea network manager protocol. It scans for provisioning-capable devices, retrieves available WiFi networks, and applies credentials — all over BLE via the Kable multiplatform library.
+The `:feature:wifi-provision` module provides BLE-based WiFi provisioning for [mPWRD-OS](https://github.com/mPWRD-OS/mPWRD-OS) devices using the Nymea network manager protocol. mPWRD-OS is a community project that combines Armbian and Meshtastic for Linux-native mesh networking hardware. This module scans for provisioning-capable devices, retrieves available WiFi networks, and applies credentials — all over BLE via the Kable multiplatform library.
### Architecture
diff --git a/feature/wifi-provision/src/commonMain/kotlin/org/meshtastic/feature/wifiprovision/ui/WifiProvisionPreviews.kt b/feature/wifi-provision/src/commonMain/kotlin/org/meshtastic/feature/wifiprovision/ui/WifiProvisionPreviews.kt
index 0bb2100aa..dc9f62f8d 100644
--- a/feature/wifi-provision/src/commonMain/kotlin/org/meshtastic/feature/wifiprovision/ui/WifiProvisionPreviews.kt
+++ b/feature/wifi-provision/src/commonMain/kotlin/org/meshtastic/feature/wifiprovision/ui/WifiProvisionPreviews.kt
@@ -346,3 +346,13 @@ private fun NetworkRowLongSsidPreview() {
}
}
}
+
+// ---------------------------------------------------------------------------
+// mPWRD-OS disclaimer banner
+// ---------------------------------------------------------------------------
+
+@PreviewLightDark
+@Composable
+private fun MpwrdDisclaimerBannerPreview() {
+ AppTheme { Surface { Column(modifier = Modifier.fillMaxWidth().padding(16.dp)) { MpwrdDisclaimerBanner() } } }
+}
diff --git a/feature/wifi-provision/src/commonMain/kotlin/org/meshtastic/feature/wifiprovision/ui/WifiProvisionScreen.kt b/feature/wifi-provision/src/commonMain/kotlin/org/meshtastic/feature/wifiprovision/ui/WifiProvisionScreen.kt
index 6f9c9dc68..ced6d212c 100644
--- a/feature/wifi-provision/src/commonMain/kotlin/org/meshtastic/feature/wifiprovision/ui/WifiProvisionScreen.kt
+++ b/feature/wifi-provision/src/commonMain/kotlin/org/meshtastic/feature/wifiprovision/ui/WifiProvisionScreen.kt
@@ -23,6 +23,7 @@ import androidx.compose.animation.expandVertically
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.shrinkVertically
+import androidx.compose.foundation.Image
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
@@ -38,6 +39,7 @@ import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.rememberScrollState
+import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.foundation.verticalScroll
@@ -77,6 +79,7 @@ import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
import androidx.compose.ui.platform.LocalHapticFeedback
import androidx.compose.ui.text.input.ImeAction
@@ -86,6 +89,7 @@ import androidx.compose.ui.text.input.VisualTransformation
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
+import org.jetbrains.compose.resources.painterResource
import org.jetbrains.compose.resources.stringResource
import org.koin.compose.viewmodel.koinViewModel
import org.meshtastic.core.resources.Res
@@ -93,6 +97,7 @@ import org.meshtastic.core.resources.apply
import org.meshtastic.core.resources.back
import org.meshtastic.core.resources.cancel
import org.meshtastic.core.resources.hide_password
+import org.meshtastic.core.resources.img_mpwrd_logo
import org.meshtastic.core.resources.password
import org.meshtastic.core.resources.show_password
import org.meshtastic.core.resources.wifi_provision_available_networks
@@ -100,6 +105,7 @@ import org.meshtastic.core.resources.wifi_provision_connect_failed
import org.meshtastic.core.resources.wifi_provision_description
import org.meshtastic.core.resources.wifi_provision_device_found
import org.meshtastic.core.resources.wifi_provision_device_found_detail
+import org.meshtastic.core.resources.wifi_provision_mpwrd_disclaimer
import org.meshtastic.core.resources.wifi_provision_no_networks
import org.meshtastic.core.resources.wifi_provision_scan_failed
import org.meshtastic.core.resources.wifi_provision_scan_networks
@@ -110,6 +116,7 @@ import org.meshtastic.core.resources.wifi_provision_signal_strength
import org.meshtastic.core.resources.wifi_provision_ssid_label
import org.meshtastic.core.resources.wifi_provision_ssid_placeholder
import org.meshtastic.core.resources.wifi_provisioning
+import org.meshtastic.core.ui.component.AutoLinkText
import org.meshtastic.feature.wifiprovision.WifiProvisionError
import org.meshtastic.feature.wifiprovision.WifiProvisionUiState
import org.meshtastic.feature.wifiprovision.WifiProvisionUiState.Phase
@@ -164,6 +171,8 @@ fun WifiProvisionScreen(
Spacer(Modifier.height(4.dp))
}
+ MpwrdDisclaimerBanner()
+
Crossfade(targetState = screenKey(uiState), label = "wifi_provision") { key ->
when (key) {
ScreenKey.ConnectingBle -> ScanningBleContent()
@@ -481,6 +490,40 @@ internal fun NetworkRow(network: WifiNetwork, isSelected: Boolean, onClick: () -
)
}
+// ---------------------------------------------------------------------------
+// mPWRD-OS disclaimer banner
+// ---------------------------------------------------------------------------
+
+private const val MPWRD_LOGO_SIZE_DP = 40
+
+/** Branded disclaimer banner shown at the top of the provisioning screen. */
+@Composable
+internal fun MpwrdDisclaimerBanner() {
+ Card(
+ modifier = Modifier.fillMaxWidth().padding(horizontal = 16.dp, vertical = 8.dp),
+ shape = MaterialTheme.shapes.medium,
+ colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.surfaceContainerHigh),
+ ) {
+ Row(
+ modifier = Modifier.padding(12.dp),
+ horizontalArrangement = Arrangement.spacedBy(12.dp),
+ verticalAlignment = Alignment.Top,
+ ) {
+ Image(
+ painter = painterResource(Res.drawable.img_mpwrd_logo),
+ contentDescription = "mPWRD-OS",
+ modifier = Modifier.size(MPWRD_LOGO_SIZE_DP.dp).clip(RoundedCornerShape(8.dp)),
+ )
+ AutoLinkText(
+ text = stringResource(Res.string.wifi_provision_mpwrd_disclaimer),
+ style = MaterialTheme.typography.bodySmall,
+ color = MaterialTheme.colorScheme.onSurfaceVariant,
+ textAlign = TextAlign.Center,
+ )
+ }
+ }
+}
+
// ---------------------------------------------------------------------------
// Shared layout wrapper for centered status screens
// ---------------------------------------------------------------------------