diff --git a/.env.example b/.env.example index e8b708f..a9d812c 100644 --- a/.env.example +++ b/.env.example @@ -78,9 +78,3 @@ MONITORINK_CODEX_TOKEN_FILE=/hermes/auth.json #MONITORINK_TRACKER_YGGREBORN_USER=ton.email@exemple.com #MONITORINK_TRACKER_YGGREBORN_PASS=TonMotDePasse #MONITORINK_TRACKER_TTL=1800 - -# Home Assistant (optionnel) — laisser vide pour désactiver -MONITORINK_HA_URL=http://homeassistant.local:8123 -MONITORINK_HA_TOKEN= -# Entités : "entity_id|Libellé|unité" séparées par des virgules -MONITORINK_HA_ENTITIES=sensor.salon_temperature|Salon|°C, binary_sensor.porte_entree|Porte, person.jerem|Jerem diff --git a/backend/config.py b/backend/config.py index 736fec2..4dc9b78 100644 --- a/backend/config.py +++ b/backend/config.py @@ -1,6 +1,6 @@ """Configuration centralisée de Monitorink, chargée depuis l'environnement. -Toutes les valeurs sensibles (token Claude, token Home Assistant) viennent de variables +Toutes les valeurs sensibles (token Claude, identifiants trackers) viennent de variables d'environnement / `.env` et ne sont jamais versionnées. """ from __future__ import annotations @@ -22,23 +22,6 @@ def _get_list(name: str) -> list[str]: return [item.strip() for item in raw.split(",") if item.strip()] -@dataclass(frozen=True) -class HAEntity: - """Une entité Home Assistant à afficher. Format env: `entity_id|Libellé|unité`.""" - - entity_id: str - label: str - unit: str = "" - - @classmethod - def parse(cls, spec: str) -> "HAEntity": - parts = [p.strip() for p in spec.split("|")] - entity_id = parts[0] - label = parts[1] if len(parts) > 1 and parts[1] else entity_id - unit = parts[2] if len(parts) > 2 else "" - return cls(entity_id=entity_id, label=label, unit=unit) - - @dataclass(frozen=True) class TrackerSpec: """Un tracker torrent privé dont on affiche le ratio. `type` choisit le fetcher @@ -95,10 +78,6 @@ class Config: weather_lat: float = field(default_factory=lambda: float(_get("MONITORINK_LAT", "48.8566"))) weather_lon: float = field(default_factory=lambda: float(_get("MONITORINK_LON", "2.3522"))) - # --- Home Assistant --- - ha_base_url: str = field(default_factory=lambda: _get("MONITORINK_HA_URL").rstrip("/")) - ha_token: str = field(default_factory=lambda: _get("MONITORINK_HA_TOKEN")) - # --- NAS (moniteur maison nas_monitor, endpoint /api/status) --- nas_url: str = field(default_factory=lambda: _get("MONITORINK_NAS_URL")) @@ -144,10 +123,6 @@ class Config: """Marge proactive de refresh en millisecondes (cf. claude_refresh_lead_minutes).""" return self.claude_refresh_lead_minutes * 60_000 - @property - def ha_entities(self) -> list[HAEntity]: - return [HAEntity.parse(s) for s in _get_list("MONITORINK_HA_ENTITIES")] - @property def trackers(self) -> list[TrackerSpec]: """Trackers actifs : `MONITORINK_TRACKERS=c411,autre` + un bloc d'env par clé, @@ -168,9 +143,5 @@ class Config: )) return specs - @property - def ha_enabled(self) -> bool: - return bool(self.ha_base_url and self.ha_token) - config = Config() diff --git a/backend/integrations/homeassistant.py b/backend/integrations/homeassistant.py deleted file mode 100644 index ea79077..0000000 --- a/backend/integrations/homeassistant.py +++ /dev/null @@ -1,55 +0,0 @@ -"""Statuts Home Assistant via l'API REST (`GET /api/states/`).""" -from __future__ import annotations - -from dataclasses import dataclass - -import httpx - -from config import HAEntity, config - - -@dataclass -class HAState: - label: str - state: str - unit: str = "" - ok: bool = True - - @property - def display(self) -> str: - if not self.ok: - return "—" - s = self.state - if s in ("on", "home", "open", "unlocked", "playing"): - s = "ON" - elif s in ("off", "away", "not_home", "closed", "locked", "idle", "paused"): - s = "OFF" - elif s in ("unavailable", "unknown"): - s = "n/d" - return f"{s}{(' ' + self.unit) if self.unit and s not in ('ON', 'OFF', 'n/d') else ''}" - - -async def fetch_states() -> list[HAState]: - entities = config.ha_entities - if not config.ha_enabled or not entities: - return [] - - headers = {"Authorization": f"Bearer {config.ha_token}", "Content-Type": "application/json"} - results: list[HAState] = [] - async with httpx.AsyncClient(timeout=15, headers=headers) as client: - for ent in entities: - results.append(await _fetch_one(client, ent)) - return results - - -async def _fetch_one(client: httpx.AsyncClient, ent: HAEntity) -> HAState: - url = f"{config.ha_base_url}/api/states/{ent.entity_id}" - try: - resp = await client.get(url) - if resp.status_code != 200: - return HAState(label=ent.label, state=f"HTTP {resp.status_code}", ok=False) - data = resp.json() - unit = ent.unit or data.get("attributes", {}).get("unit_of_measurement", "") - return HAState(label=ent.label, state=str(data.get("state", "")), unit=unit) - except httpx.HTTPError: - return HAState(label=ent.label, state="erreur", ok=False) diff --git a/dev/preview.py b/dev/preview.py index 06db57e..8d89562 100644 --- a/dev/preview.py +++ b/dev/preview.py @@ -1,6 +1,6 @@ """Aperçu design hors-ligne : rend dashboard.html avec des données fictives en PNG paysage (1680x1264, niveaux de gris, sans rotation), pour itérer sur le design sans -dépendre des intégrations réseau (Claude, météo, NAS, HA). +dépendre des intégrations réseau (Claude, météo, NAS, Codex). Usage: .venv/bin/python ../dev/preview.py [sortie.png] """