Files
ignis-core/main.py
Артём Кокос c7adc24b07 Initial commit
2026-02-12 21:52:24 +07:00

140 lines
4.5 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 app.core.discovery import DiscoveryService
from app.core.state import state_manager
from app.drivers.wiz import WizDriver
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):
"""
Жизненный цикл приложения: выполняется при старте и остановке.
"""
# Задача для периодического сканирования сети
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}")
# Интервал сканирования (например, раз в 10 минут)
await asyncio.sleep(600)
discovery_task = asyncio.create_task(periodic_discovery())
yield
# Логика при завершении работы сервера
discovery_task.cancel()
logger.info("🛑 Фоновая служба Discovery остановлена")
app = FastAPI(
title="Ignis Core API",
description="Ядро системы умного дома для управления лампами WiZ/Gauss",
version="1.0.0",
lifespan=lifespan,
)
@app.get("/health")
async def health():
"""Проверка работоспособности сервиса."""
return {"status": "online", "devices_discovered": len(state_manager.devices)}
@app.get("/devices")
async def get_all_devices():
"""Возвращает список всех обнаруженных устройств из стейта."""
return state_manager.devices
@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,
):
"""
Управление группой ламп (люстрой).
Параметры передаются в JSON или как query-params.
"""
# 1. Получаем IP-адреса всех ламп в группе из стейт-менеджера
ips = state_manager.get_group_ips(group_id)
if not ips:
raise HTTPException(
status_code=404, detail=f"Группа '{group_id}' не найдена или пуста"
)
# 2. Формируем параметры команды для WiZ
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="Не указаны параметры управления (state, brightness или scene)",
)
# 3. Отправляем команды всем лампам группы параллельно
tasks = [wiz.set_pilot(ip, params) for ip in ips]
results = await asyncio.gather(*tasks, return_exceptions=True)
return {
"group_id": group_id,
"affected_ips": ips,
"status": "commands_sent",
"params": params,
}
@app.post("/control/device/{device_id}")
async def control_device(device_id: str, state: bool):
"""Прямое управление устройством по его ID (MAC-адресу)."""
device = state_manager.devices.get(device_id)
if not device:
raise HTTPException(
status_code=404, detail="Устройство не найдено в текущем стейте"
)
result = await wiz.set_pilot(device.ip, {"state": state})
return {"device_id": device_id, "ip": device.ip, "result": result}
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8000)