Fix proactive charging, logging, and status page ordering

1. SOC high bypass: when SOC >= soc_high_bypass_pct (default 90%), activate
   car charging without requiring a good forecast. Solves today's missed
   charging window where 4kW was exported for 6h with a full battery because
   the intraday forecast dropped from 15.4 to 13.3 kWh.

2. Log ambient_c in every cycle: makes it diagnosable why isHeatingPeriod
   suppresses SG-Ready on warm days (heating_min_ambient_c: 15°C gate).

3. Consumer names in logs: replace slog integer Consumer values with
   .String() so logs show "wallbox_a" instead of "2".

4. Status page order: reorder consumers by EMS priority (WallboxA →
   WallboxB → WW → SG-Ready) instead of the old reversed order.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-09 20:47:56 +02:00
parent f45d23df97
commit 2728755738
5 changed files with 54 additions and 29 deletions

View File

@@ -33,14 +33,15 @@ type BatteryConfig struct {
// CarChargingConfig holds parameters for the proactive car charging strategy.
type CarChargingConfig struct {
MinSOC int `yaml:"min_soc"` // minimum SOC% to start car charging (e.g. 35)
SOCFloor int `yaml:"soc_floor"` // never drain battery below this % (e.g. 5)
EODSOCTarget int `yaml:"eod_soc_target"` // target SOC% to reach by sunset (e.g. 90)
EODTime string `yaml:"eod_time"` // soft-stop check starts at this time (e.g. "16:00")
NoCarRetryMin int `yaml:"no_car_retry_min"` // minutes before retrying after no-car detection
PVThresholdAW float64 `yaml:"pv_threshold_a_w"` // min PV production to start WallboxA (W)
PVThresholdBW float64 `yaml:"pv_threshold_b_w"` // min PV production to start WallboxB (W)
GridDeltaThreshW float64 `yaml:"grid_delta_thresh_w"` // min grid power shift after WallboxB activation = car detected (W)
MinSOC int `yaml:"min_soc"` // minimum SOC% to start car charging (e.g. 25)
SOCFloor int `yaml:"soc_floor"` // never drain battery below this % (e.g. 5)
SOCHighBypassPct int `yaml:"soc_high_bypass_pct"` // activate without forecast check when SOC ≥ this % (0 → default 90%)
EODSOCTarget int `yaml:"eod_soc_target"` // target SOC% to reach by sunset (e.g. 90)
EODTime string `yaml:"eod_time"` // soft-stop check starts at this time (e.g. "16:00")
NoCarRetryMin int `yaml:"no_car_retry_min"` // minutes before retrying after no-car detection
PVThresholdAW float64 `yaml:"pv_threshold_a_w"` // min PV production to start WallboxA (W)
PVThresholdBW float64 `yaml:"pv_threshold_b_w"` // min PV production to start WallboxB (W)
GridDeltaThreshW float64 `yaml:"grid_delta_thresh_w"` // min grid power shift after WallboxB activation = car detected (W)
}
func (c *CarChargingConfig) EODTimeParsed(ref time.Time) time.Time {