docs(landing): rewrite as clear how-to (4 steps with copy-paste commands); v0.1.9
This commit is contained in:
@@ -1203,3 +1203,21 @@ paho connect() rc=0 даже при CONNACK≠0 (on_connect обязателен
|
||||
подставляется через {{VERSION}} (без лишней "v").
|
||||
- v0.1.8 (digest db648725dfe6) задеплоен; проверка: "/" 200, favicon/logo
|
||||
200 (image/svg+xml), chip v0.1.8.
|
||||
|
||||
---
|
||||
|
||||
## 34. Лендинг переписан — «понятно, без воды» (v0.1.9) (19:30 GMT+03)
|
||||
|
||||
- Пользователь: «слишком сухо, ничего не понял, воду лить не надо, но чтобы
|
||||
было ясно».
|
||||
- Переработано содержимое landing.html (дизайн/структура сохранены):
|
||||
- hero: «Это база телеметрии. Устройства шлют показания по MQTT...»;
|
||||
- «Как это работает»: цепочка простыми словами + «Три понятия»
|
||||
(namespace / device_id / топик на примере myhome и boiler-1);
|
||||
- «Быстрый старт — 4 шага» с ГОТОВЫМИ копипаст-командами: 1) токен
|
||||
(одна python-команда, объяснено что подпись не проверяется),
|
||||
2) регистрация устройства (два curl, объяснено ГДЕ пароль),
|
||||
3) отправка показания (полный paho-пример), 4) просмотр результата;
|
||||
- «MQTT endpoint (exqx)»: почему 400 в браузере и что делать.
|
||||
- v0.1.9 (digest fb488a8666ab) задеплоен, страница проверена в браузере
|
||||
(структура отрисована корректно).
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Makefile — монолит iot-service (образ naeel/iot-service).
|
||||
# Старые k8s-цели — в legacy/Makefile.old.
|
||||
|
||||
VERSION ?= v0.1.8
|
||||
VERSION ?= v0.1.9
|
||||
IMAGE ?= naeel/iot-service
|
||||
LDFLAGS = -X main.version=$(VERSION)
|
||||
|
||||
|
||||
@@ -161,50 +161,52 @@ pre code { background: none; border: none; padding: 0; }
|
||||
<img src="/static/logo.svg" height="28" alt="nubes">
|
||||
<div>
|
||||
<h1>IoT <span class="badge">тестирование</span></h1>
|
||||
<p class="sub">Приём и хранение телеметрии устройств: MQTT → шина SQS →
|
||||
PostgreSQL → REST API и консоль. Устройства изолированы по namespace,
|
||||
каждому namespace — отдельная база данных.</p>
|
||||
<p class="sub">Это база телеметрии. Устройства шлют сюда показания по MQTT —
|
||||
показания сохраняются и доступны через API и консоль. Ниже — как отправить
|
||||
первое показание за 2 минуты.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header">Что это и зачем</div>
|
||||
<div class="card-header">Как это работает</div>
|
||||
<div class="card-body">
|
||||
<p>Платформа принимает телеметрию от устройств по протоколу MQTT
|
||||
(WebSocket с TLS) и доставляет её в базу данных по цепочке:</p>
|
||||
<p>Устройство (датчик, бойлер, счётчик) отправляет показания по протоколу
|
||||
MQTT. Дальше показания проходят по цепочке и попадают в базу данных:</p>
|
||||
<div class="hint-box">
|
||||
устройство → <code>MQTT (wss)</code> → очередь <code>iot-telemetry</code>
|
||||
→ <b>PostgreSQL</b> → API / консоль
|
||||
</div>
|
||||
<p style="color:#374151">
|
||||
устройство → <code>wss://…/mqtt</code> (EMQX) → очередь
|
||||
<code>iot-telemetry</code> (shared-sqs) → потребитель → PostgreSQL →
|
||||
REST API / консоль.
|
||||
У каждого устройства — свои логин и пароль, поэтому чужое устройство не
|
||||
сможет писать в ваш топик. Данные разных проектов (namespace) лежат в
|
||||
разных базах и не смешиваются.
|
||||
</p>
|
||||
<p><b>Три понятия:</b></p>
|
||||
<p style="color:#374151">
|
||||
Устройства регистрируются через REST API и получают собственный
|
||||
пароль. Каждое устройство публикует только в свой топик
|
||||
<code>{namespace}/telemetry/{device_id}</code> — чужие топики
|
||||
отклоняются брокером.
|
||||
<b>namespace</b> — имя вашего проекта/группы устройств (например
|
||||
<code>myhome</code>);<br>
|
||||
<b>device_id</b> — идентификатор конкретного устройства (например
|
||||
<code>boiler-1</code>);<br>
|
||||
<b>топик</b> — адрес, в который устройство отправляет показания:
|
||||
<code>namespace/telemetry/device_id</code>.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header">Порядок действий — 3 шага</div>
|
||||
<div class="card-header">Быстрый старт — 4 шага</div>
|
||||
<div class="card-body">
|
||||
|
||||
<div class="step">
|
||||
<div class="step-num">1</div>
|
||||
<div class="step-body">
|
||||
<div class="step-title">Регистрация устройства</div>
|
||||
<p>Устройство создаётся через REST API с Bearer-JWT (структура
|
||||
<code>sub</code> + <code>exp</code>). В ответе — <code>mqtt_username</code>;
|
||||
пароль возвращается только запросом GET по имени устройства.</p>
|
||||
<div class="step-title">Получить токен доступа к API</div>
|
||||
<p>Все команды API требуют заголовок <code>Authorization: Bearer
|
||||
<токен></code>. Для тестов подойдёт самодельный токен: сервис
|
||||
проверяет только два поля (кто и до какого времени), подпись не
|
||||
проверяется. Одна команда — токен готов:</p>
|
||||
<div class="hint-box">
|
||||
<code>TOKEN="…jwt…"</code><br>
|
||||
<code>curl -X POST -H "Authorization: Bearer $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"name":"dev1","device_id":"dev-001"}' \
|
||||
https://iot.containerk8s.dev.nubes.ru/v1/namespaces/test/iot/devices</code><br>
|
||||
<code>curl -H "Authorization: Bearer $TOKEN" \
|
||||
https://iot.containerk8s.dev.nubes.ru/v1/namespaces/test/iot/devices/dev1</code>
|
||||
<code>TOKEN=$(python3 -c "import base64,json,time;h=base64.urlsafe_b64encode(b'{\"alg\":\"none\",\"typ\":\"JWT\"}').rstrip(b'=').decode();p=base64.urlsafe_b64encode(json.dumps({'sub':'admin','exp':int(time.time())+86400}).encode()).rstrip(b'=').decode();print(h+'.'+p+'.sig')")</code>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -212,18 +214,19 @@ pre code { background: none; border: none; padding: 0; }
|
||||
<div class="step">
|
||||
<div class="step-num">2</div>
|
||||
<div class="step-body">
|
||||
<div class="step-title">Подключение устройства к MQTT</div>
|
||||
<p>Endpoint <code>wss://exqx.containerk8s.dev.nubes.ru/mqtt</code>,
|
||||
подпротокол WebSocket — <code>mqtt</code>; username
|
||||
<code>{namespace}_{device_id}</code>, пароль из шага 1; публикация в
|
||||
топик <code>{namespace}/telemetry/{device_id}</code>.</p>
|
||||
<div class="step-title">Зарегистрировать устройство</div>
|
||||
<p>Одна команда — и устройство <code>boiler-1</code> появляется в
|
||||
проекте <code>myhome</code>. В ответе будет логин для MQTT
|
||||
(<code>myhome_boiler-1</code>). Пароль в ответе НЕ показывается —
|
||||
его отдаёт второй запрос.</p>
|
||||
<div class="hint-box">
|
||||
<code>import paho.mqtt.client as mqtt</code><br>
|
||||
<code>c = mqtt.Client(transport="websockets")</code><br>
|
||||
<code>c.ws_set_options(path="/mqtt")</code><br>
|
||||
<code>c.username_pw_set("test_dev-001", "<пароль>")</code><br>
|
||||
<code>c.connect("exqx.containerk8s.dev.nubes.ru", 443)</code><br>
|
||||
<code>c.publish("test/telemetry/dev-001", '{"temp":23.5}')</code>
|
||||
<code>curl -X POST -H "Authorization: Bearer $TOKEN" \<br>
|
||||
-H "Content-Type: application/json" \<br>
|
||||
-d '{"name":"boiler","device_id":"boiler-1"}' \<br>
|
||||
https://iot.containerk8s.dev.nubes.ru/v1/namespaces/myhome/iot/devices</code><br>
|
||||
<br><code># пароль — во втором запросе (поле mqtt_password):</code><br>
|
||||
<code>curl -H "Authorization: Bearer $TOKEN" \<br>
|
||||
https://iot.containerk8s.dev.nubes.ru/v1/namespaces/myhome/iot/devices/boiler</code>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -231,11 +234,31 @@ pre code { background: none; border: none; padding: 0; }
|
||||
<div class="step">
|
||||
<div class="step-num">3</div>
|
||||
<div class="step-body">
|
||||
<div class="step-title">Чтение телеметрии</div>
|
||||
<p>История сообщений — REST API (Bearer-JWT):</p>
|
||||
<div class="step-title">Отправить показание</div>
|
||||
<p>Подключение к MQTT — по адресу
|
||||
<code>wss://exqx.containerk8s.dev.nubes.ru/mqtt</code>. Логин —
|
||||
<code>myhome_boiler-1</code>, пароль — из шага 2. Показание
|
||||
отправляется в топик <code>myhome/telemetry/boiler-1</code>.</p>
|
||||
<div class="hint-box">
|
||||
<code>curl -H "Authorization: Bearer $TOKEN" \
|
||||
"https://iot.containerk8s.dev.nubes.ru/v1/namespaces/test/iot/telemetry?limit=50"</code>
|
||||
<code>import paho.mqtt.client as mqtt, json</code><br>
|
||||
<code>c = mqtt.Client(transport="websockets")</code><br>
|
||||
<code>c.ws_set_options(path="/mqtt")</code><br>
|
||||
<code>c.username_pw_set("myhome_boiler-1", "<пароль из шага 2>")</code><br>
|
||||
<code>c.connect("exqx.containerk8s.dev.nubes.ru", 443)</code><br>
|
||||
<code>c.publish("myhome/telemetry/boiler-1", json.dumps({"temp": 58.3}))</code>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="step">
|
||||
<div class="step-num">4</div>
|
||||
<div class="step-body">
|
||||
<div class="step-title">Увидеть результат</div>
|
||||
<p>Показание доходит в базу за ~1 секунду. Посмотреть — запросом или
|
||||
в консоли:</p>
|
||||
<div class="hint-box">
|
||||
<code>curl -H "Authorization: Bearer $TOKEN" \<br>
|
||||
"https://iot.containerk8s.dev.nubes.ru/v1/namespaces/myhome/iot/telemetry?limit=50"</code>
|
||||
</div>
|
||||
<a class="btn-hero" href="/console">Открыть консоль →</a>
|
||||
</div>
|
||||
@@ -272,17 +295,17 @@ pre code { background: none; border: none; padding: 0; }
|
||||
<div class="card-header">MQTT endpoint (exqx)</div>
|
||||
<div class="card-body">
|
||||
<p style="color:#374151">
|
||||
<code>https://exqx.containerk8s.dev.nubes.ru</code> — это MQTT-брокер
|
||||
(EMQX), а не веб-страница. Точка подключения — только
|
||||
<code>/mqtt</code> через WebSocket. Открытие этого адреса в браузере
|
||||
или HTTP-запрос вернёт <code>400</code> — это штатное поведение
|
||||
(нет WebSocket-подпротокола <code>mqtt</code>).
|
||||
<code>exqx.containerk8s.dev.nubes.ru</code> — это MQTT-брокер, а не
|
||||
веб-страница. Точка подключения — только <code>/mqtt</code> через
|
||||
WebSocket. Если открыть этот адрес в браузере, вернётся
|
||||
<code>400</code> — так и должно быть: браузер не использует
|
||||
WebSocket-подпротокол <code>mqtt</code>. Подключаться нужно MQTT-
|
||||
клиентом (paho-mqtt, mqtt.js и т.п.), как в шаге 3.
|
||||
</p>
|
||||
<p style="color:#374151">
|
||||
Ограничения: размер сообщения — до ~250 КБ (лимит SQS 256 КБ);
|
||||
платформа может разрывать внешние WebSocket-соединения примерно раз
|
||||
в 150 секунд — устройства должны автоматически переподключаться
|
||||
(для QoS 1 повторы выполняются клиентом).
|
||||
Ограничения: сообщение — до ~250 КБ; платформа может разрывать
|
||||
внешние WebSocket-соединения примерно раз в 150 секунд — клиент
|
||||
должен уметь переподключаться (в paho это делается автоматически).
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user