Calculations¶
EMS Mode Decision (EMSController._determine_mode)¶
The controller evaluates four mutually-exclusive modes in priority order:
1. PROTECT_BATTERY — battery_soc < battery_min_soc
2. PV_CHARGING — (pv_w − load_w) > pv_surplus_threshold_w AND soc < max
3. GRID_CHARGING — price < cheap_rate_threshold_eur AND soc < max
4. IDLE — none of the above
PV Surplus¶
If surplus_w > pv_surplus_threshold_w (default 200 W) and battery is not
full (soc < battery_max_soc), the system enters PV Charging mode.
Cheap Rate¶
Default threshold: 0.10 €/kWh.
Cost & Energy Accounting (CostOptimizer.record_tick)¶
Called every EMS tick (default 30 s). All values accumulate per calendar day
and reset automatically at midnight (Python date.today()).
Interval Duration¶
Grid Import¶
kwh_imported = (grid_power_w / 1000) * hours # only when grid_power_w > 0
grid_cost_eur += kwh_imported * price_eur_kwh
grid_import_kwh += kwh_imported
Grid export (negative grid_power_w) is not charged and not tracked.
PV Self-Consumption¶
pv_to_load_w = clamp(pv_power_w, 0, load_power_w) # portion of PV covering load
kwh_pv_used = (pv_to_load_w / 1000) * hours
pv_used_kwh += kwh_pv_used
pv_saved_eur += kwh_pv_used * price_eur_kwh
pv_saved_eur represents the cost that would have been paid if the same
energy had been bought from the grid at the current spot price.
Aggregated Metrics¶
| Metric | Formula |
|---|---|
today_grid_cost_eur |
Σ kwh_imported × price for today |
today_pv_saved_eur |
Σ kwh_pv_used × price for today |
today_grid_import_kwh |
Σ kwh_imported for today |
today_pv_used_kwh |
Σ kwh_pv_used for today |
week_grid_cost_eur |
Σ today_grid_cost_eur for last 7 days |
week_pv_saved_eur |
Σ today_pv_saved_eur for last 7 days |
Note: Accumulated values are in-memory only. They reset on add-on restart. For long-term persistence, use HA's built-in statistics on the published
total_increasingsensors.
Prognose & Vorhersage (ConsumptionModel)¶
Berechnet einmal pro EMS-Tick (30 s) in consumption_model.py.
Datenquelle: SQLite-Tageshistorie (store.py) + optionaler HA-Wetterforecast.
Prognostizierter Verbrauch (predicted_load_kwh)¶
Wenn weather_entity konfiguriert UND Forecast verfügbar:
target_temp = Tageshöchstwert von morgen (forecast.temp_tomorrow_c)
similar_days = Tage der letzten 60 Tage mit |avg_night_temp − target| ≤ 4 °C
Falls |similar_days| ≥ 3:
predicted_load = Median(load_total_kwh aller similar_days) → Confidence "high"
Sonst:
predicted_load = Median(load_total_kwh der letzten 30 Tage) → Confidence "low"
Sonst (kein Wetter):
predicted_load = Median(load_total_kwh der letzten 30 Tage) → Confidence "low"
Keine Historie:
predicted_load = 0,0 kWh → Confidence "none"
Prognostizierter PV-Ertrag (predicted_pv_kwh)¶
peaks = [peak_pv_w | letzte 14 Tage, peak_pv_w > 100 W]
p75 = 75. Perzentil(peaks) (konservative Schätzung)
Mit Forecast:
clear_frac = Ø(1 − cloud_coverage / 100) über alle Forecast-Slots
daylight_h = astronomische Tageslänge für HA-Breitengrad + aktuellen Monat
pv_factor = clear_frac × min(1,0 ; daylight_h / 12,0)
Ohne Forecast:
pv_factor = 0,5 (neutrale Annahme)
daylight_h = Näherung für 51 °N + aktuellen Monat
predicted_pv = max(0 ; (p75 / 1000) × pv_factor × daylight_h)
Tageslängenformel (daylight_hours_approx in weather_client.py):
day_of_year = (month − 1) × 30 + 15
decl = 23,45° × sin(360° × (284 + day_of_year) / 365)
cos_ha = −tan(lat) × tan(decl) [geclampt auf −1 … 1]
daylight_h = 2 × arccos(cos_ha) / 15
Grid-Charge-Empfehlung (should_grid_charge)¶
usable_kwh = battery_capacity_kwh × max(0 ; soc − battery_min_soc) / 100
should_grid_charge = (predicted_load > 0)
AND (usable_kwh + predicted_pv) < predicted_load
Wenn Batterie + erwarteter PV-Ertrag den prognostizierten Verbrauch nicht decken, empfiehlt miniEMS das Nachladen aus dem Netz.
Wetterdaten-Cache¶
WeatherClient cached das Ergebnis von weather.get_forecasts für 30 Minuten.
Der HA-Breitengrad wird einmalig von http://supervisor/core/api/config gelesen.