Latest Updates done, before integrating

This commit is contained in:
2026-04-12 10:13:53 +02:00
parent db46fcf0c6
commit 5354e34055
19 changed files with 820 additions and 41 deletions

View File

@@ -690,8 +690,12 @@ func (e *Engine) socEmergencyBrake(soc float64, now time.Time) []Action {
continue
}
// Proactive wallboxes: only emergency-brake at SOCFloor
if cs.ProactiveCharging && (c == ConsumerWallboxA || c == ConsumerWallboxB) {
// Intentional wallbox charging (proactive or trip mode): only emergency-brake at SOCFloor.
// Trip mode uses ManualOverride=true, proactive charging uses ProactiveCharging=true —
// both deserve soc_floor protection instead of the standard block_all gate.
isIntentionalCharging := (cs.ProactiveCharging || cs.ManualOverride) &&
(c == ConsumerWallboxA || c == ConsumerWallboxB)
if isIntentionalCharging {
floor := float64(e.cfg.CarCharging.SOCFloor)
if floor > 0 && soc >= floor {
continue // still above floor, keep charging
@@ -967,7 +971,10 @@ func (e *Engine) RecoverState(states map[Consumer]DeviceStatus) {
// On match: expired overrides are cleared, resuming normal EMS control.
// Power readings (from PM-capable devices) update the low-power cycle counter for
// car-not-charging detection.
func (e *Engine) SyncHardwareState(states map[Consumer]DeviceStatus, now time.Time, overrideTimeout time.Duration) {
// When monitorOnly is true, hardware mismatches silently update engine state without
// logging or applying override lockouts — the EMS cannot act anyway, so treating
// every missed switch as a "manual override" would produce spurious log spam.
func (e *Engine) SyncHardwareState(states map[Consumer]DeviceStatus, now time.Time, overrideTimeout time.Duration, monitorOnly bool) {
for c, status := range states {
cs, ok := e.consumers[c]
if !ok {
@@ -975,21 +982,33 @@ func (e *Engine) SyncHardwareState(states map[Consumer]DeviceStatus, now time.Ti
}
if cs.Active != status.On {
// External change detected
e.logger.Info("manual override detected — external state change",
"consumer", c.String(),
"engine_state", cs.Active,
"hardware_state", status.On,
"override_until", now.Add(overrideTimeout).Format("15:04"),
)
cs.Active = status.On
cs.ManualOverride = true
cs.OverrideUntil = now.Add(overrideTimeout)
cs.LowPowerCycles = 0
if !status.On {
cs.ActivatedAt = time.Time{}
if monitorOnly {
// In monitor-only mode the EMS cannot execute switch actions, so a
// mismatch just means a pending action couldn't be carried out.
// Silently realign engine state to hardware without locking.
cs.Active = status.On
if !status.On {
cs.ActivatedAt = time.Time{}
} else {
cs.ActivatedAt = now
}
} else {
cs.ActivatedAt = now
// External change detected — log and apply override lockout
e.logger.Info("manual override detected — external state change",
"consumer", c.String(),
"engine_state", cs.Active,
"hardware_state", status.On,
"override_until", now.Add(overrideTimeout).Format("15:04"),
)
cs.Active = status.On
cs.ManualOverride = true
cs.OverrideUntil = now.Add(overrideTimeout)
cs.LowPowerCycles = 0
if !status.On {
cs.ActivatedAt = time.Time{}
} else {
cs.ActivatedAt = now
}
}
} else if cs.ManualOverride && now.After(cs.OverrideUntil) {
// Override expired and state matches — resume EMS control