Retire l'intégration Home Assistant (code mort + config + env.example)
This commit is contained in:
@@ -78,9 +78,3 @@ MONITORINK_CODEX_TOKEN_FILE=/hermes/auth.json
|
|||||||
#MONITORINK_TRACKER_YGGREBORN_USER=ton.email@exemple.com
|
#MONITORINK_TRACKER_YGGREBORN_USER=ton.email@exemple.com
|
||||||
#MONITORINK_TRACKER_YGGREBORN_PASS=TonMotDePasse
|
#MONITORINK_TRACKER_YGGREBORN_PASS=TonMotDePasse
|
||||||
#MONITORINK_TRACKER_TTL=1800
|
#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
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
"""Configuration centralisée de Monitorink, chargée depuis l'environnement.
|
"""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.
|
d'environnement / `.env` et ne sont jamais versionnées.
|
||||||
"""
|
"""
|
||||||
from __future__ import annotations
|
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()]
|
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)
|
@dataclass(frozen=True)
|
||||||
class TrackerSpec:
|
class TrackerSpec:
|
||||||
"""Un tracker torrent privé dont on affiche le ratio. `type` choisit le fetcher
|
"""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_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")))
|
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 (moniteur maison nas_monitor, endpoint /api/status) ---
|
||||||
nas_url: str = field(default_factory=lambda: _get("MONITORINK_NAS_URL"))
|
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)."""
|
"""Marge proactive de refresh en millisecondes (cf. claude_refresh_lead_minutes)."""
|
||||||
return self.claude_refresh_lead_minutes * 60_000
|
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
|
@property
|
||||||
def trackers(self) -> list[TrackerSpec]:
|
def trackers(self) -> list[TrackerSpec]:
|
||||||
"""Trackers actifs : `MONITORINK_TRACKERS=c411,autre` + un bloc d'env par clé,
|
"""Trackers actifs : `MONITORINK_TRACKERS=c411,autre` + un bloc d'env par clé,
|
||||||
@@ -168,9 +143,5 @@ class Config:
|
|||||||
))
|
))
|
||||||
return specs
|
return specs
|
||||||
|
|
||||||
@property
|
|
||||||
def ha_enabled(self) -> bool:
|
|
||||||
return bool(self.ha_base_url and self.ha_token)
|
|
||||||
|
|
||||||
|
|
||||||
config = Config()
|
config = Config()
|
||||||
|
|||||||
@@ -1,55 +0,0 @@
|
|||||||
"""Statuts Home Assistant via l'API REST (`GET /api/states/<entity_id>`)."""
|
|
||||||
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)
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
"""Aperçu design hors-ligne : rend dashboard.html avec des données fictives en PNG
|
"""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
|
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]
|
Usage: .venv/bin/python ../dev/preview.py [sortie.png]
|
||||||
"""
|
"""
|
||||||
|
|||||||
Reference in New Issue
Block a user