Implement proactive forecast-driven car charging strategy

- Replace reactive export-threshold wallbox activation with proactive
  logic: WallboxA/B activate when forecast ≥ mid AND PV ≥ threshold
  AND SOC ≥ 35%, without requiring grid export surplus
- Add WallboxB no-car detection via grid-delta probe (no PM available):
  after probe window, if grid shift < GridDeltaThreshW → no car, retry
  after configured timeout
- Add EOD soft stop: after 16:00, stop proactive car charging if
  remaining PV estimate can't cover battery deficit to 90% by sunset
- WW boost no longer requires export threshold; dynamic setpoint uses
  tank top temp + hysteresis + boost delta, capped at 60°C
- Proactive wallboxes bypass import-hysteresis shutdown; SOC emergency
  brake uses SOCFloor (5%) instead of standard AllConsumers gate
- Add WWTopTempC to SystemState (ww_top_temp metric from DHW cylinder)
- Add BatteryConfig (capacity_kwh), CarChargingConfig to config
- Add WWMaxSetpointC, WWHysteresisC to StrategicConfig
- Update Decide() signature: forecastKWh + sunsetTime parameters
- Update all tests; add proactive charging test cases

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-07 21:23:04 +02:00
parent fc535d395c
commit 971e22c4be
6 changed files with 447 additions and 114 deletions

View File

@@ -15,6 +15,7 @@ prometheus:
phase_l1_power: "pcc_ac_active_power_phaseOne" # per-phase grid power L1 (W)
phase_l2_power: "pcc_ac_active_power_phaseTwo" # per-phase grid power L2 (W)
phase_l3_power: "pcc_ac_active_power_phaseThree" # per-phase grid power L3 (W)
ww_top_temp: "heating_dhw_sensors_temperature_dhwCylinder_top_value" # DHW cylinder top temperature (°C)
# Shelly actuators
shelly:
@@ -69,6 +70,8 @@ strategic:
forecast_high_kwh: 25
forecast_mid_kwh: 15
ww_base_c: 48 # normal WW setpoint (°C)
ww_max_setpoint_c: 60 # absolute maximum WW setpoint (°C)
ww_hysteresis_c: 5 # Viessmann switchOn = setpoint - hysteresis (°C)
ww_boost_high_c: 5 # +5°C on high-forecast days (>25 kWh)
ww_boost_mid_c: 3 # +3°C on medium-forecast days (>15 kWh)
ww_window_start: "12:30" # WW boost only allowed from 12:30
@@ -118,6 +121,21 @@ ems:
trip_goal_file: "/var/lib/ems/trip-goal.json" # persisted active trip goal
session_log_file: "/var/lib/ems/sessions.jsonl" # JSONL log of completed charge sessions
# Battery physical properties (for EOD soft-stop calculation)
battery:
capacity_kwh: 8.0 # usable battery capacity (kWh)
# Proactive car charging strategy (forecast-driven, no export threshold required)
car_charging:
min_soc: 35 # start car charging only if battery SOC ≥ this (%)
soc_floor: 5 # emergency brake floor — stop if SOC drops below this (%)
eod_soc_target: 90 # desired battery SOC at end of day / sunset (%)
eod_time: "16:00" # start EOD soft-stop checks after this time
no_car_retry_min: 30 # minutes before retrying after no-car detection
pv_threshold_a_w: 1000 # min PV production to start WallboxA (W) — 50% of 2kW rated
pv_threshold_b_w: 2000 # min PV production to start WallboxB (W) — 50% of 4kW rated
grid_delta_thresh_w: 2000 # min grid power shift after WallboxB activation = car detected (W)
# Known car profiles for trip mode
cars:
mini: