Авторизация: User JWT и Device Token
User JWT
Для мобильного приложения. Заголовок:
Authorization: Bearer <access_token>
Используется для: /plants, /devices, /telemetry/latest, /relay/last, OTA actions и т.д.
Device Token
Для прошивки устройства. Токен выдаётся на POST /device/hello.
Сервер принимает 2 варианта заголовка:
Authorization: Device <device_token>
X-Device-Token: <device_token>
Формат токена обычно начинается с dev_ (генерируется как dev_ + token_urlsafe).
CORS
Ответы большинства device-эндпоинтов включают Access-Control-Allow-Origin: *.
Firmware (публичные эндпоинты)
Используется устройствами для проверки обновлений и скачивания .bin.
GET
/firmware/latest?device_model=hpfs-esp8266
Возвращает последнюю версию прошивки для модели (по semver), либо 204 если нет записей.
{
"deviceModel": "hpfs-esp8266",
"version": "1.2.4",
"url": "https://api.hpfs.ru/static/firmware/hpfs-esp8266/v1.2.4/hpfs-esp8266-v1.2.4.bin",
"sha256": "....",
"md5": "....",
"mandatory": false,
"changelog": "Исправления и улучшения",
"releasedAt": "2026-01-03T18:56:52"
}
Ошибки: 400 если не передан device_model. 204 если прошивок для модели нет.
GET
/firmware/history?device_model=hpfs-esp8266
История версий прошивки для модели.
[
{
"deviceModel": "hpfs-esp8266",
"version": "1.2.4",
"url": "https://api.hpfs.ru/static/firmware/hpfs-esp8266/v1.2.4/hpfs-esp8266-v1.2.4.bin",
"sha256": "...",
"md5": "...",
"mandatory": false,
"changelog": "....",
"releasedAt": "2026-01-03T18:56:52"
}
]
GET
/static/firmware/<path>
Скачивание файла прошивки (.bin). Используется полем url из /firmware/latest.
Пример:
GET /static/firmware/hpfs-esp8266/v1.2.4/hpfs-esp8266-v1.2.4.bin
Важно
Админ-панель публикации прошивки (/admin/*) не документируется публично.
Пользователь
JWT
Требуется Authorization: Bearer <user_jwt>.
POST
/me/push_token
Регистрация push-токена для уведомлений. platform по умолчанию ios.
{
"token": "apns_or_fcm_token_here",
"platform": "ios"
}
{
"status": "ok"
}
Если токен не передан: {"error":"token_required"}, HTTP 400.
DELETE
/account/delete
Удаление аккаунта пользователя и персональных данных. Требует JSON: {"password":"..."}. Возвращает 204 без тела.
{
"password": "your_password"
}
HTTP 204 No Content
Возможные ошибки: 400 (Password required), 401 (Wrong password / Invalid auth identity), 404 (User not found), 500 (internal).
Растения (Plants)
JWT
Все эндпоинты требуют Authorization: Bearer <user_jwt>.
GET
/plants
Список растений пользователя.
[
{
"id": 12,
"name": "Фикус",
"location": "Подоконник",
"avatar": 2,
"humidity": 40,
"temperature": 21
}
]
POST
/plants
Создать растение. Обязательные поля: name, location, avatar.
Необязательные: humidity, temperature, type/category, species, location_kind.
Если humidity/temperature не переданы — сервер попробует получить рекомендации (GigaChat), иначе использует safe defaults.
{
"name": "Фикус",
"location": "Подоконник",
"avatar": 2,
"type": "tropical",
"species": "Ficus elastica",
"location_kind": "indoor"
}
{
"id": 12,
"name": "Фикус",
"type": "tropical",
"species": "Ficus elastica",
"location": "Подоконник",
"comfort": {
"soil_temp_c": 22,
"soil_moisture_pct": 60
}
}
Валидация: name 1..12 символов, avatar 0..4, humidity 0..100, temperature -5..45.
DELETE
/plants/<plant_id>
Удалить растение пользователя.
{
"ok": true
}
422
Validation
Ошибка формата данных (422), обработчик возвращает общий текст.
{
"error": "Неверные данные (422)"
}
Авторизация (Auth)
JWT заголовок
Authorization: Bearer <access_token>
Access TTL
HPFS_ACCESS_TTL_MIN (по умолчанию 43200 = 30 дней)
Refresh TTL
HPFS_REFRESH_TTL_DAYS (по умолчанию 60 дней)
POST
/auth/register/start
Начало регистрации: создаёт/обновляет пользователя (если email ещё не подтверждён) и отправляет 6-значный код на почту.
Код живёт 15 минут.
{
"email": "user@example.com",
"password": "password123"
}
{
"ok": true
}
Ошибки: 400 (некорректный email / слабый пароль), 409 (email уже подтверждён/занят), 502 (не удалось отправить письмо).
POST
/auth/register/verify
Подтверждение кода (6 цифр). При успехе возвращает access и refresh.
Есть ограничение попыток: максимум 5, затем нужно запросить новый код.
{
"email": "user@example.com",
"code": "123456"
}
{
"access": "eyJhbGciOiJIUzI1NiIs...",
"refresh": "eyJhbGciOiJIUzI1NiIs..."
}
Ошибки: 400 (код неверный/истёк), 404 (пользователь не найден), 429 (превышено число попыток).
POST
/auth/resend
Повторно отправить код подтверждения, если email ещё не подтверждён.
{
"email": "user@example.com"
}
{
"ok": true
}
Ошибки: 400 (email некорректный), 404 (пользователь не найден), 409 (email уже подтверждён), 502 (почта недоступна).
POST
/auth/login
Логин по email+паролю. Требует подтверждённую почту.
{
"email": "user@example.com",
"password": "password123"
}
{
"access": "eyJhbGciOiJIUzI1NiIs...",
"refresh": "eyJhbGciOiJIUzI1NiIs..."
}
Ошибки: 400 (некорректные данные), 401 (неверная почта/пароль), 403 (почта не подтверждена).
POST
/auth/refresh
Ротация токенов: принимает refresh и возвращает новый access + новый refresh.
{
"refresh": "eyJhbGciOiJIUzI1NiIs..."
}
{
"access": "eyJhbGciOiJIUzI1NiIs...",
"refresh": "eyJhbGciOiJIUzI1NiIs..."
}
Ошибки: 400 (refresh token required), 401 (expired/invalid token, wrong type), 404 (user not found).
JWT ошибки (flask_jwt_extended)
- 401: нет токена / токен истёк (
{"error":"Token expired"})
- 422: токен некорректный (
{"error":"<reason>"})
Claims (привязка устройства)
POST
/claims/new
Создать 6-значный код для привязки устройства.
{
"user_id": 1
}
{
"code": "785931",
"ttl_sec": 300
}
GET
/claims/status?code=785
Проверка статуса claim-кода.
{
"status": "pending",
"device_id": null
}
GET
/claims/<code>
JWT
Проверка статуса claim-кода (с JWT пользователя).
Прошивка: handshake и online
POST
/device/hello
Device
Устройство регистрируется и получает deviceToken. Поддерживает device_id или claim/mdns.
{
"device_id": "hpfs-010101",
"fw": "1.2.3",
"claim": "785"
}
{
"deviceToken": "devtok_xxx",
"token": "devtok_xxx",
"device_id": "hpfs-010101",
"ownerAttached": false,
"owner_id": null
}
POST
/device/ping
Device
Пинг прошивки: обновляет версию fw и помечает устройство онлайн.
{
"device_id": "hpfs-010101",
"fw": "1.2.3"
}
{
"ok": true,
"device_id": "hpfs-010101"
}
GET
/devices/ping?device_id=hpfs-010101
Проверка online по last_seen.
{
"device_id": "hpfs-010101",
"online": true,
"last_seen_at": "2026-01-03T18:56:52Z"
}
Приложение: привязка устройства
POST
/devices/attach
JWT
Привязать устройство к пользователю (через claim или “hard”).
{
"claim": "785",
"plant_id": 12
}
{
"ok": true,
"mode": "claim",
"device": {
"device_id": "hpfs-010101",
"owner_id": 5,
"plant_id": 12,
"ownerAttached": true
}
}
GET
/devices
JWT
Список устройств пользователя.
GET
/devices/lookup?device_id=hpfs-010101
Публичный lookup устройства (возвращает ownerAttached).
Телеметрия
POST
/upload
Device
Устройство отправляет телеметрию. Сохраняются humidity, water, raw_json.
При переходе water в “пусто” отправляются email+push владельцу (если привязано).
{
"humidity": 42,
"water": {"present": true},
"ts": 1735920000
}
{
"ok": true
}
Авторизация: Authorization: Device <device_token>
GET
/telemetry/latest?device_id=hpfs-010101
JWT
Последняя телеметрия для приложения.
{
"ts": 1735920000,
"humidity": 42,
"temperature": null,
"water": "yes",
"online": true
}
Команды устройству (relay queue)
GET
/relay/pull?since=-1
Device
Устройство забирает следующую команду. Если команд нет — 204.
Если state хранится как JSON-строка — сервер вернёт распарсенный dict.
{
"version": 12,
"type": "relay",
"state": "on"
}
GET
/relay/last?device_id=hpfs-010101
JWT
Последнее состояние реле для UI.
{
"state": "on",
"version": 12,
"updated_at": 1735920000
}
POST
/devices/<device_id>/actions/relay
JWT
Пользователь включает/выключает реле (команда кладётся в очередь).
{
"state": "on"
}
POST
/devices/<device_id>/actions/led
JWT
Установить яркость LED (0..255), команда уходит в очередь.
{
"brightness": 180
}
POST
/devices/<device_id>/calibration/soil
JWT
Калибровка датчика почвы (wet/dry), сохраняется и кладётся команда устройству.
{
"point": "wet",
"value": 620
}
{
"ok": true,
"device_id": "hpfs-010101",
"point": "wet",
"value": 620,
"queued_version": 13
}
OTA обновление прошивки
POST
/devices/<device_id>/actions/ota
JWT
Поставить задачу OTA (target_version обязателен). Возвращает 202.
{
"target_version": "1.2.4"
}
{
"ok": true
}
GET
/devices/<device_id>/ota_status
JWT
Статус OTA для UI (idle/downloading/installing/rebooting/success/failed).
{
"state": "downloading",
"progress": 0.3,
"current_version": null
}
POST
/device/ota_event
Device
Устройство присылает событие OTA: installing/rebooting/failed/success.
{
"phase": "success",
"target_version": "1.2.4"
}
GET
/ota/pull
Device
Устройство получает “пинок” проверить обновление. Если нечего — 204.
{
"action": "check",
"target": "1.2.4"
}