odoo_mqtt/iot_bridge/config/loader.py

125 lines
3.8 KiB
Python

"""Configuration loading functions."""
from pathlib import Path
import yaml
from config.schema import (
BridgeConfig,
DeviceConfig,
DeviceStatusConfig,
EnvOverrideSettings,
EventQueueConfig,
LoggingConfig,
MQTTConfig,
OdooConfig,
SessionConfig,
)
from exceptions import ConfigurationError
def _apply_environment_overrides(mqtt_data: dict, odoo_data: dict) -> tuple[dict, dict]:
"""Apply environment variable overrides for MQTT and Odoo settings."""
env = EnvOverrideSettings()
mqtt_overrides = {
"broker": env.mqtt_broker,
"port": env.mqtt_port,
"username": env.mqtt_username,
"password": env.mqtt_password,
"client_id": env.mqtt_client_id,
"keepalive": env.mqtt_keepalive,
"use_tls": env.mqtt_use_tls,
}
odoo_overrides = {
"base_url": env.odoo_base_url,
"database": env.odoo_database,
"username": env.odoo_username,
"api_key": env.odoo_api_key,
}
merged_mqtt = {**mqtt_data, **{key: value for key, value in mqtt_overrides.items() if value is not None}}
merged_odoo = {**odoo_data, **{key: value for key, value in odoo_overrides.items() if value is not None}}
return merged_mqtt, merged_odoo
def load_config(config_path: str = "config.yaml") -> BridgeConfig:
"""Load configuration from YAML file.
Resolution order:
1. YAML values from ``config_path``
2. Environment variable overrides (via ``EnvOverrideSettings``)
3. Pydantic default values from schema models
"""
path = Path(config_path)
if not path.exists():
raise ConfigurationError(
f"Config file not found: {config_path}",
path=str(path.absolute()),
)
with open(path) as f:
data = yaml.safe_load(f)
mqtt_data, odoo_data = _apply_environment_overrides(data["mqtt"], data.get("odoo", {}))
mqtt_config = MQTTConfig(**mqtt_data)
odoo_config = OdooConfig(**odoo_data)
logging_config = LoggingConfig(**data.get("logging", {}))
event_queue_config = EventQueueConfig(**data.get("event_queue", {}))
device_status_config = DeviceStatusConfig(**data.get("device_status", {}))
devices = []
for dev_data in data.get("devices", []):
session_cfg = SessionConfig(**dev_data["session_config"])
device = DeviceConfig(
device_id=dev_data["device_id"],
mqtt_topic=dev_data["mqtt_topic"],
parser_type=dev_data["parser_type"],
machine_name=dev_data["machine_name"],
session_config=session_cfg,
)
devices.append(device)
return BridgeConfig(
mqtt=mqtt_config,
odoo=odoo_config,
logging=logging_config,
event_queue=event_queue_config,
device_status=device_status_config,
devices=devices,
)
def load_devices_from_config(config_path: str) -> list[DeviceConfig]:
"""Load only devices from a config file (for config-active.yaml)."""
path = Path(config_path)
if not path.exists():
raise ConfigurationError(
f"Config file not found: {config_path}",
path=str(path.absolute()),
)
with open(path) as f:
data = yaml.safe_load(f)
devices = []
for dev_data in data.get("devices", []):
session_data = dev_data["session_config"].copy()
# Add default strategy if not present (pushed configs don't include it)
if "strategy" not in session_data:
session_data["strategy"] = "power_threshold"
session_cfg = SessionConfig(**session_data)
device = DeviceConfig(
device_id=dev_data["device_id"],
mqtt_topic=dev_data["mqtt_topic"],
parser_type=dev_data["parser_type"],
machine_name=dev_data["machine_name"],
session_config=session_cfg,
)
devices.append(device)
return devices