- Enrich server data with MQTT system topics (cpu_usage_percent, ram_usage_percent, temp, model etc.) published by Unraid MQTT Agent - Works for both Daddelolymp and Adriahub topics - MQTT overlay runs on every request (even cached) for fresh metrics - Remove JWT auth from /api/ha/control — local dashboard doesn't need it - Add cpu brand field to GraphQL query Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
81 lines
2.3 KiB
Python
81 lines
2.3 KiB
Python
"""Home Assistant data router — read states & control entities."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import logging
|
|
from typing import Any, Dict, Optional
|
|
|
|
from fastapi import APIRouter
|
|
from pydantic import BaseModel
|
|
|
|
from server.cache import cache
|
|
from server.config import get_settings
|
|
from server.services.ha_service import call_ha_service, fetch_ha_data
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
router = APIRouter(prefix="/api", tags=["homeassistant"])
|
|
|
|
CACHE_KEY = "ha"
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# GET /api/ha — read-only, no auth needed
|
|
# ---------------------------------------------------------------------------
|
|
|
|
@router.get("/ha")
|
|
async def get_ha() -> Dict[str, Any]:
|
|
"""Return Home Assistant entity data (cached)."""
|
|
|
|
cached = await cache.get(CACHE_KEY)
|
|
if cached is not None:
|
|
return cached
|
|
|
|
try:
|
|
data: Dict[str, Any] = await fetch_ha_data(
|
|
get_settings().ha_url,
|
|
get_settings().ha_token,
|
|
)
|
|
except Exception as exc:
|
|
logger.exception("Failed to fetch Home Assistant data")
|
|
return {"error": True, "message": str(exc)}
|
|
|
|
await cache.set(CACHE_KEY, data, get_settings().ha_cache_ttl)
|
|
return data
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# POST /api/ha/control — requires auth
|
|
# ---------------------------------------------------------------------------
|
|
|
|
class HAControlRequest(BaseModel):
|
|
entity_id: str
|
|
action: str # toggle, turn_on, turn_off, open, close, stop
|
|
data: Optional[Dict[str, Any]] = None
|
|
|
|
|
|
@router.post("/ha/control")
|
|
async def control_ha(
|
|
body: HAControlRequest,
|
|
) -> Dict[str, Any]:
|
|
"""Control a Home Assistant entity (toggle light, open cover, etc.)."""
|
|
|
|
settings = get_settings()
|
|
|
|
if not settings.ha_url or not settings.ha_token:
|
|
return {"ok": False, "error": "Home Assistant not configured"}
|
|
|
|
result = await call_ha_service(
|
|
url=settings.ha_url,
|
|
token=settings.ha_token,
|
|
entity_id=body.entity_id,
|
|
action=body.action,
|
|
service_data=body.data,
|
|
)
|
|
|
|
# Invalidate cache so the next GET reflects the new state
|
|
if result.get("ok"):
|
|
await cache.invalidate(CACHE_KEY)
|
|
logger.info("[HA] Cache invalidated after %s on %s", body.action, body.entity_id)
|
|
|
|
return result
|