HPFS API

Документация эндпоинтов · примеры запросов · ошибки

Base URL: https://api.hpfs.ru

Быстрый старт

Авторизация
User JWT: Authorization: Bearer <user_jwt>
Device token: Authorization: Device <device_token> (или X-Device-Token)
Заголовки
Authorization: Bearer <token>
Content-Type: application/json
Формат
JSON (UTF-8)
curl -X GET "https://api.hpfs.ru/health"

Авторизация: 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>"})

Устройства

GET /devices
Список устройств пользователя (пример-заглушка).
[
  {
    "id": "hpfs-001",
    "name": "Фикус",
    "online": true,
    "last_update": "2026-01-03T18:00:00Z"
  }
]

Телеметрия

GET /telemetry/latest?device_id=hpfs-001
Последние показания (пример-заглушка).
{
  "soil_moisture": 42,
  "temperature": 23.5,
  "air_humidity": 55
}

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

Ошибки

401 Unauthorized
Не передан токен или токен неверный.
{
  "error": "unauthorized"
}
404 Not Found
Эндпоинт не существует.
{
  "error": "not_found"
}