mirror of
https://github.com/meshtastic/Meshtastic-Android.git
synced 2026-03-28 10:42:31 -04:00
feat(bluetooth): conditional RSSI polling (#3489)
Signed-off-by: James Rich <2199651+jamesarich@users.noreply.github.com>
This commit is contained in:
@@ -158,14 +158,18 @@ constructor(
|
||||
@Suppress("LoopWithTooManyJumpStatements", "MagicNumber")
|
||||
val pollingJob =
|
||||
service.serviceScope.handledLaunch {
|
||||
while (true) {
|
||||
try {
|
||||
delay(2500) // Poll every 5 seconds
|
||||
safe?.asyncReadRemoteRssi { res -> res.getOrNull()?.let { trySend(it) } }
|
||||
} catch (ex: CancellationException) {
|
||||
break // Stop polling on cancellation
|
||||
} catch (ex: Exception) {
|
||||
Timber.d("RSSI polling error: ${ex.message}")
|
||||
service.isRssiPollingEnabled.collect { isEnabled ->
|
||||
if (isEnabled) {
|
||||
while (true) {
|
||||
try {
|
||||
delay(10000) // Poll every 10 seconds
|
||||
safe?.asyncReadRemoteRssi { res -> res.getOrNull()?.let { trySend(it) } }
|
||||
} catch (ex: CancellationException) {
|
||||
break // Stop polling on cancellation
|
||||
} catch (ex: Exception) {
|
||||
Timber.d("RSSI polling error: ${ex.message}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,6 +87,13 @@ 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
|
||||
|
||||
@@ -17,12 +17,14 @@
|
||||
|
||||
package com.geeksville.mesh.service
|
||||
|
||||
import android.Manifest
|
||||
import android.bluetooth.BluetoothGatt
|
||||
import android.bluetooth.BluetoothGattCallback
|
||||
import android.bluetooth.BluetoothGattCharacteristic
|
||||
import android.bluetooth.BluetoothGattDescriptor
|
||||
import android.bluetooth.BluetoothProfile
|
||||
import android.os.Build
|
||||
import androidx.annotation.RequiresPermission
|
||||
import com.geeksville.mesh.logAssert
|
||||
import com.geeksville.mesh.util.exceptionReporter
|
||||
import timber.log.Timber
|
||||
@@ -43,20 +45,20 @@ internal class SafeBluetoothGattCallback(private val safeBluetooth: SafeBluetoot
|
||||
private const val MYSTERY_STATUS_CODE = 257
|
||||
}
|
||||
|
||||
@RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
|
||||
@Suppress("CyclomaticComplexMethod")
|
||||
override fun onConnectionStateChange(g: BluetoothGatt, status: Int, newState: Int) = exceptionReporter {
|
||||
Timber.i("new bluetooth connection state $newState, status $status")
|
||||
|
||||
when (newState) {
|
||||
BluetoothProfile.STATE_CONNECTED -> {
|
||||
safeBluetooth.state =
|
||||
newState // we only care about connected/disconnected - not the transitional states
|
||||
|
||||
// If autoconnect is on and this connect attempt failed, hopefully some future attempt will
|
||||
// succeed
|
||||
if (status != BluetoothGatt.GATT_SUCCESS && safeBluetooth.autoReconnect) {
|
||||
Timber.e("Connect attempt failed $status, not calling connect completion handler...")
|
||||
if (status != BluetoothGatt.GATT_SUCCESS) {
|
||||
Timber.e("Connect attempt failed with status $status")
|
||||
safeBluetooth.lostConnection("connection failed with status $status")
|
||||
} else {
|
||||
safeBluetooth.state = newState
|
||||
workQueue.completeWork(status, Unit)
|
||||
}
|
||||
}
|
||||
@@ -111,6 +113,10 @@ internal class SafeBluetoothGattCallback(private val safeBluetooth: SafeBluetoot
|
||||
}
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
// Anything that is not a successful connection should be treated as a failure.
|
||||
safeBluetooth.lostConnection("unexpected connection state: $newState")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -142,6 +148,7 @@ internal class SafeBluetoothGattCallback(private val safeBluetooth: SafeBluetoot
|
||||
workQueue.completeWork(status, Unit)
|
||||
}
|
||||
|
||||
@RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
|
||||
override fun onCharacteristicWrite(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic, status: Int) {
|
||||
val reliable = safeBluetooth.currentReliableWrite
|
||||
if (reliable != null) {
|
||||
|
||||
@@ -38,6 +38,7 @@ 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
|
||||
@@ -116,6 +117,11 @@ 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) {
|
||||
|
||||
@@ -19,6 +19,7 @@ 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
|
||||
@@ -39,10 +40,19 @@ 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())
|
||||
|
||||
|
||||
Reference in New Issue
Block a user