Дашборд IoT-телеметрії у мобільному додатку
IoT-дашборд — екран, що показує дані з кількох датчиків одночасно: поточні значення, тренди, історичні графіки, статуси пристроїв. Ключова проблема — продуктивність при великій кількості віджетів та часто оновлюваних даних. Неправильна архітектура дає затримку UI, пропуск оновлень та витрату батареї.
Архітектура даних для дашборду
Дашборд агрегує дані з різних джерел: MQTT-топіки з realtime телеметрією, REST API для історичних даних, WebSocket для подій. Всі це потребує об'єднання у единую ViewModel, яка реактивно оновлює віджети.
На Android — combine кількох StateFlow:
class DashboardViewModel : ViewModel() {
private val temperatureFlow = mqttRepository.getTopicFlow("sensors/+/temperature")
private val humidityFlow = mqttRepository.getTopicFlow("sensors/+/humidity")
private val devicesFlow = deviceRepository.devices
val dashboardState = combine(
temperatureFlow,
humidityFlow,
devicesFlow
) { temperatures, humidities, devices ->
DashboardState(
sensors = devices.map { device ->
SensorWidgetData(
id = device.id,
name = device.name,
temperature = temperatures[device.id],
humidity = humidities[device.id],
isOnline = device.isOnline
)
}
)
}.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), DashboardState())
}
SharingStarted.WhileSubscribed(5_000) — потік припиняється через 5 секунд після переходу екрану у background. Економить з'єднання, дані перезавантажуються при поверненні.
Віджети: Що та як рендерити
Типовий IoT-дашборд містить:
- Числові віджети — поточне значення + одиниця + статус (норма/попередження/критично)
- Мініатюрні графіки — тренд за останню годину
- Gauge/Speedometer — для параметрів з явними границями (тиск, рівень CO2)
- Карточки статусу пристроїв — онлайн/офлайн + останні дані
На Android Compose: кожен віджет — окремий @Composable з ключем пристрою. LazyVerticalGrid для сітки:
LazyVerticalGrid(
columns = GridCells.Adaptive(minSize = 160.dp),
contentPadding = PaddingValues(16.dp),
horizontalArrangement = Arrangement.spacedBy(12.dp),
verticalArrangement = Arrangement.spacedBy(12.dp)
) {
items(dashboardState.sensors, key = { it.id }) { sensor ->
SensorWidget(
sensor = sensor,
modifier = Modifier.animateItemPlacement()
)
}
}
animateItemPlacement() — плавна анімація при додаванні/видаленні віджетів. Без key — Compose повністю перерисовуватиме список при кожному оновленні.
Числові віджети та частві оновлення. MQTT може слати дані раз у секунду. Оновлювання UI з такою частотою — витрата батареї. Throttle на рівні Flow:
temperatureFlow
.throttleLatest(1000) // не чаще раза в секунду
.collect { updateWidget(it) }
throttleLatest замість debounce — показує останнє значення за період, не чекає паузи.
Історичні дані: Завантаження та кеш
При відкритті екрану потребуються історичні дані за останні N годин для мініатюрних графіків. Завантажувати все дані разом при відкритті дашборду — погано, якщо пристроїв багато.
Паттерн: завантажувати історію лінивим чином, по мірі появлення віджетів у viewport. LazyColumn/LazyGrid + LaunchedEffect при першому показу віджету:
@Composable
fun SensorWidget(sensor: SensorWidgetData, viewModel: DashboardViewModel) {
LaunchedEffect(sensor.id) {
viewModel.loadHistory(sensor.id, hours = 1)
}
// Показати skeleton поки дані грузяться
val history by viewModel.getHistoryFlow(sensor.id).collectAsState(emptyList())
MiniChart(data = history)
}
Кешувати історію у Room з TTL: дані старше 5 хвилин — запросити з сервера заново. Свіжі — повернути з кешу без мережевого запиту.
Налаштування дашборду користувачем
Перетаскування віджетів, додавання/видалення датчиків — опціональна, але цінна функція. На Android: ItemTouchHelper для RecyclerView або ReorderableItem з бібліотеки compose-reorderable. Зберігайте порядок віджетів у SharedPreferences або DataStore.
Реалізація дашборду з realtime оновленням, історичними графіками та адаптивною сіткою: 4–6 тижнів. Стоимость рассчитывается индивидуально.







