4.9 KiB
Подключение устройства к IoT (Nubes, тест-стенд)
Проверено end-to-end 2026-10-11 на развёрнутом стенде
(https://iot-api.containerk8s.dev.nubes.ru, wss://exqx-iot.containerk8s.dev.nubes.ru/mqtt).
1. Регистрация устройства (HTTP API, JWT HS256)
JWT подписывается JWT_HMAC_SECRET (HS256), claim sub = ваш namespace,
exp обязателен; namespace в пути обязан совпадать с sub, иначе 403.
curl -X POST "$API/v1/namespaces/$NS/iot/devices" \
-H "Authorization: Bearer $JWT" -H 'Content-Type: application/json' \
-d '{"name":"dev1","device_id":"d-01"}'
Ответ 201 содержит единственный раз выдаваемый пароль:
{"name":"dev1","namespace":"e2e-nubes","device_id":"d-01","enabled":true,
"mqtt_username":"e2e-nubes_d-01","mqtt_password":"<hex>",
"telemetry_topic":"e2e-nubes/telemetry/d-01"}
GET …/devices/{name} пароль не возвращает. Ограничения идентификаторов:
^[a-zA-Z0-9][a-zA-Z0-9-]*$, namespace ≤ 56, name/device_id ≤ 64 символов
(иначе 400). Дубликаты name/device_id → 409.
2. Подключение по MQTT
- URL:
wss://exqx-iot.containerk8s.dev.nubes.ru/mqtt(WebSocket, subprotocolmqtt); внутри кластера —ws://containerk8s.<uuid-инстанса EMQX>.svc.cluster.local:8083/mqtt. - username =
namespace_device_id, пароль — из ответа на создание. - Публикация: топик
namespace/telemetry/device_id(telemetry_topic).
ACL: устройству разрешён только свой топик. Публикация/подписка куда-либо ещё
(чужой namespace, другое устройство, wildcard +/#) → deny_action=disconnect,
соединение рвётся. Также запрещены: retained-публикации
(retain_available=false), топики глубже 10 уровней, subscribe на
+/telemetry/+ (это привилегия бриджа).
Коды CONNACK: 0 — успех, 4 — неверный username/пароль, 5 — не авторизован
(в т.ч. устройство выключено через PATCH {"enabled": false}).
3. Требование к payload — ВАЖНО
Дедупликация выполняется по содержимому сообщения:
message_id = SHA256(namespace | device_id | topic | payload)
(internal/service/bridge/handler.go). Поэтому:
- два побайтово одинаковых payload подряд считаются дубликатом и сохраняются один раз (это защита от повторной доставки SQS at-least-once);
- ⇒ в payload обязателен timestamp или счётчик, иначе легитимные повторные
одинаковые показания (например,
{"t":21.0}каждую минуту) будут отброшены.
Пример корректного payload:
{"temp": 21.5, "ts": 1791737028, "seq": 4211}
Payload валидный JSON сохраняется как JSON; не-JSON (текст, бинарь) оборачивается
в строку. Лимиты: MQTT-пакет ≤ 1 МБ (max_packet_size), envelope для SQS ≤ 250 КБ
(maxSQSBodyBytes) — сообщения больше отбрасываются мостом.
QoS: QoS0 — «выстрелил и забыл»; для гарантии доставки используйте QoS1
(дождитесь PUBACK перед закрытием соединения).
4. Чтение телеметрии
curl "$API/v1/namespaces/$NS/iot/telemetry?device_id=d-01&limit=50&offset=0" \
-H "Authorization: Bearer $JWT"
limit — дефолт 50, потолок 1000. Ответ:
{"count":N,"items":[{"id":…,"device_id":…,"ts":…,"payload":…}]}.
⚠️ Чтение отдаёт payload целиком, поэтому limit=1000 при крупных payload'ах
дорого для клиента и сервиса — для больших объёмов берите меньше и
используйте offset.
5. Наблюдаемое поведение (e2e, 2026-10-11)
Задержка «MQTT → видно в API» недетерминирована и доходит до ~20 с: потребитель
SQS работает long-poll окном (SQS_LONG_POLL_SECONDS=20), батч пишется в PG и
только потом удаляется из очереди. Для тестов/интеграции — опрашивать с
таймаутом ≥ 30 с, а не фиксированной паузой 1–5 с.