Реализация настройки пороговых значений IoT-датчиков через мобильное приложение
Пороговые значения — границы нормы для датчика: выше +28°C — предупреждение, ниже +5°C — критично, отправить push. Пользователь настраивает эти границы в приложении, сервер хранит их и генерирует алерты при выходе за пределы. Задача выглядит простой, но требует аккуратного UX и надёжной синхронизации с бэкендом.
UI для настройки порогов
Числовые поля ввода — плохой выбор для порогов. Пользователь не помнит диапазон параметра, не видит контекст. Лучше — Range Slider с отображением текущего значения сенсора на той же шкале.
На Android Compose — кастомный RangeSlider или Slider из Material3. Стандартный RangeSlider из Material3 поддерживает два ползунка:
var thresholds by remember { mutableStateOf(sensor.minThreshold..sensor.maxThreshold) }
Column {
Text("Температура: ${sensor.currentValue}°C")
Text("Допустимый диапазон: ${thresholds.start.roundToInt()}°C — ${thresholds.endInclusive.roundToInt()}°C")
RangeSlider(
value = thresholds,
onValueChange = { thresholds = it },
valueRange = sensor.absoluteMin..sensor.absoluteMax,
steps = 0,
onValueChangeFinished = {
viewModel.updateThresholds(sensor.id, thresholds.start, thresholds.endInclusive)
}
)
}
onValueChangeFinished — отправлять на сервер только после отпускания ползунка, не при каждом движении. Иначе — шквал API-запросов при перетаскивании.
Текущее значение сенсора на шкале слайдера — показать как вертикальную метку. Через Canvas поверх слайдера: вычислить X-позицию по (currentValue - min) / (max - min) * sliderWidth.
Типы порогов
Разные параметры требуют разных конфигураций:
| Параметр | Логика | Пример |
|---|---|---|
| Температура | Нижняя + верхняя граница | +5°C … +25°C |
| Движение | Только булев триггер | Обнаружено/нет |
| Уровень CO2 | Только верхняя граница | > 1000 ppm |
| Давление | Нижняя + верхняя + скорость изменения | < 950 или > 1050 hPa |
Не пытаться сделать один универсальный компонент для всех. Лучше набор специализированных: BooleanThreshold, SingleBoundThreshold, RangeThreshold. Разные экраны настройки для разных типов датчиков.
Синхронизация с сервером и локальный кеш
Пороги хранятся на сервере и применяются на сервере при обработке телеметрии. Мобильное приложение — только UI для их настройки.
При открытии экрана: загрузить актуальные пороги с сервера, показать. При изменении: сохранить локально (оптимистичное обновление), отправить на сервер, при ошибке — откатить к предыдущему значению.
fun updateThreshold(deviceId: String, min: Float, max: Float) {
val previous = _thresholds.value
// Оптимистичное обновление
_thresholds.update { it.copy(minValue = min, maxValue = max) }
viewModelScope.launch {
val result = repository.saveThreshold(deviceId, min, max)
if (result.isFailure) {
// Откат
_thresholds.value = previous
_events.emit(UiEvent.ShowError("Не удалось сохранить настройки"))
}
}
}
Оптимистичное обновление делает UI отзывчивым — пользователь не ждёт ответа сервера. Откат — защита от потери данных при сетевой ошибке.
Уведомления при превышении порога
Алерты приходят через push (FCM/APNS). Payload уведомления должен содержать device_id, parameter, current_value, threshold_exceeded — чтобы приложение могло открыть нужный экран при тапе на уведомление.
На Android: setNotification() в FCM payload только если приложение в фоне. Для foreground — FirebaseMessagingService.onMessageReceived() с локальным NotificationManager. Разные каналы (NotificationChannel) для предупреждений и критических алертов — разные звуки и приоритеты.
Реализация настройки порогов с Range Slider, оптимистичным обновлением и push-алертами: 2–3 недели. Стоимость рассчитывается индивидуально.







