mirror of
https://github.com/meshtastic/Meshtastic-Android.git
synced 2026-04-20 22:23:37 +00:00
feat: add NodeDetailsScreen with metrics and remote admin navigation
This commit is contained in:
parent
b73c53bc11
commit
6be44675e2
11 changed files with 322 additions and 308 deletions
|
|
@ -1,37 +1,27 @@
|
|||
package com.geeksville.mesh.model
|
||||
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.geeksville.mesh.R
|
||||
import com.geeksville.mesh.TelemetryProtos.Telemetry
|
||||
import com.geeksville.mesh.database.MeshLogRepository
|
||||
import com.geeksville.mesh.repository.datastore.RadioConfigRepository
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.SharingStarted.Companion.WhileSubscribed
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.flatMapLatest
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
import kotlin.enums.EnumEntries
|
||||
|
||||
enum class MetricsPage(
|
||||
@StringRes val titleResId: Int,
|
||||
@DrawableRes val drawableResId: Int,
|
||||
) {
|
||||
DEVICE(R.string.device, R.drawable.baseline_charging_station_24),
|
||||
ENVIRONMENT(R.string.environment, R.drawable.baseline_thermostat_24),
|
||||
}
|
||||
|
||||
data class MetricsState(
|
||||
val pages: EnumEntries<MetricsPage> = MetricsPage.entries,
|
||||
val isLoading: Boolean = false,
|
||||
val deviceMetrics: List<Telemetry> = emptyList(),
|
||||
val environmentMetrics: List<Telemetry> = emptyList(),
|
||||
val environmentDisplayFahrenheit: Boolean = false,
|
||||
) {
|
||||
fun hasDeviceMetrics() = deviceMetrics.isNotEmpty()
|
||||
fun hasEnvironmentMetrics() = environmentMetrics.isNotEmpty()
|
||||
|
||||
companion object {
|
||||
val Empty = MetricsState()
|
||||
}
|
||||
|
|
@ -39,60 +29,35 @@ data class MetricsState(
|
|||
|
||||
@HiltViewModel
|
||||
class MetricsViewModel @Inject constructor(
|
||||
val nodeDB: NodeDB,
|
||||
private val meshLogRepository: MeshLogRepository,
|
||||
meshLogRepository: MeshLogRepository,
|
||||
radioConfigRepository: RadioConfigRepository,
|
||||
) : ViewModel() {
|
||||
private val destNum = MutableStateFlow(0)
|
||||
|
||||
private val isLoading = MutableStateFlow(false)
|
||||
private val _deviceMetrics = MutableStateFlow<List<Telemetry>>(emptyList())
|
||||
private val _environmentMetrics = MutableStateFlow<List<Telemetry>>(emptyList())
|
||||
|
||||
val state = combine(
|
||||
isLoading,
|
||||
_deviceMetrics,
|
||||
_environmentMetrics,
|
||||
radioConfigRepository.deviceProfileFlow,
|
||||
) { isLoading, device, environment, profile ->
|
||||
MetricsState(
|
||||
isLoading = isLoading,
|
||||
deviceMetrics = device,
|
||||
environmentMetrics = environment,
|
||||
environmentDisplayFahrenheit = profile.moduleConfig.telemetry.environmentDisplayFahrenheit,
|
||||
)
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
val state = destNum.flatMapLatest { destNum ->
|
||||
combine(
|
||||
meshLogRepository.getTelemetryFrom(destNum),
|
||||
radioConfigRepository.moduleConfigFlow,
|
||||
) { telemetry, config ->
|
||||
MetricsState(
|
||||
deviceMetrics = telemetry.filter { it.hasDeviceMetrics() },
|
||||
environmentMetrics = telemetry.filter {
|
||||
it.hasEnvironmentMetrics() && it.environmentMetrics.relativeHumidity >= 0f
|
||||
},
|
||||
environmentDisplayFahrenheit = config.telemetry.environmentDisplayFahrenheit,
|
||||
)
|
||||
}
|
||||
}.stateIn(
|
||||
scope = viewModelScope,
|
||||
started = WhileSubscribed(5_000),
|
||||
started = WhileSubscribed(),
|
||||
initialValue = MetricsState.Empty,
|
||||
)
|
||||
|
||||
/**
|
||||
* Gets the short name of the node identified by `nodeNum`.
|
||||
*/
|
||||
fun getNodeName(nodeNum: Int): String? = nodeDB.nodeDBbyNum.value[nodeNum]?.user?.shortName
|
||||
|
||||
/**
|
||||
* Used to set the Node for which the user will see charts for.
|
||||
*/
|
||||
fun setSelectedNode(nodeNum: Int) {
|
||||
viewModelScope.launch {
|
||||
isLoading.value = true
|
||||
meshLogRepository.getTelemetryFrom(nodeNum).collect {
|
||||
val deviceList = mutableListOf<Telemetry>()
|
||||
val environmentList = mutableListOf<Telemetry>()
|
||||
for (telemetry in it) {
|
||||
if (telemetry.hasDeviceMetrics()) {
|
||||
deviceList.add(telemetry)
|
||||
}
|
||||
/* Avoiding negative outliers */
|
||||
if (telemetry.hasEnvironmentMetrics() && telemetry.environmentMetrics.relativeHumidity >= 0f) {
|
||||
environmentList.add(telemetry)
|
||||
}
|
||||
}
|
||||
_deviceMetrics.value = deviceList
|
||||
_environmentMetrics.value = environmentList
|
||||
isLoading.value = false
|
||||
}
|
||||
}
|
||||
destNum.value = nodeNum
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue