session_config wird nicht mehr über die API/YAML gesendet. parser_config ist jetzt das einzige Config-Format zwischen Odoo und Bridge. Änderungen: - api/models.py: DeviceConfig.session_config → parser_config: dict SessionConfig bleibt als internes Modell für SessionDetector DeviceConfig.to_session_config() extrahiert Werte mit Defaults - config/schema.py: DeviceConfig gleich umgestellt + to_session_config() - config/loader.py: liest parser_config aus YAML, Fallback für legacy session_config (Rückwärtskompatibilität für bestehende config-active.yaml) - core/device_manager.py: device.session_config → device.to_session_config() - core/service_manager.py: session_config Referenzen entfernt - Odoo _build_bridge_config: sendet parser_config direkt (+ heartbeat) - Odoo iot_api.py: gleich umgestellt - Tests: alle SessionConfig-Fixtures → parser_config dicts 63/63 passing
156 lines
4.9 KiB
Python
156 lines
4.9 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
|
|
|
|
Args:
|
|
config_path: Path to YAML configuration file.
|
|
|
|
Returns:
|
|
Fully validated bridge configuration object.
|
|
|
|
Raises:
|
|
ConfigurationError: If the config file does not exist.
|
|
"""
|
|
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", []):
|
|
# Support both new parser_config and legacy session_config format
|
|
if "parser_config" in dev_data:
|
|
parser_cfg = dev_data["parser_config"] or {}
|
|
elif "session_config" in dev_data:
|
|
# Legacy: flatten session_config into parser_config
|
|
parser_cfg = {k: v for k, v in dev_data["session_config"].items()
|
|
if k != "strategy"}
|
|
else:
|
|
parser_cfg = {}
|
|
device = DeviceConfig(
|
|
device_id=dev_data["device_id"],
|
|
mqtt_topic=dev_data["mqtt_topic"],
|
|
parser_type=dev_data.get("parser_type", "shelly_pm"),
|
|
machine_name=dev_data["machine_name"],
|
|
parser_config=parser_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).
|
|
|
|
Args:
|
|
config_path: Path to persisted active configuration.
|
|
|
|
Returns:
|
|
List of validated ``DeviceConfig`` entries.
|
|
|
|
Raises:
|
|
ConfigurationError: If the config file does not exist.
|
|
"""
|
|
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", []):
|
|
# Support both new parser_config and legacy session_config format
|
|
# (config-active.yaml may still contain session_config from older pushes)
|
|
if "parser_config" in dev_data:
|
|
parser_cfg = dev_data["parser_config"] or {}
|
|
elif "session_config" in dev_data:
|
|
# Legacy: flatten session_config into parser_config
|
|
parser_cfg = {k: v for k, v in dev_data["session_config"].items()
|
|
if k != "strategy"}
|
|
else:
|
|
parser_cfg = {}
|
|
device = DeviceConfig(
|
|
device_id=dev_data["device_id"],
|
|
mqtt_topic=dev_data["mqtt_topic"],
|
|
parser_type=dev_data.get("parser_type", "shelly_pm"),
|
|
machine_name=dev_data["machine_name"],
|
|
parser_config=parser_cfg,
|
|
)
|
|
devices.append(device)
|
|
|
|
return devices
|