"""Heartbeat: DB záznam + volitelný HTTP pulz do Loxone.""" from __future__ import annotations import logging import os import httpx from app.config import get_settings logger = logging.getLogger(__name__) EMS_BACKEND_VERSION = "v1.0.0" async def send_heartbeat( site_id: int, db, loxone_host: str | None = None, loxone_port: int | None = None, ) -> None: """ 1. Aktualizuje ems.site_heartbeat v DB 2. Pokud je Loxone nakonfigurováno, pošle HTTP pulz """ try: endpoint = await db.fetchrow( """ SELECT host, port, protocol, auth_reference FROM ems.site_endpoint WHERE site_id = $1 AND endpoint_type = 'loxone_http' AND enabled = true ORDER BY id LIMIT 1 """, site_id, ) loxone_ok = False if endpoint: proto = (endpoint["protocol"] or "http").lower() if proto not in ("http", "https"): proto = "http" host = loxone_host if loxone_host is not None else endpoint["host"] if loxone_port is not None: port = int(loxone_port) else: port = int(endpoint["port"] or (443 if proto == "https" else 80)) url = f"{proto}://{host}:{port}/dev/sps/io/EMS_Heartbeat/1" settings = get_settings() user = settings.loxone_user or os.getenv("LOXONE_USER") or "" password = settings.loxone_password or os.getenv("LOXONE_PASSWORD") or "" auth = (user, password) if user else None try: async with httpx.AsyncClient(timeout=5.0) as client: await client.get(url, auth=auth) loxone_ok = True except Exception as e: logger.warning("Heartbeat Loxone failed (site=%s): %s", site_id, e) status = "ok" if (not endpoint or loxone_ok) else "degraded" await db.execute( "select ems.fn_update_heartbeat($1, $2, $3)", site_id, status, EMS_BACKEND_VERSION, ) except Exception as e: logger.error("Heartbeat service error (site=%s): %s", site_id, e)