310 lines
10 KiB
Markdown
310 lines
10 KiB
Markdown
# REST API ButtonTask `/api/v1`
|
||
|
||
Базовый адрес — тот же, что у веб-конфигуратора (по умолчанию порт 8080). Отдельного процесса нет.
|
||
|
||
Чтение открыто. Запись требует заголовок `X-Api-Key`. Ключ лежит в `settings.apiKey`, создаётся сам при первом запуске и показывается на вкладке «Безопасность» конфигуратора. Запроса «отдать ключ» в этом API нет: его увидел бы любой в сети. Неверный или пустой ключ — HTTP 401:
|
||
|
||
```json
|
||
{ "ok": false, "error": "unauthorized" }
|
||
```
|
||
|
||
Тело ошибок всегда `{"ok": false, "error": "<код>"}`.
|
||
|
||
Калибровка в файле хранится как `min` / `max`. В API это `empty_mm` (0%) и `full_mm` (100%). Уровень: `(raw_mm - empty_mm) / (full_mm - empty_mm) * 100`, зажим 0…100, один знак после запятой. Нет границ или они равны — `level_pct` равен `null`.
|
||
|
||
Байт питания — десятые вольта. `voltage_mv = байт * 100`. Байт `255` — нет данных, тогда `voltage_mv` и `charge_pct` равны `null`. `charge_pct` — оценка остатка двух дешёвых AA по кривой разряда.
|
||
|
||
`stale_timeout_s` только для чтения: `report_interval_min * 60 * 3`. Для 30 минут это 5400.
|
||
|
||
## GET /api/v1/health
|
||
|
||
Авторизация не требуется.
|
||
|
||
Сводка коллектора, его аптайм, метрики хоста и счётчики датчиков. `ok` и `status: "healthy"` — файл статуса коллектора свежий (не старше 15 секунд). Иначе `status: "degraded"`, HTTP всё равно 200.
|
||
|
||
```json
|
||
{
|
||
"ok": true,
|
||
"status": "healthy",
|
||
"uptime_s": 86400.1,
|
||
"version": "1.2.3",
|
||
"ts": 1730000000.0,
|
||
"collector": {
|
||
"available": true,
|
||
"sensors_live": 2,
|
||
"sensors_stale": 1,
|
||
"sensors_total": 3
|
||
},
|
||
"system": {
|
||
"cpu_percent": 12.5,
|
||
"memory_used_percent": 41.0,
|
||
"load_1m": 0.42
|
||
}
|
||
}
|
||
```
|
||
|
||
`uptime_s` — секунды с старта процесса коллектора. Если метки старта нет, поле `null`.
|
||
|
||
## GET /api/v1/sensors
|
||
|
||
Авторизация не требуется.
|
||
|
||
Интервал отчёта, таймаут устаревания и карточки известных датчиков. Пустых слотов нет.
|
||
|
||
```json
|
||
{
|
||
"report_interval_min": 30,
|
||
"stale_timeout_s": 5400,
|
||
"sensors": [
|
||
{
|
||
"mac": "AA:BB:CC:DD:EE:FF",
|
||
"name": "Резервуар_1",
|
||
"raw_mm": 420,
|
||
"level_pct": 63.5,
|
||
"voltage_mv": 2800,
|
||
"charge_pct": 72,
|
||
"age_s": 42,
|
||
"state": "live",
|
||
"rssi": -67,
|
||
"last_seen": 1730000000.0
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
`raw_mm` равен `null`, если датчик не видит цель. `state` — `live` или `stale`.
|
||
|
||
## GET /api/v1/sensors/{mac}
|
||
|
||
Авторизация не требуется.
|
||
|
||
Карточка одного датчика, тот же объект, что элемент `sensors` выше. Неизвестный адрес или кривой MAC — HTTP 404 `sensor_not_found`.
|
||
|
||
## GET /api/v1/sensors/{mac}/history
|
||
|
||
Авторизация не требуется.
|
||
|
||
Параметры:
|
||
|
||
- `limit` — от 1 до 1000, по умолчанию 100. Иначе HTTP 400 `invalid_limit`.
|
||
- `since` — необязательный Unix timestamp, остаются точки с `ts >= since`. Нечисло — HTTP 400 `invalid_since`.
|
||
|
||
Кольцо на диске `run/sensors-history.json`, не больше 1000 точек на датчик. Ответ от новых к старым. Проценты считаются по текущей калибровке, а не по той, что была в момент замера.
|
||
|
||
Неизвестный датчик — HTTP 404 `sensor_not_found`. Известный датчик без точек — пустой `entries`.
|
||
|
||
```json
|
||
{
|
||
"mac": "AA:BB:CC:DD:EE:FF",
|
||
"entries": [
|
||
{
|
||
"ts": 1730000000.0,
|
||
"raw_mm": 420,
|
||
"level_pct": 63.5,
|
||
"voltage_mv": 2800,
|
||
"charge_pct": 72,
|
||
"rssi": -67
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
## PUT /api/v1/sensors/{mac}/name
|
||
|
||
Заголовок `X-Api-Key` обязателен.
|
||
|
||
```json
|
||
{ "name": "Резервуар_1" }
|
||
```
|
||
|
||
Пустая строка сбрасывает имя. `name` не строка — HTTP 400 `invalid_name`. Кривой MAC — HTTP 400 `invalid_mac`.
|
||
|
||
```json
|
||
{ "ok": true, "mac": "AA:BB:CC:DD:EE:FF", "name": "Резервуар_1" }
|
||
```
|
||
|
||
## GET /api/v1/settings
|
||
|
||
Авторизация не требуется.
|
||
|
||
```json
|
||
{
|
||
"report_interval_min": 30,
|
||
"stale_timeout_s": 5400
|
||
}
|
||
```
|
||
|
||
## PUT /api/v1/settings
|
||
|
||
Заголовок `X-Api-Key` обязателен.
|
||
|
||
Принимается только `report_interval_min`, целое от 1 до 65535. Иначе HTTP 400 `invalid_interval`. `stale_timeout_s` записать нельзя.
|
||
|
||
Ответ — та же пара полей, что у GET.
|
||
|
||
## GET /api/v1/sensors/{mac}/calibration
|
||
|
||
Авторизация не требуется.
|
||
|
||
```json
|
||
{ "empty_mm": 1200, "full_mm": 200 }
|
||
```
|
||
|
||
Границ ещё нет — оба поля `null`. Кривой MAC — HTTP 404 `sensor_not_found`.
|
||
|
||
## PUT /api/v1/sensors/{mac}/calibration
|
||
|
||
Заголовок `X-Api-Key` обязателен.
|
||
|
||
```json
|
||
{ "empty_mm": 1200, "full_mm": 200 }
|
||
```
|
||
|
||
Оба значения — целые миллиметры от 0 до 65535 и не равны друг другу. Иначе HTTP 400 `invalid_calibration`. Кривой MAC — HTTP 400 `invalid_mac`.
|
||
|
||
`empty_mm` — показание 0%, `full_mm` — показание 100%. Ответ повторяет записанные границы.
|
||
|
||
## GET /api/v1/buttons
|
||
|
||
Авторизация не требуется.
|
||
|
||
Только идентификатор и подпись. URL и остальные поля кнопки не отдаются.
|
||
|
||
```json
|
||
[
|
||
{ "id": "example-1", "label": "Пример" }
|
||
]
|
||
```
|
||
|
||
## GET /api/v1/buttons/{id}
|
||
|
||
Заголовок `X-Api-Key` обязателен.
|
||
|
||
Полная карточка кнопки: подпись, иконка, действие, тексты ответа, удержание и цвет. Неизвестный id — HTTP 404 `button_not_found`.
|
||
|
||
```json
|
||
{
|
||
"id": "example-1",
|
||
"label": "Пример",
|
||
"iconPath": "cleaner1.png",
|
||
"action": {
|
||
"type": "http_get",
|
||
"url": "http://localhost/btn?bid=01",
|
||
"headers": {},
|
||
"body": ""
|
||
},
|
||
"feedback": {
|
||
"successText": "Отправлено",
|
||
"errorText": "Ошибка",
|
||
"pendingText": "...",
|
||
"fadeMs": 5000
|
||
},
|
||
"trigger": { "mode": "hold", "holdMs": 800 },
|
||
"color": "#7b007b"
|
||
}
|
||
```
|
||
|
||
## POST /api/v1/buttons
|
||
|
||
Заголовок `X-Api-Key` обязателен.
|
||
|
||
Создаёт кнопку. Идентификатор назначается сам. Поля те же, что при правке, плюс плоские имена действия: `actionType` (`http_get` или `http_post`), `url`, `headers`, `body`, `timeoutMs` (1000…600000), `successText`, `errorText`, `pendingText`, `fadeMs`, `triggerMode` (`click` или `hold`), `holdMs`, `color`, `iconPath`, `latchReset`, `responseCheck`. Некорректное тело — HTTP 400 `invalid_button` и поле `message` с причиной. Ответ HTTP 201 — созданная карточка.
|
||
|
||
```json
|
||
{
|
||
"label": "Пример",
|
||
"actionType": "http_get",
|
||
"url": "http://localhost/btn?bid=01",
|
||
"triggerMode": "hold",
|
||
"holdMs": 800,
|
||
"color": "#7b007b"
|
||
}
|
||
```
|
||
|
||
## PUT /api/v1/buttons/{id}
|
||
|
||
Заголовок `X-Api-Key` обязателен.
|
||
|
||
Меняет только переданные поля. Имена полей те же, что у создания. Неизвестный id — HTTP 404 `button_not_found`. Некорректное поле — HTTP 400 `invalid_button`. Ответ — карточка после записи. Экран подхватывает конфиг сам.
|
||
|
||
## DELETE /api/v1/buttons/{id}
|
||
|
||
Заголовок `X-Api-Key` обязателен.
|
||
|
||
Удаляет кнопку. Неизвестный id — HTTP 404 `button_not_found`.
|
||
|
||
```json
|
||
{ "ok": true }
|
||
```
|
||
|
||
## POST /api/v1/buttons/reorder
|
||
|
||
Заголовок `X-Api-Key` обязателен.
|
||
|
||
Порядок кнопок на экране. В `order` — идентификаторы спереди назад. Пропущенные остаются в конце в прежнем порядке. `order` не массив строк — HTTP 400 `invalid_order`.
|
||
|
||
```json
|
||
{ "order": ["example-1", "cbd6791cdcb74a58861554b30239c65e"] }
|
||
```
|
||
|
||
Ответ:
|
||
|
||
```json
|
||
{ "ok": true, "order": ["example-1", "cbd6791cdcb74a58861554b30239c65e"] }
|
||
```
|
||
|
||
## GET /api/v1/background
|
||
|
||
Авторизация не требуется.
|
||
|
||
```json
|
||
{
|
||
"type": "gradient",
|
||
"color1": "#00007b",
|
||
"color2": "#7b007b",
|
||
"animated": true,
|
||
"imagePath": ""
|
||
}
|
||
```
|
||
|
||
`type`: `gradient`, `solid` или `image`.
|
||
|
||
## PUT /api/v1/background
|
||
|
||
Заголовок `X-Api-Key` обязателен.
|
||
|
||
Меняет только переданные поля. Цвет — `#rrggbb`. `animated` — логическое. Иначе HTTP 400 `invalid_background`. Ответ — фон после записи.
|
||
|
||
```json
|
||
{
|
||
"type": "gradient",
|
||
"color1": "#00007b",
|
||
"color2": "#7b007b",
|
||
"animated": true
|
||
}
|
||
```
|
||
|
||
## POST /api/v1/buttons/{id}/press
|
||
|
||
Заголовок `X-Api-Key` обязателен.
|
||
|
||
То же действие, что нажатие на экране, включая защёлку. Тело не нужно.
|
||
|
||
Ответ HTTP 202 значит, что команда принята экраном, а не что внешний HTTP-запрос кнопки уже завершился:
|
||
|
||
```json
|
||
{ "ok": true, "id": "example-1" }
|
||
```
|
||
|
||
Неизвестный id — HTTP 404 `button_not_found`. Повторная команда, пока экран не забрал предыдущую, заменяет её.
|
||
|
||
## POST /api/v1/system/reboot
|
||
|
||
Заголовок `X-Api-Key` обязателен.
|
||
|
||
Перезагрузка устройства тем же путём, что кнопка в конфигураторе. Ответ уходит до того, как машина погаснет.
|
||
|
||
```json
|
||
{ "ok": true }
|
||
```
|