from contextlib import asynccontextmanager from collections.abc import AsyncIterator from pathlib import Path from typing import cast from fastapi import FastAPI from fastapi.responses import FileResponse from fastapi.staticfiles import StaticFiles from app.api.v1.entities import router as entities_router from app.api.v1.automations import router as automations_router from app.automations.store import AutomationStore from app.config import load_settings from app.core.exception_handlers import register_exception_handlers from app.ha.client import HaClient, HaClientSettings from app.ha.reader import HaReader from app.ml.registry.model_registry import ModelRegistry from backend.routes.ml import init_ml_routes @asynccontextmanager async def lifespan(app: FastAPI) -> AsyncIterator[None]: settings = app.state.settings client: HaClient | None = None app.state.registry = ModelRegistry(settings.model_store) app.state.automation_store = AutomationStore(settings.automation_store) if hasattr(app.state, "ha_reader"): del app.state.ha_reader if settings.ha_configured: client = HaClient( settings=HaClientSettings( url=cast(str, settings.ha_url), token=cast(str, settings.ha_token), ) ) app.state.ha_reader = HaReader(client=client) try: yield finally: if client is not None: client.close() app = FastAPI( title="SillyHome Next API", description="Lokales Smart-Home-Intelligenzsystem für Home Assistant.", version="0.3.0", lifespan=lifespan, ) app.state.settings = load_settings() register_exception_handlers(app) app.include_router(entities_router) app.include_router(automations_router) init_ml_routes(app, model_store=app.state.settings.model_store) STATIC_DIR = Path(__file__).with_name("static") app.mount("/static", StaticFiles(directory=STATIC_DIR), name="static") @app.get("/health") def health() -> dict[str, str]: return {"status": "ok"} @app.get("/") def root() -> FileResponse: return FileResponse(STATIC_DIR / "index.html")