92 lines
3.4 KiB
Python
92 lines
3.4 KiB
Python
"""Configuration centralisée de Monitorink, chargée depuis l'environnement.
|
|
|
|
Toutes les valeurs sensibles (token Claude, token Home Assistant) viennent de variables
|
|
d'environnement / `.env` et ne sont jamais versionnées.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
from dataclasses import dataclass, field
|
|
|
|
from dotenv import load_dotenv
|
|
|
|
load_dotenv()
|
|
|
|
|
|
def _get(name: str, default: str = "") -> str:
|
|
return os.environ.get(name, default).strip()
|
|
|
|
|
|
def _get_list(name: str) -> list[str]:
|
|
raw = _get(name)
|
|
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 Config:
|
|
# --- Affichage ---
|
|
timezone: str = field(default_factory=lambda: _get("MONITORINK_TZ", "Europe/Paris"))
|
|
locale: str = field(default_factory=lambda: _get("MONITORINK_LOCALE", "fr_FR"))
|
|
# Canevas de RENDU en paysage (1680x1264). Le PNG est ensuite pivoté de 90° dans
|
|
# render.py pour le panneau e-ink physiquement en portrait (1264x1680).
|
|
width: int = field(default_factory=lambda: int(_get("MONITORINK_WIDTH", "1680")))
|
|
height: int = field(default_factory=lambda: int(_get("MONITORINK_HEIGHT", "1264")))
|
|
# Sens de rotation pour l'affichage Kobo : "cw" (bouton à droite) ou "ccw".
|
|
rotate: str = field(default_factory=lambda: _get("MONITORINK_ROTATE", "cw").lower())
|
|
|
|
# --- Claude ---
|
|
# Chemin du fichier .credentials.json d'un login Claude ISOLÉ dédié à Monitorink
|
|
# (CLAUDE_CONFIG_DIR séparé). Le backend y lit/écrit (refresh) sans toucher le
|
|
# ~/.claude partagé. L'endpoint /usage exige le scope user:profile -> login complet
|
|
# requis (le token `claude setup-token` ne suffit pas, scope insuffisant).
|
|
claude_creds_path: str = field(
|
|
default_factory=lambda: _get("MONITORINK_CLAUDE_CREDS", "/creds/.credentials.json")
|
|
)
|
|
claude_ua: str = field(
|
|
default_factory=lambda: _get("MONITORINK_CLAUDE_UA", "claude-code/2.1.172")
|
|
)
|
|
ccusage_enabled: bool = field(
|
|
default_factory=lambda: _get("MONITORINK_CCUSAGE", "0") in ("1", "true", "yes")
|
|
)
|
|
|
|
# --- Météo (Open-Meteo, sans clé) ---
|
|
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"))
|
|
|
|
# --- Cache / rafraîchissement serveur ---
|
|
cache_ttl_seconds: int = field(
|
|
default_factory=lambda: int(_get("MONITORINK_CACHE_TTL", "120"))
|
|
)
|
|
|
|
@property
|
|
def ha_entities(self) -> list[HAEntity]:
|
|
return [HAEntity.parse(s) for s in _get_list("MONITORINK_HA_ENTITIES")]
|
|
|
|
@property
|
|
def ha_enabled(self) -> bool:
|
|
return bool(self.ha_base_url and self.ha_token)
|
|
|
|
|
|
config = Config()
|