Initial pump monitor with Coolify deployment
This commit is contained in:
@@ -0,0 +1,84 @@
|
||||
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)
|
||||
Reference in New Issue
Block a user