Files
rpotter6298 c8e3016fd6 Add new daemons and debug scripts for Sigenergy and Oracle functionalities
- Implement `sigen_daemon.py` to poll Sigenergy plant metrics and store snapshots.
- Create `web_daemon.py` for serving a web interface with various endpoints.
- Add debug scripts:
  - `debug_duplicates.py` to find duplicate target times in forecast data.
  - `debug_energy_forecast.py` to print baseline energy forecast curves.
  - `debug_oracle_evaluations.py` to run the oracle evaluator.
  - `debug_sigen.py` to inspect stored Sigenergy plant snapshots.
  - `debug_weather.py` to trace resolved truth data.
  - `modbus_test.py` for exploring Sigenergy plants or inverters over Modbus TCP.
- Introduce `oracle_evaluator.py` for evaluating stored oracle predictions against actuals.
- Add TCN training scripts in `tcn` directory for training usage sequence models.
2026-04-28 08:14:00 +02:00

160 lines
4.0 KiB
Python

from __future__ import annotations
from dataclasses import dataclass, field
from datetime import datetime, timezone
from enum import Enum
from typing import Any
class ObservationQuality(str, Enum):
OK = "ok"
STALE = "stale"
ESTIMATED = "estimated"
MISSING = "missing"
ERROR = "error"
class PowerStage(str, Enum):
ALWAYS = "always"
SURPLUS = "surplus"
CHEAP_GRID = "cheap_grid"
STANDARD = "standard"
CONSERVE = "conserve"
class ForecastKind(str, Enum):
SOLAR = "solar"
LOAD = "load"
@dataclass(frozen=True)
class Observation:
source: str
metric: str
value: int | float | str | bool | None
unit: str = "none"
quality: ObservationQuality = ObservationQuality.OK
observed_at: datetime = field(default_factory=lambda: datetime.now(timezone.utc))
received_at: datetime = field(default_factory=lambda: datetime.now(timezone.utc))
metadata: dict[str, Any] = field(default_factory=dict)
@dataclass(frozen=True)
class Snapshot:
created_at: datetime
solar_power_w: float | None = None
home_power_w: float | None = None
battery_soc_pct: float | None = None
grid_import_w: float | None = None
grid_export_w: float | None = None
cheap_window_active: bool = False
input_quality: dict[str, ObservationQuality] = field(default_factory=dict)
@dataclass(frozen=True)
class Decision:
created_at: datetime
stage: PowerStage
reasons: list[str]
confidence: float
@dataclass(frozen=True)
class WeatherForecastPoint:
issued_at: datetime
target_at: datetime
horizon_hours: int
temperature_c: float | None
shortwave_radiation_w_m2: float | None
cloud_cover_pct: float | None = None
source: str = "open_meteo"
@dataclass(frozen=True)
class WeatherForecastRun:
issued_at: datetime
source: str
latitude: float
longitude: float
points: list[WeatherForecastPoint]
@dataclass(frozen=True)
class WeatherResolvedTruth:
resolved_at: datetime
temperature_c: float | None
shortwave_radiation_w_m2: float | None
source: str
cloud_cover_pct: float | None = None
@dataclass(frozen=True)
class SigenPlantSnapshot:
observed_at: datetime
received_at: datetime
source: str = "sigen_modbus"
plant_epoch_seconds: int | None = None
plant_ems_work_mode: int | None = None
plant_running_state: int | None = None
grid_sensor_status: int | None = None
solar_power_w: float | None = None
battery_soc_pct: float | None = None
battery_soh_pct: float | None = None
battery_power_w: float | None = None
grid_power_w: float | None = None
grid_import_w: float | None = None
grid_export_w: float | None = None
load_power_w: float | None = None
plant_active_power_w: float | None = None
accumulated_pv_energy_kwh: float | None = None
daily_consumed_energy_kwh: float | None = None
accumulated_consumed_energy_kwh: float | None = None
raw_values: dict[str, int | float | str | bool | None] = field(default_factory=dict)
@dataclass(frozen=True)
class PowerForecastPoint:
target_at: datetime
horizon_minutes: int
expected_power_w: float
p10_power_w: float
p50_power_w: float
p90_power_w: float
confidence: float
source: str
model_version: str
metadata: dict[str, Any] = field(default_factory=dict)
@dataclass(frozen=True)
class PowerForecastRun:
issued_at: datetime
kind: ForecastKind
source: str
model_version: str
points: list[PowerForecastPoint]
@dataclass(frozen=True)
class NetPowerForecastPoint:
target_at: datetime
horizon_minutes: int
expected_net_power_w: float
safe_net_power_w: float
p10_net_power_w: float
p50_net_power_w: float
p90_net_power_w: float
solar_p50_power_w: float
load_p50_power_w: float
solar_p10_power_w: float
solar_p90_power_w: float
load_p10_power_w: float
load_p90_power_w: float
@dataclass(frozen=True)
class NetPowerForecastRun:
issued_at: datetime
source: str
points: list[NetPowerForecastPoint]