9.0 KiB
9.0 KiB
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.
Быстрый старт
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
python-dotenv подхватывает .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
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 /devicesGET /devices/groupsGET /devices/scenesPOST /devices/groupsDELETE /devices/groups/{group_id}POST /devices/rescanPOST /control/device/{device_id}POST /control/group/{group_id}POST /control/device/{device_id}/blinkGET /control/device/{device_id}/statusGET /control/group/{group_id}/statusPOST /schedules/oncePOST /schedules/cronGET /schedules/tasksDELETE /schedules/{job_id}GET /api-keysPOST /api-keysPOST /api-keys/revokePOST /api-keys/activateGET /stats/summaryGET /stats/logGET /system/info
Ролевые ограничения:
guest: чтение устройств/групп/сцен, управление светом,status,blink, безопасныйsystem/info.admin: всё выше плюс создание/удаление групп,rescan, расписания, stats/log, расширенныйsystem/info.master: всё выше плюс управление API-ключами.
Формат основных тел:
{"state": true}
{"brightness": 60}
{"temp": 3200}
{"r": 255, "g": 180, "b": 120}
{"scene": "Cozy"}
Валидация:
brightness:10..100temp:2200..6500r/g/b:0..255- можно передать только один режим из
scene,tempилиrgb r/g/bнужно передавать полной тройкой- для
POST /schedules/onceнужно передать ровно одно изrun_atилиhours_from_now
Примеры:
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}'
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-таблицы:
groupsapi_keysevent_logschedulesapscheduler_jobsdevices
Важно:
- онлайн-устройства и статусы живут в runtime-памяти процесса;
- таблица
devicesпока не является полноценным source of truth; - миграций схемы нет, используется
Base.metadata.create_all().
OpenAPI
Актуальная схема закоммичена в openapi.json.
Перегенерация:
.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")'
Тесты
Основная команда:
timeout 120s .venv/bin/python -m unittest discover -s tests -v
Дополнительно:
.venv/bin/python -m compileall app tests main.py
node --check static/app.js
Ограничения
- discovery всё ещё сетевой перебор внутри выбранных подсетей;
- runtime-state устройств живёт в памяти процесса;
- UI монолитный, без отдельного frontend-build step;
- stats — это аудит и summary, а не полноценная аналитика.