From 7497ef71c02e6d5d3c0ddce6d54b690c5c7bdb39 Mon Sep 17 00:00:00 2001 From: Dane Evans Date: Thu, 30 Oct 2025 16:54:07 +1100 Subject: [PATCH] feat #3436 - add current IP address' to the settings > network panel. (#3537) --- .../geeksville/mesh/service/MeshService.kt | 8 ++++ .../meshtastic/core/service/IMeshService.aidl | 3 ++ core/strings/src/main/res/values/strings.xml | 3 ++ .../settings/radio/RadioConfigViewModel.kt | 18 +++++++++ .../radio/component/NetworkConfigItemList.kt | 37 +++++++++++++++++++ 5 files changed, 69 insertions(+) diff --git a/app/src/main/java/com/geeksville/mesh/service/MeshService.kt b/app/src/main/java/com/geeksville/mesh/service/MeshService.kt index 81d64d0d7..56b093822 100644 --- a/app/src/main/java/com/geeksville/mesh/service/MeshService.kt +++ b/app/src/main/java/com/geeksville/mesh/service/MeshService.kt @@ -2296,5 +2296,13 @@ class MeshService : Service() { override fun requestNodedbReset(requestId: Int, destNum: Int) = toRemoteExceptions { packetHandler.sendToRadio(newMeshPacketTo(destNum).buildAdminPacket(id = requestId) { nodedbReset = 1 }) } + + override fun getDeviceConnectionStatus(requestId: Int, destNum: Int) = toRemoteExceptions { + packetHandler.sendToRadio( + newMeshPacketTo(destNum).buildAdminPacket(id = requestId, wantResponse = true) { + getDeviceConnectionStatusRequest = true + }, + ) + } } } diff --git a/core/service/src/main/aidl/org/meshtastic/core/service/IMeshService.aidl b/core/service/src/main/aidl/org/meshtastic/core/service/IMeshService.aidl index 13ab9cef7..754794ee5 100644 --- a/core/service/src/main/aidl/org/meshtastic/core/service/IMeshService.aidl +++ b/core/service/src/main/aidl/org/meshtastic/core/service/IMeshService.aidl @@ -170,4 +170,7 @@ interface IMeshService { /// Send request for node UserInfo void requestUserInfo(in int destNum); + + /// Request device connection status from the radio + void getDeviceConnectionStatus(in int requestId, in int destNum); } diff --git a/core/strings/src/main/res/values/strings.xml b/core/strings/src/main/res/values/strings.xml index c6be10f50..421442047 100644 --- a/core/strings/src/main/res/values/strings.xml +++ b/core/strings/src/main/res/values/strings.xml @@ -207,6 +207,9 @@ Port: Connected Connected to radio (%s) + Current connections: + Wifi IP: + Ethernet IP: Not connected Connected to radio, but it is sleeping Application update required diff --git a/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/RadioConfigViewModel.kt b/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/RadioConfigViewModel.kt index 946e7cddf..591338ea8 100644 --- a/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/RadioConfigViewModel.kt +++ b/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/RadioConfigViewModel.kt @@ -71,6 +71,7 @@ import org.meshtastic.proto.ChannelProtos import org.meshtastic.proto.ClientOnlyProtos.DeviceProfile import org.meshtastic.proto.ConfigProtos import org.meshtastic.proto.ConfigProtos.Config.SecurityConfig +import org.meshtastic.proto.ConnStatusProtos import org.meshtastic.proto.MeshProtos import org.meshtastic.proto.ModuleConfigProtos import org.meshtastic.proto.Portnums @@ -93,6 +94,7 @@ data class RadioConfigState( val moduleConfig: ModuleConfigProtos.ModuleConfig = moduleConfig {}, val ringtone: String = "", val cannedMessageMessages: String = "", + val deviceConnectionStatus: ConnStatusProtos.DeviceConnectionStatus? = null, val responseState: ResponseState = ResponseState.Empty, val analyticsAvailable: Boolean = true, val analyticsEnabled: Boolean = false, @@ -329,6 +331,12 @@ constructor( "Request getCannedMessages error", ) + private fun getDeviceConnectionStatus(destNum: Int) = request( + destNum, + { service, packetId, dest -> service.getDeviceConnectionStatus(packetId, dest) }, + "Request getDeviceConnectionStatus error", + ) + private fun requestShutdown(destNum: Int) = request( destNum, { service, packetId, dest -> service.requestShutdown(packetId, dest) }, @@ -542,6 +550,9 @@ constructor( if (route == ConfigRoute.LORA) { getChannel(destNum, 0) } + if (route == ConfigRoute.NETWORK) { + getDeviceConnectionStatus(destNum) + } getConfig(destNum, route.type) } @@ -696,6 +707,13 @@ constructor( incrementCompleted() } + AdminProtos.AdminMessage.PayloadVariantCase.GET_DEVICE_CONNECTION_STATUS_RESPONSE -> { + _radioConfigState.update { + it.copy(deviceConnectionStatus = parsed.getDeviceConnectionStatusResponse) + } + incrementCompleted() + } + else -> Timber.d("No custom processing needed for ${parsed.payloadVariantCase}") } diff --git a/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/NetworkConfigItemList.kt b/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/NetworkConfigItemList.kt index 19f57e50a..274f280ef 100644 --- a/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/NetworkConfigItemList.kt +++ b/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/NetworkConfigItemList.kt @@ -47,6 +47,7 @@ import org.meshtastic.core.ui.component.DropDownPreference import org.meshtastic.core.ui.component.EditIPv4Preference import org.meshtastic.core.ui.component.EditPasswordPreference import org.meshtastic.core.ui.component.EditTextPreference +import org.meshtastic.core.ui.component.ListItem import org.meshtastic.core.ui.component.SimpleAlertDialog import org.meshtastic.core.ui.component.SwitchPreference import org.meshtastic.core.ui.component.TitledCard @@ -110,6 +111,36 @@ fun NetworkConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBac viewModel.setConfig(config) }, ) { + // Display device connection status + state.deviceConnectionStatus?.let { connectionStatus -> + if ( + connectionStatus.wifi?.status?.isConnected == true || + connectionStatus.ethernet?.status?.isConnected == true + ) { + item { + TitledCard(title = stringResource(R.string.connection_status)) { + connectionStatus.wifi?.let { wifiStatus -> + if (wifiStatus.status.isConnected) { + ListItem( + text = stringResource(R.string.wifi_ip), + supportingText = formatIpAddress(wifiStatus.status.ipAddress), + trailingIcon = null, + ) + } + } + connectionStatus.ethernet?.let { ethernetStatus -> + if (ethernetStatus.status.isConnected) { + ListItem( + text = stringResource(R.string.ethernet_ip), + supportingText = formatIpAddress(ethernetStatus.status.ipAddress), + trailingIcon = null, + ) + } + } + } + } + } + } if (state.metadata?.hasWifi == true) { item { TitledCard(title = stringResource(R.string.wifi_config)) { @@ -274,3 +305,9 @@ fun NetworkConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBac private fun extractWifiCredentials(qrCode: String) = Regex("""WIFI:S:(.*?);.*?P:(.*?);""").find(qrCode)?.destructured?.let { (ssid, password) -> ssid to password } ?: (null to null) + +@Suppress("detekt:MagicNumber") +private fun formatIpAddress(ipAddress: Int): String = "${(ipAddress) and 0xFF}." + + "${(ipAddress shr 8) and 0xFF}." + + "${(ipAddress shr 16) and 0xFF}." + + "${(ipAddress shr 24) and 0xFF}"