""" Demeter Server — Configuration Loads settings from environment variables and the device registry from config/devices.yaml. """ from __future__ import annotations import logging from pathlib import Path from typing import Optional import yaml from pydantic import BaseModel, Field from pydantic_settings import BaseSettings logger = logging.getLogger(__name__) # ── Paths ── BASE_DIR = Path(__file__).resolve().parent.parent CONFIG_DIR = BASE_DIR / "config" TEMPLATES_DIR = BASE_DIR / "templates" # ── Device Config Models ── class ResourceConfig(BaseModel): """A single CoAP resource on a device.""" uri: str name: str type: str = "periodic" # "periodic" or "event" class DeviceConfig(BaseModel): """A single ESP sensor node.""" id: str name: str ip: str port: int = 5683 enabled: bool = True resources: list[ResourceConfig] = Field(default_factory=list) class DevicesConfig(BaseModel): """Top-level device registry loaded from YAML.""" devices: list[DeviceConfig] = Field(default_factory=list) # ── Application Settings ── class Settings(BaseSettings): """Server settings loaded from environment variables.""" # FastAPI app_title: str = "Demeter IoT Server" app_version: str = "0.1.0" debug: bool = False # Server host: str = "0.0.0.0" port: int = 8000 # Loki loki_url: Optional[str] = None # e.g. "http://localhost:3100/loki/api/v1/push" # CoAP coap_request_timeout: float = 10.0 # seconds coap_reconnect_base: float = 5.0 # base backoff seconds coap_reconnect_max: float = 60.0 # max backoff seconds # Device config path devices_config_path: str = str(CONFIG_DIR / "devices.yaml") model_config = {"env_prefix": "DEMETER_", "env_file": ".env"} def load_devices_config(path: str | Path | None = None) -> DevicesConfig: """Load and validate the device registry from YAML.""" if path is None: path = CONFIG_DIR / "devices.yaml" path = Path(path) if not path.exists(): logger.warning("Device config not found at %s, using empty registry", path) return DevicesConfig(devices=[]) with open(path) as f: raw = yaml.safe_load(f) if raw is None: return DevicesConfig(devices=[]) return DevicesConfig.model_validate(raw) # Singleton settings instance settings = Settings()