Files
GrigoandClaude Opus 5.5 ec84133417 rest api: accept Basic Auth with the panel password for writes
Any login, password = web/settings password (or master password). 401
carries WWW-Authenticate so browsers/Postman prompt. X-Api-Key still
works for existing integrations. Docs warn that over plain HTTP the
password travels in every request.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-10 10:58:05 +00:00

12 KiB
Raw Permalink Blame History

REST API ButtonTask /api/v1

Базовый адрес — тот же, что у веб-конфигуратора (по умолчанию порт 8080). Отдельного процесса нет.

Чтение открыто. Запись требует авторизацию, подходит любой из двух способов:

  • Basic Auth с паролем веб-панели (основной способ). Логин любой, например admin; пароль тот же, что для входа в конфигуратор и настройки на экране (подходит и мастер-пароль). Сменили пароль в панели — меняется и пароль для API.

    curl -u admin:ПАРОЛЬ -X POST http://<ip>:8080/api/v1/buttons/<id>/press
    
  • Заголовок X-Api-Key (оставлен для уже настроенных интеграций). Ключ лежит в settings.apiKey и показывается на вкладке «Безопасность» конфигуратора.

Устройство работает по HTTP, поэтому пароль в Basic Auth передаётся по сети открытым текстом в каждом запросе (так же, как при входе в веб-панель). Кто перехватит трафик, получит пароль администратора, а с ним и OTA, то есть root на устройстве. Ключ API при утечке даёт меньше: только методы записи API. В доверенной сети это приемлемо; если интеграция ходит через чужую сеть, лучше ключ.

Без авторизации или с неверными данными — HTTP 401 с заголовком WWW-Authenticate: Basic:

{ "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.

{
  "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

Авторизация не требуется.

Интервал отчёта, таймаут устаревания и карточки известных датчиков. Пустых слотов нет.

{
  "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.

{
  "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

Нужна авторизация (Basic или X-Api-Key, см. начало).

{ "name": "Резервуар_1" }

Пустая строка сбрасывает имя. name не строка — HTTP 400 invalid_name. Кривой MAC — HTTP 400 invalid_mac.

{ "ok": true, "mac": "AA:BB:CC:DD:EE:FF", "name": "Резервуар_1" }

GET /api/v1/settings

Авторизация не требуется.

{
  "report_interval_min": 30,
  "stale_timeout_s": 5400
}

PUT /api/v1/settings

Нужна авторизация (Basic или X-Api-Key, см. начало).

Принимается только report_interval_min, целое от 1 до 65535. Иначе HTTP 400 invalid_interval. stale_timeout_s записать нельзя.

Ответ — та же пара полей, что у GET.

GET /api/v1/sensors/{mac}/calibration

Авторизация не требуется.

{ "empty_mm": 1200, "full_mm": 200 }

Границ ещё нет — оба поля null. Кривой MAC — HTTP 404 sensor_not_found.

PUT /api/v1/sensors/{mac}/calibration

Нужна авторизация (Basic или X-Api-Key, см. начало).

{ "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 и остальные поля кнопки не отдаются.

[
  { "id": "example-1", "label": "Пример" }
]

GET /api/v1/buttons/{id}

Нужна авторизация (Basic или X-Api-Key, см. начало).

Полная карточка кнопки: подпись, иконка, действие, тексты ответа, удержание и цвет. Неизвестный id — HTTP 404 button_not_found.

{
  "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

Нужна авторизация (Basic или 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 — созданная карточка.

{
  "label": "Пример",
  "actionType": "http_get",
  "url": "http://localhost/btn?bid=01",
  "triggerMode": "hold",
  "holdMs": 800,
  "color": "#7b007b"
}

PUT /api/v1/buttons/{id}

Нужна авторизация (Basic или X-Api-Key, см. начало).

Меняет только переданные поля. Имена полей те же, что у создания. Неизвестный id — HTTP 404 button_not_found. Некорректное поле — HTTP 400 invalid_button. Ответ — карточка после записи. Экран подхватывает конфиг сам.

DELETE /api/v1/buttons/{id}

Нужна авторизация (Basic или X-Api-Key, см. начало).

Удаляет кнопку. Неизвестный id — HTTP 404 button_not_found.

{ "ok": true }

POST /api/v1/buttons/reorder

Нужна авторизация (Basic или X-Api-Key, см. начало).

Порядок кнопок на экране. В order — идентификаторы спереди назад. Пропущенные остаются в конце в прежнем порядке. order не массив строк — HTTP 400 invalid_order.

{ "order": ["example-1", "cbd6791cdcb74a58861554b30239c65e"] }

Ответ:

{ "ok": true, "order": ["example-1", "cbd6791cdcb74a58861554b30239c65e"] }

GET /api/v1/background

Авторизация не требуется.

{
  "type": "gradient",
  "color1": "#00007b",
  "color2": "#7b007b",
  "animated": true,
  "imagePath": ""
}

type: gradient, solid или image.

PUT /api/v1/background

Нужна авторизация (Basic или X-Api-Key, см. начало).

Меняет только переданные поля. Цвет — #rrggbb. animated — логическое. Иначе HTTP 400 invalid_background. Ответ — фон после записи.

{
  "type": "gradient",
  "color1": "#00007b",
  "color2": "#7b007b",
  "animated": true
}

POST /api/v1/buttons/{id}/press

Нужна авторизация (Basic или X-Api-Key, см. начало).

То же действие, что нажатие на экране, включая защёлку. Тело не нужно.

Ответ HTTP 202 значит, что команда принята экраном, а не что внешний HTTP-запрос кнопки уже завершился:

{ "ok": true, "id": "example-1" }

Неизвестный id — HTTP 404 button_not_found. Повторная команда, пока экран не забрал предыдущую, заменяет её.

POST /api/v1/system/reboot

Нужна авторизация (Basic или X-Api-Key, см. начало).

Перезагрузка устройства тем же путём, что кнопка в конфигураторе. Ответ уходит до того, как машина погаснет.

{ "ok": true }