# Ignis Core Локальный FastAPI-сервер для управления лампами WiZ. ## Что есть - discovery устройств в локальной сети - группы устройств - команды для device/group - one-shot и cron расписания - guest/admin/master API-ключи - event log и базовая статистика - встроенный UI в `static/index.html` ## Запуск ```bash python3 -m venv .venv source .venv/bin/activate pip install -r requirements.txt cp .env.example .env uvicorn main:app --host 0.0.0.0 --port 8000 ``` UI: `http://:8000/` ## Конфигурация Минимум: ```env IGNIS_API_KEY=change-me APP_TIMEZONE=Asia/Novosibirsk SCAN_NETWORK= DISCOVERY_INTERVAL_SECONDS=600 DISCOVERY_BACKGROUND_MISSING_THRESHOLD=2 LOG_LEVEL=INFO EVENT_LOG_RETENTION_DAYS=30 ``` БД: ```env IGNIS_DATABASE_URL=sqlite+aiosqlite:///./ignis.db IGNIS_SYNC_DATABASE_URL=sqlite:///./ignis.db ``` Замечание по discovery: - если `SCAN_NETWORK` не задан, сервер сам выбирает private IPv4-подсети обычных интерфейсов и старается не сканировать VPN / docker / tunnel-интерфейсы - если на хосте есть VPN или несколько интерфейсов, всё равно лучше явно задать `SCAN_NETWORK` - формат: `192.168.0.0/24` или список через запятую - startup scan выполняется до старта фонового цикла - background refresh по умолчанию удаляет устройство только после двух подряд промахов discovery - manual `POST /devices/rescan` удаляет оффлайн-устройства сразу и возвращает summary (`found`, `added`, `updated`, `removed_offline`, `pending_removal`, `online`) ## Авторизация Заголовок: `X-API-Key` Роли: - `master`: значение из `IGNIS_API_KEY`, полный доступ - `admin`: ключ из БД, доступ к группам, расписаниям, stats и rescan - `guest`: обычное управление и чтение Сервер работает в `fail-closed`: если `IGNIS_API_KEY` не задан, защищённые маршруты недоступны. ## 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 /auth/me` `control` и `schedules` принимают JSON body. Поддерживаемые параметры команд: - `state` - `brightness` - `scene` - `temp` - `r`, `g`, `b` Примеры: ```bash curl -X POST 'http://localhost:8000/control/device/dev-1' \ -H 'X-API-Key: change-me' \ -H 'Content-Type: application/json' \ -d '{"temp": 4200}' ``` ```bash curl -X POST 'http://localhost:8000/schedules/once' \ -H 'X-API-Key: change-me' \ -H 'Content-Type: application/json' \ -d '{"target_id":"bedroom","hours_from_now":2,"is_group":true,"temp":3200}' ``` Валидация: - `brightness`: `10..100` - `temp`: `2200..6500` - `r/g/b`: `0..255` - `scene`, `temp` и `rgb` взаимоисключаемы - `r`, `g`, `b` нужно передавать только полной тройкой - для `schedules/once` нужно передать ровно одно из `run_at` или `hours_from_now` ## API keys - список ключей возвращает публичный `key` / `key_id` - полный секрет возвращается только при создании - маршруты `/api-keys/*` доступны только `master` ## Хранилище Основные таблицы: - `groups` - `api_keys` - `event_log` - `schedules` - `apscheduler_jobs` ## 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 .venv/bin/python -m compileall app tests main.py ``` Полный прогон: ```bash timeout 120s .venv/bin/python -m unittest discover -s tests -v ``` Сейчас есть 25 тестов. Покрыты: - auth и роли - lifecycle API-ключей - control/status ошибки и partial success - валидация scene - one-shot и cron расписания - миграция legacy jobs - auto-subnet selection для discovery - background offline cleanup threshold - manual rescan summary и immediate cleanup - агрегация stats без двойного счёта `*_requested` ## Ограничения - discovery всё ещё основан на переборе IP по подсетям - UI остаётся монолитным файлом - stats пока простые и не заменяют нормальную аналитику