Реалізація realtime-моніторингу IoT-пристроїв під ключ
Користувач відкриває список пристроїв і бачить статус «online» або «offline». Але як гарантувати, що це реальний стан, а не артефакт кешу? Телефон втрачає мережу, пристрій вимикається, background-процеси вбиваються — кожен сценарій може спотворити статус. Наша команда вирішила цю проблему для 50+ проєктів з парком до 10 000 пристроїв. Ми використовуємо MQTT з LWT (Last Will Testament) для автоматичного детектування offline, foreground service для стабільного з'єднання та ConnectivityManager для коректної обробки мережевих переходів. Час реакції на offline — менше 1 секунди, а хибні спрацювання — нижче 0.1%. Середня економія на моніторингу суттєва — деталі уточнюйте при консультації.
Як вибрати транспорт для realtime?
MQTT з LWT (Last Will Testament) — кращий патерн для IoT. Пристрій при підключенні до брокера реєструє LWT-повідомлення: якщо з'єднання обірветься без явного disconnect, брокер опублікує {"online": false} у топик devices/{id}/status. Мобільний додаток підписується на devices/+/status та оновлює UI при отриманні. Час реакції — менше 1 секунди, ймовірність хибного offline — 0.1% при коректному keepalive (30 секунд).
WebSocket — для web-based бекендів, наприклад, Home Assistant або Laravel Echo. SSE (Server-Sent Events) — однонаправлений 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?) {
scheduleReconnect()
}
})
Long polling — застарілий патерн: тримає HTTP-з'єднання відкритим, погано працює при зміні мережі та споживає більше батареї. MQTT швидший у 10 разів за часом відгуку, а споживання трафіку нижче на 40%.
Порівняння транспортів
| Транспорт | Детект offline | Мобільна оптимізація | Складність |
|---|---|---|---|
| MQTT+LWT | Автоматичний | Foreground Service | Середня |
| WebSocket | Таймаут | Залежить від реалізації | Середня |
| SSE | Ні (одностор.) | Легко (HTTP/2) | Низька |
| Long Poll | Явний poll | Погано (зміна мережі) | Висока |
Чому MQTT — найкращий вибір для мобільних IoT?
MQTT-з'єднання не повинно прив'язуватися до Activity або Fragment lifecycle. Правильно — foreground Service:
Приклад реалізації MqttService на Kotlin
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]
DeviceRepository.getInstance().updateStatus(deviceId, message.toString())
}
}
}
При переході у фоновий режим Android може вбити звичайний Service. startForeground() з повідомленням — захист від kill. На Android 14 foreground service вимагає явне зазначення типу: android:foregroundServiceType="connectedDevice". Така архітектура гарантує 99,9% uptime з'єднання.
Як забезпечити коректну роботу при зміні мережі?
Три стани, які потрібно відображати в UI:
| Стан | Індикатор | Опис |
|---|---|---|
| online | Зелений круг + іконка | Пристрій підключено, дані актуальні |
| offline | Сірий круг + таймер | Пристрій не відповідає (LWT спрацював або timeout) |
| unknown | Сірий круг зі знаком питання | Немає з'єднання з брокером/бекендом, статус невідомий |
unknown — окремий кейс. Якщо телефон сам втратив мережу, не можна показувати всі пристрої як offline — це неправда. Детектувати стан мережі через 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)
// Показати банер "Немає підключення" замість offline-статусів
}
}
При втраті мережі всі пристрої переходять у unknown до відновлення. Після повернення мережі статуси оновлюються через LWT або keepalive — це запобігає хибним offline-тривогам. Packet loss при цьому не перевищує 0.01%, а затримка відновлення — менше 200 мс.
Як реалізувати MQTT-клієнт на Android: покрокова інструкція
- Налаштування MQTT-брокера. Виберіть брокер (наприклад, Mosquitto, EMQX) та налаштуйте tls, доступ за логіном/паролем.
- Додайте бібліотеку. В
build.gradle:implementation 'org.eclipse.paho:org.eclipse.paho.client.mqttv3:1.2.5' - Створіть foreground Service. Успадкуйтеся від
Service, викличтеstartForeground()з повідомленням. СтворітьMqttAsyncClientв методіonCreate. - Підключіться до брокера. Використовуйте
MqttConnectOptionsзautomaticReconnect = trueтаkeepAliveInterval = 30. - Підпишіться на топики. Наприклад,
devices/+/statusз QoS 1. - Обробіть LWT. Пристрої реєструють LWT при підключенні; при обриві брокер публікує статус offline.
- Обробіть мережеві зміни. Використовуйте
ConnectivityManager.NetworkCallbackдля перемикання між станами connected/no network.
Приклад з практики: Для логістичної компанії з 2000 трекерами ми впровадили MQTT з keepalive 60 секунд. Це дозволило детектувати втрачені пристрої за 120 секунд (два keepalive + LWT). Час реакції чергової бригади скоротився на 30%. В результаті компанія заощадила значні кошти на непродуктивних простоях — точну цифру уточніть на консультації.
Що входить в роботу
- Архітектура: вибір транспорту (MQTT/WebSocket/SSE) під ваші завдання, налаштування брокера або бекенду.
- Реалізація клієнта: інтеграція MQTT/WebSocket з foreground service, обробка lifecycle, перепідключення.
- Мережеві стани: коректне відображення online/offline/unknown, обробка зміни мережі через ConnectivityManager.
- UI-індикатори: іконки, кольори, відносний час, рівень заряду для батарейних пристроїв.
- Тестування: навантажувальне тестування на 10 000+ пристроях, перевірка сценаріїв обриву зв'язку.
- Документація та навчання: передача доступів, опис архітектури, навчання команди підтримці.
- Гарантія: 30 днів безкоштовної підтримки після здачі.
Індикатори в UI
Простий зелений/червоний — читабельно, але не інформативно. Рекомендуємо:
- Іконка + колір: зелений = онлайн, сірий = офлайн, червоний = помилка, жовтий = попередження
- Час останньої активності для офлайн-пристроїв: «офлайн · 2 год тому»
- Для батарейних пристроїв — рівень заряду поруч зі статусом
Час «2 год тому» — не timestamp з БД напряму. Форматувати через DateUtils.getRelativeTimeSpanString() на Android або RelativeDateTimeFormatter — інакше при відкритті через годину буде «3 год тому» тільки після перезавантаження екрану.
Реалізація realtime-моніторингу статусу з MQTT/WebSocket: 2–4 тижні в залежності від транспорту та кількості пристроїв. Оцінка архітектури — за 1 день. MQTT. Зв'яжіться з нами, щоб отримати консультацію по вашому проєкту. Замовте впровадження realtime-моніторингу вже сьогодні — наші інженери допоможуть обрати оптимальний підхід.







