85 lines
2.8 KiB
Python
85 lines
2.8 KiB
Python
from __future__ import annotations
|
|
|
|
import os
|
|
from dataclasses import dataclass
|
|
from zoneinfo import ZoneInfo, ZoneInfoNotFoundError
|
|
|
|
|
|
def _float_env(name: str, default: float, minimum: float) -> float:
|
|
raw = os.getenv(name, str(default))
|
|
try:
|
|
value = float(raw)
|
|
except ValueError as exc:
|
|
raise ValueError(f"{name} must be a number") from exc
|
|
if value < minimum:
|
|
raise ValueError(f"{name} must be at least {minimum}")
|
|
return value
|
|
|
|
|
|
def _int_env(name: str, default: int, minimum: int) -> int:
|
|
raw = os.getenv(name, str(default))
|
|
try:
|
|
value = int(raw)
|
|
except ValueError as exc:
|
|
raise ValueError(f"{name} must be an integer") from exc
|
|
if value < minimum:
|
|
raise ValueError(f"{name} must be at least {minimum}")
|
|
return value
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class Settings:
|
|
tapo_host: str
|
|
tapo_username: str
|
|
tapo_password: str
|
|
timezone_name: str
|
|
poll_interval_seconds: float
|
|
start_watts: float
|
|
stop_watts: float
|
|
confirm_samples: int
|
|
max_gap_seconds: float
|
|
sample_retention_days: int
|
|
database_path: str
|
|
dashboard_username: str
|
|
dashboard_password: str
|
|
|
|
@classmethod
|
|
def from_env(cls) -> "Settings":
|
|
timezone_name = os.getenv("TZ", "Europe/Prague")
|
|
try:
|
|
ZoneInfo(timezone_name)
|
|
except ZoneInfoNotFoundError as exc:
|
|
raise ValueError(f"TZ is not a known timezone: {timezone_name}") from exc
|
|
|
|
start_watts = _float_env("START_WATTS", 100.0, 0.1)
|
|
stop_watts = _float_env("STOP_WATTS", 30.0, 0.0)
|
|
if stop_watts >= start_watts:
|
|
raise ValueError("STOP_WATTS must be lower than START_WATTS")
|
|
|
|
poll_interval = _float_env("POLL_INTERVAL_SECONDS", 2.0, 0.5)
|
|
max_gap = _float_env("MAX_GAP_SECONDS", max(20.0, poll_interval * 5), poll_interval * 2)
|
|
|
|
return cls(
|
|
tapo_host=os.getenv("TAPO_HOST", "").strip(),
|
|
tapo_username=os.getenv("TAPO_USERNAME", "").strip(),
|
|
tapo_password=os.getenv("TAPO_PASSWORD", ""),
|
|
timezone_name=timezone_name,
|
|
poll_interval_seconds=poll_interval,
|
|
start_watts=start_watts,
|
|
stop_watts=stop_watts,
|
|
confirm_samples=_int_env("CONFIRM_SAMPLES", 2, 1),
|
|
max_gap_seconds=max_gap,
|
|
sample_retention_days=_int_env("SAMPLE_RETENTION_DAYS", 30, 1),
|
|
database_path=os.getenv("DATABASE_PATH", "/data/pump-monitor.sqlite3"),
|
|
dashboard_username=os.getenv("DASHBOARD_USERNAME", "water-monitor").strip(),
|
|
dashboard_password=os.getenv("DASHBOARD_PASSWORD", ""),
|
|
)
|
|
|
|
@property
|
|
def credentials_configured(self) -> bool:
|
|
return bool(self.tapo_username and self.tapo_password)
|
|
|
|
@property
|
|
def timezone(self) -> ZoneInfo:
|
|
return ZoneInfo(self.timezone_name)
|