Files
ignis-core/main.py
Артём Кокос 738edd4db9 fulREST ready
2026-02-12 22:21:38 +07:00

155 lines
5.0 KiB
Python
Raw 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.
import asyncio
import logging
from contextlib import asynccontextmanager
from typing import Optional, List
from fastapi import FastAPI, HTTPException
from sqlalchemy import select
from app.core.discovery import DiscoveryService
from app.core.state import state_manager
from app.drivers.wiz import WizDriver
from app.core.database import init_db, async_session
from app.models.device import GroupModel, DeviceModel, GroupCreateSchema
logging.basicConfig(
level=logging.INFO, format="%(asctime)s | %(levelname)s | %(message)s"
)
logger = logging.getLogger(__name__)
discovery = DiscoveryService()
wiz = WizDriver()
@asynccontextmanager
async def lifespan(app: FastAPI):
"""Жизненный цикл приложения."""
# 1. Инициализация БД (создание таблиц)
await init_db()
logger.info("🗄️ База данных инициализирована")
# 2. Загрузка групп из БД в память
async with async_session() as session:
result = await session.execute(select(GroupModel))
groups = result.scalars().all()
for g in groups:
# Превращаем модель БД в формат стейта (если нужно)
state_manager.groups[g.id] = g
logger.info(f"📂 Загружена группа: {g.name} (MACs: {g.device_ids})")
# 3. Задача для периодического сканирования сети
async def periodic_discovery():
logger.info("🚀 Запущена фоновая служба Discovery")
while True:
try:
found_devices = await discovery.scan_network()
for dev_data in found_devices:
state_manager.update_device(dev_data)
logger.info(
f"📡 Сеть просканирована. Устройств онлайн: {len(state_manager.devices)}"
)
except Exception as e:
logger.error(f"❌ Ошибка в цикле Discovery: {e}")
await asyncio.sleep(600)
discovery_task = asyncio.create_task(periodic_discovery())
yield
discovery_task.cancel()
logger.info("🛑 Фоновая служба Discovery остановлена")
app = FastAPI(title="Ignis Core API", lifespan=lifespan)
# --- Эндпоинты Устройств ---
@app.get("/devices")
async def get_all_devices():
"""Список всех обнаруженных ламп."""
return state_manager.devices
@app.post("/control/device/{device_id}")
async def control_device(device_id: str, state: bool, brightness: Optional[int] = None):
"""Прямое управление лампой по MAC."""
device = state_manager.devices.get(device_id)
if not device:
raise HTTPException(status_code=404, detail="Лампа не в сети")
params = {"state": state}
if brightness:
params["dimming"] = brightness
result = await wiz.set_pilot(device.ip, params)
return {"device_id": device_id, "result": result}
# --- Эндпоинты Групп ---
@app.get("/groups")
async def get_groups():
"""Список всех созданных люстр/групп."""
return state_manager.groups
@app.post("/groups")
async def create_group(data: GroupCreateSchema):
async with async_session() as session:
existing = await session.get(GroupModel, data.id)
if existing:
raise HTTPException(status_code=400, detail="Группа с таким ID уже есть")
# Берем данные из схемы
new_group = GroupModel(id=data.id, name=data.name, device_ids=data.macs)
session.add(new_group)
await session.commit()
state_manager.groups[data.id] = new_group
return {"status": "created", "group": data.name}
@app.post("/control/group/{group_id}")
async def control_group(
group_id: str,
state: Optional[bool] = None,
brightness: Optional[int] = None,
scene: Optional[str] = None,
):
"""Управление всей люстрой сразу."""
ips = state_manager.get_group_ips(group_id)
if not ips:
raise HTTPException(
status_code=404, detail="Группа не найдена или лампы оффлайн"
)
params = {}
if state is not None:
params["state"] = state
if brightness is not None:
params["dimming"] = brightness
if scene and scene in wiz.SCENES:
params["sceneId"] = wiz.SCENES[scene]
if not params:
raise HTTPException(status_code=400, detail="Никаких команд не передано")
# Параллельная отправка всем лампам группы
tasks = [wiz.set_pilot(ip, params) for ip in ips]
await asyncio.gather(*tasks)
return {"status": "ok", "group": group_id, "sent_to": ips}
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8000)