Моніторинг статусу IoT-пристроїв у реальному часі у мобільному додатку
Користувач відкриває список пристроїв та бачить актуальний статус кожного — онлайн, офлайн, помилка живлення, низький заряд. Не «статус 5 хвилин тому», а зараз. Технічно це завдання двостороннього realtime-з'єднання з правильним управлінням lifecycle на мобільному пристрої.
Транспорти для realtime статусу
MQTT з LWT (Last Will Testament). Пристрій при підключенні до брокера реєструє LWT-повідомлення: якщо з'єднання розірветься без явного disconnect, брокер публікує {"online": false} у топік devices/{id}/status. Мобільний додаток підписується на devices/+/status та оновлює UI при отриманні.
Це найкращий паттерн для IoT: виявлення офлайну відбувається автоматично, без polling.
WebSocket. Для web-based бекендів. Home Assistant API, користувацький бекенд на Laravel з Pusher або Laravel Echo — всі через WebSocket. Підходить коли MQTT-брокер недоступний напрямки з телефона.
Server-Sent Events (SSE). Однонаправлений HTTP-стриміїм. Простіше WebSocket, працює через будь-який CDN. На Android — OkHttp з EventSource:
val request = Request.Builder().url("$baseUrl/api/devices/stream").build()
val eventSource = EventSources.createFactory(okHttpClient)
.newEventSource(request, object : EventSourceListener() {
override fun onEvent(source: EventSource, id: String?, type: String?, data: String) {
val update = json.decodeFromString<DeviceStatusUpdate>(data)
repository.updateDeviceStatus(update.deviceId, update.status)
}
override fun onFailure(source: EventSource, t: Throwable?, response: Response?) {
// Переподключення через ExponentialBackoff
scheduleReconnect()
}
})
Long polling — застарілий паттерн, не рекомендується для мобільних додатків. Тримає HTTP-з'єднання відкритим, погано працює при смені мережі.
MQTT на Android: Управління lifecycle
MQTT-з'єднання не має прив'язуватися до Activity або Fragment lifecycle. Правильно — foreground Service:
class MqttService : Service() {
private lateinit var mqttClient: MqttAsyncClient
override fun onCreate() {
val options = MqttConnectOptions().apply {
isAutomaticReconnect = true
isCleanSession = false
keepAliveInterval = 30
}
mqttClient = MqttAsyncClient(brokerUrl, clientId, MqttDefaultFilePersistence())
mqttClient.connect(options).waitForCompletion()
mqttClient.subscribe("devices/+/status", 1) { topic, message ->
val deviceId = topic.split("/")[1]
// Broadcast або через Repository
DeviceRepository.getInstance().updateStatus(deviceId, message.toString())
}
}
}
При переході у фоновий режим Android може вбити звичайний Service. startForeground() з повідомленням — захист від kill. На Android 14 foreground service потребує явного указання типу: android:foregroundServiceType="connectedDevice".
Обробка переходів online/offline
Три стани, які потребує відображати у UI:
-
online— пристрій підключено, дані актуальні -
offline— пристрій не відповідає (LWT сработав або timeout) -
unknown— немає з'єднання з брокером/бекендом, статус невідомий
unknown — окремий кейс. Якщо телефон сам втратив мережу, не можна показувати всі пристрої як офлайн — це неправда. Виявляйте стан мережі через ConnectivityManager.NetworkCallback:
val networkCallback = object : ConnectivityManager.NetworkCallback() {
override fun onAvailable(network: Network) {
viewModel.setConnectionState(ConnectionState.CONNECTED)
}
override fun onLost(network: Network) {
viewModel.setConnectionState(ConnectionState.NO_NETWORK)
// Показати баннер "Немає з'єднання" замість офлайн-статусів
}
}
Індикатори у UI
Простий зелений/червоний крапко — читаємо, але не інформативно. Краще:
- Іконка + колір: зелений = онлайн, сірий = офлайн, червоний = помилка, жовтий = попередження
- Час останньої активності для офлайн-пристроїв: «офлайн · 2 г тому»
- Для батарейних пристроїв — рівень заряду поруч із статусом
Час «2 г тому» — не timestamp з БД напрямки. Форматуйте через DateUtils.getRelativeTimeSpanString() на Android або RelativeDateTimeFormatter — інакше при повторному відкритті через годину буде «3 г тому» лише після перезавантаження екрану.
Реалізація realtime-мониторинга статусу з MQTT/WebSocket: 2–4 тижні залежно від транспорту та кількості пристроїв.







