feat(ui): Display BLE signal strength for connected device (#3721)

Signed-off-by: James Rich <2199651+jamesarich@users.noreply.github.com>
This commit is contained in:
James Rich
2025-11-17 15:15:44 -06:00
committed by GitHub
parent 73d933fe14
commit a497086c77
4 changed files with 26 additions and 23 deletions

View File

@@ -90,13 +90,6 @@ constructor(
private val _currentDeviceAddressFlow = MutableStateFlow(radioPrefs.devAddr)
val currentDeviceAddressFlow: StateFlow<String?> = _currentDeviceAddressFlow.asStateFlow()
private val _isRssiPollingEnabled = MutableStateFlow(false)
val isRssiPollingEnabled: StateFlow<Boolean> = _isRssiPollingEnabled.asStateFlow()
fun setRssiPolling(enabled: Boolean) {
_isRssiPollingEnabled.value = enabled
}
private val logSends = false
private val logReceives = false
private lateinit var sentPacketsLog: BinaryLogFile

View File

@@ -42,7 +42,6 @@ import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
@@ -56,6 +55,7 @@ import androidx.compose.ui.unit.sp
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.geeksville.mesh.model.BTScanModel
import com.geeksville.mesh.model.DeviceListEntry
import com.geeksville.mesh.ui.connections.components.BLEDevices
import com.geeksville.mesh.ui.connections.components.ConnectionsSegmentedBar
import com.geeksville.mesh.ui.connections.components.CurrentlyConnectedInfo
@@ -126,11 +126,6 @@ fun ConnectionsScreen(
val recentTcpDevices by scanModel.recentTcpDevicesForUi.collectAsStateWithLifecycle()
val usbDevices by scanModel.usbDevicesForUi.collectAsStateWithLifecycle()
DisposableEffect(Unit) {
connectionsViewModel.onStart()
onDispose { connectionsViewModel.onStop() }
}
/* Animate waiting for the configurations */
var isWaiting by remember { mutableStateOf(false) }
if (isWaiting) {
@@ -209,6 +204,9 @@ fun ConnectionsScreen(
TitledCard(title = stringResource(Res.string.connected_device)) {
CurrentlyConnectedInfo(
node = node,
bleDevice =
bleDevices.firstOrNull { it.fullAddress == selectedDevice }
as DeviceListEntry.Ble?,
onNavigateToNodeDetails = onNavigateToNodeDetails,
onClickDisconnect = { scanModel.disconnect() },
)

View File

@@ -19,7 +19,6 @@ package com.geeksville.mesh.ui.connections
import androidx.lifecycle.ViewModel
import com.geeksville.mesh.repository.bluetooth.BluetoothRepository
import com.geeksville.mesh.repository.radio.RadioInterfaceService
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
@@ -40,18 +39,10 @@ class ConnectionsViewModel
constructor(
radioConfigRepository: RadioConfigRepository,
serviceRepository: ServiceRepository,
private val radioInterfaceService: RadioInterfaceService,
nodeRepository: NodeRepository,
bluetoothRepository: BluetoothRepository,
private val uiPrefs: UiPrefs,
) : ViewModel() {
fun onStart() {
radioInterfaceService.setRssiPolling(true)
}
fun onStop() {
radioInterfaceService.setRssiPolling(false)
}
val localConfig: StateFlow<LocalConfig> =
radioConfigRepository.localConfigFlow.stateInWhileSubscribed(initialValue = LocalConfig.getDefaultInstance())

View File

@@ -28,6 +28,11 @@ import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
@@ -35,27 +40,40 @@ import androidx.compose.ui.graphics.RectangleShape
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.PreviewLightDark
import androidx.compose.ui.unit.dp
import com.geeksville.mesh.model.DeviceListEntry
import kotlinx.coroutines.delay
import org.jetbrains.compose.resources.stringResource
import org.meshtastic.core.database.model.Node
import org.meshtastic.core.strings.Res
import org.meshtastic.core.strings.disconnect
import org.meshtastic.core.strings.firmware_version
import org.meshtastic.core.ui.component.MaterialBatteryInfo
import org.meshtastic.core.ui.component.MaterialBluetoothSignalInfo
import org.meshtastic.core.ui.component.NodeChip
import org.meshtastic.core.ui.theme.AppTheme
import org.meshtastic.core.ui.theme.StatusColors.StatusRed
import org.meshtastic.proto.MeshProtos
import org.meshtastic.proto.PaxcountProtos
import org.meshtastic.proto.TelemetryProtos
import kotlin.time.Duration.Companion.seconds
/** Converts Bluetooth RSSI to a 0-4 bar signal strength level. */
@Composable
fun CurrentlyConnectedInfo(
node: Node,
onNavigateToNodeDetails: (Int) -> Unit,
onClickDisconnect: () -> Unit,
modifier: Modifier = Modifier,
bleDevice: DeviceListEntry.Ble? = null,
) {
var rssi by remember { mutableIntStateOf(0) }
LaunchedEffect(bleDevice) {
if (bleDevice != null) {
while (bleDevice.peripheral.isConnected) {
rssi = bleDevice.peripheral.readRssi()
delay(10.seconds)
}
}
}
Column(modifier = modifier) {
Row(
modifier = Modifier.fillMaxWidth().padding(start = 16.dp, end = 16.dp, top = 8.dp),
@@ -63,6 +81,9 @@ fun CurrentlyConnectedInfo(
verticalAlignment = Alignment.CenterVertically,
) {
MaterialBatteryInfo(level = node.batteryLevel, voltage = node.voltage)
if (bleDevice is DeviceListEntry.Ble) {
MaterialBluetoothSignalInfo(rssi)
}
}
Row(verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(8.dp)) {
Column(