Files
ignis-core/README.md
2026-05-21 22:22:47 +07:00

246 lines
9.0 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Ignis Core
Локальный FastAPI-сервер для WiZ-ламп: discovery, управление, группы, расписания, API-ключи, аудит и встроенная веб-морда.
## Что умеет
- искать лампы в локальной сети при старте, вручную и в фоне;
- управлять лампой или группой;
- мигать лампой (`blink`) и читать живой статус;
- хранить группы, ключи, события и расписания в SQLite;
- выполнять `one-shot` и `cron`-задачи через APScheduler;
- разделять доступ на `master`, `admin`, `guest`;
- отдавать встроенный web UI из `static/`;
- публиковать OpenAPI.
## Структура
- `main.py` — FastAPI, middleware, lifecycle, статика.
- `app/api/routes/` — HTTP API.
- `app/core/discovery.py` — discovery и выбор подсетей.
- `app/core/scheduler.py` — расписания и reconciliation.
- `app/core/state.py` — runtime-state устройств и групп.
- `app/models/` — SQLAlchemy и схемы данных.
- `static/` — встроенный UI.
- `deploy/``systemd`-деплой и пример env.
## Быстрый старт
```bash
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
cp deploy/ignis-core.env.example .env
.venv/bin/python -m uvicorn main:app --host 0.0.0.0 --port 8000
```
- UI: `http://127.0.0.1:8000/`
- OpenAPI runtime: `http://127.0.0.1:8000/openapi.json`
- Коммитнутый экспорт: [openapi.json](openapi.json)
`python-dotenv` подхватывает `.env` автоматически.
## Конфиг
Минимум:
```env
IGNIS_API_KEY=change-me
IGNIS_INSTANCE_NAME=Home
APP_TIMEZONE=Asia/Novosibirsk
SCAN_NETWORK=192.168.0.0/24
IGNIS_DATABASE_URL=sqlite+aiosqlite:///./ignis.db
IGNIS_SYNC_DATABASE_URL=sqlite:///./ignis.db
```
Основные переменные:
- `IGNIS_API_KEY` — мастер-ключ. Без него сервер работает в `fail-closed` и защищённые маршруты отвечают `503`.
- `IGNIS_INSTANCE_NAME` — имя инстанса в UI и `GET /system/info`.
- `APP_TIMEZONE` — таймзона расписаний.
- `SCAN_NETWORK` — подсеть или список подсетей для discovery. На хостах с VPN или несколькими NIC лучше задавать явно.
- `DISCOVERY_INTERVAL_SECONDS` — период фонового refresh.
- `DISCOVERY_BACKGROUND_MISSING_THRESHOLD` — сколько циклов подряд лампа может не отвечать до удаления.
- `DISCOVERY_ENV_MIN_PREFIX_LEN` — минимально допустимая маска для `SCAN_NETWORK`.
- `DISCOVERY_AUTO_MIN_PREFIX_LEN` — минимально допустимая маска для auto-discovery.
- `EVENT_LOG_RETENTION_DAYS` — срок хранения event log.
- `IGNIS_PUBLIC_BASE_URL` — внешний URL, если сервер стоит за reverse proxy.
- `IGNIS_BUILD_VERSION`, `IGNIS_BUILD_DATE`, `IGNIS_GIT_SHA` — build metadata для диагностики.
Полный пример: [deploy/ignis-core.env.example](deploy/ignis-core.env.example)
## Discovery
- при старте выполняется `startup_refresh()`;
- `POST /devices/rescan` делает ручной refresh и сразу убирает оффлайн-устройства;
- фоновый refresh удаляет устройство только после `DISCOVERY_BACKGROUND_MISSING_THRESHOLD` подряд промахов;
- если `SCAN_NETWORK` пуст, сервер сам выбирает private IPv4 подсети и старается не лезть в `docker`, `tun`, `wg`, `tailscale` и похожие интерфейсы.
## Роли
Заголовок авторизации: `X-API-Key`
- `master` — значение `IGNIS_API_KEY`, полный доступ.
- `admin` — ключ из БД с `is_admin=true`, доступ к группам, рескану, расписаниям, stats и расширенному `system/info`.
- `guest` — управление светом и чтение безопасной части API.
`GET /auth/me` возвращает текущую роль и имя ключа.
## HTTP API
Основные группы маршрутов:
- `GET /devices`
- `GET /devices/groups`
- `GET /devices/scenes`
- `POST /devices/groups`
- `DELETE /devices/groups/{group_id}`
- `POST /devices/rescan`
- `POST /control/device/{device_id}`
- `POST /control/group/{group_id}`
- `POST /control/device/{device_id}/blink`
- `GET /control/device/{device_id}/status`
- `GET /control/group/{group_id}/status`
- `POST /schedules/once`
- `POST /schedules/cron`
- `GET /schedules/tasks`
- `DELETE /schedules/{job_id}`
- `GET /api-keys`
- `POST /api-keys`
- `POST /api-keys/revoke`
- `POST /api-keys/activate`
- `GET /stats/summary`
- `GET /stats/log`
- `GET /system/info`
Ролевые ограничения:
- `guest`: чтение устройств/групп/сцен, управление светом, `status`, `blink`, безопасный `system/info`.
- `admin`: всё выше плюс создание/удаление групп, `rescan`, расписания, stats/log, расширенный `system/info`.
- `master`: всё выше плюс управление API-ключами.
Формат основных тел:
```json
{"state": true}
```
```json
{"brightness": 60}
```
```json
{"temp": 3200}
```
```json
{"r": 255, "g": 180, "b": 120}
```
```json
{"scene": "Cozy"}
```
Валидация:
- `brightness`: `10..100`
- `temp`: `2200..6500`
- `r/g/b`: `0..255`
- можно передать только один режим из `scene`, `temp` или `rgb`
- `r/g/b` нужно передавать полной тройкой
- для `POST /schedules/once` нужно передать ровно одно из `run_at` или `hours_from_now`
Примеры:
```bash
curl -sS http://127.0.0.1:8000/control/group/bedroom \
-H 'X-API-Key: change-me' \
-H 'Content-Type: application/json' \
-d '{"state":true,"brightness":60}'
```
```bash
curl -sS http://127.0.0.1:8000/api-keys \
-H 'X-API-Key: change-me'
```
Замечания по ключам:
- `GET /api-keys` возвращает публичные `key_id`, а не полный секрет;
- `POST /api-keys` показывает полный секрет только один раз;
- `POST /api-keys/revoke` и `POST /api-keys/activate` принимают JSON вида `{"key":"<secret-or-public-id>"}`.
## Встроенный UI
UI лежит в `static/` и не требует отдельной сборки.
Что есть сейчас:
- вход по API-ключу;
- роль-зависимые вкладки;
- пульт групп со сценами, яркостью, температурой, цветом и таймером на 4 часа;
- список устройств и сборка групп;
- one-shot и cron;
- серверная вкладка с метаданными инстанса;
- аудит, stats и API-ключи для нужных ролей.
Свойства UI:
- использует только локальные ассеты;
- не использует `localStorage`;
- может хранить ключ только в `sessionStorage` текущей вкладки;
- гость не видит чувствительные поля `system/info`.
## Хранилище
SQLite-таблицы:
- `groups`
- `api_keys`
- `event_log`
- `schedules`
- `apscheduler_jobs`
- `devices`
Важно:
- онлайн-устройства и статусы живут в runtime-памяти процесса;
- таблица `devices` пока не является полноценным source of truth;
- миграций схемы нет, используется `Base.metadata.create_all()`.
## OpenAPI
Актуальная схема закоммичена в `openapi.json`.
Перегенерация:
```bash
.venv/bin/python -c 'import json, os, pathlib; os.environ.setdefault("IGNIS_API_KEY", "openapi-export"); from main import app; pathlib.Path("openapi.json").write_text(json.dumps(app.openapi(), ensure_ascii=False, indent=2) + "\n", encoding="utf-8")'
```
## Тесты
Основная команда:
```bash
timeout 120s .venv/bin/python -m unittest discover -s tests -v
```
Дополнительно:
```bash
.venv/bin/python -m compileall app tests main.py
node --check static/app.js
```
## Ограничения
- discovery всё ещё сетевой перебор внутри выбранных подсетей;
- runtime-state устройств живёт в памяти процесса;
- UI монолитный, без отдельного frontend-build step;
- stats — это аудит и summary, а не полноценная аналитика.
## License
[MIT](LICENSE)