Fix Shelly unreachable: roll back engine state on failed actions

Without this, a failed Execute() left engine state diverged from hardware.
SyncHardwareState would then misread the mismatch as a manual override and
apply a 1-hour lockout — causing either a stuck-on or stuck-off loop.

Now Execute() returns per-action []error. The control loop calls
Engine.RollbackAction() for each failed action, keeping engine state in
sync with hardware so the next cycle simply retries.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-08 20:44:08 +02:00
parent 2dd53d1543
commit f45d23df97
4 changed files with 57 additions and 22 deletions

View File

@@ -755,6 +755,34 @@ func (e *Engine) ApplyOverride(consumer Consumer, on bool, duration time.Duratio
)
}
// RollbackAction reverts the engine's internal state for an action that the actuator
// failed to execute. Without this, the engine believes the switch happened, diverges
// from hardware, and SyncHardwareState will misinterpret the next read-back as a
// manual override and apply a 1-hour lockout.
//
// After rollback the engine state matches hardware again, so the next cycle's
// SyncHardwareState sees no mismatch and the action is simply retried.
func (e *Engine) RollbackAction(action Action) {
cs, ok := e.consumers[action.Consumer]
if !ok {
return
}
e.logger.Warn("rolling back engine state after failed action",
"consumer", action.Consumer,
"turn_on", action.TurnOn,
)
cs.Active = !action.TurnOn
if action.TurnOn {
// Turn-on failed: undo activation side-effects
cs.ActivatedAt = time.Time{}
cs.ProactiveCharging = false
cs.ProbeStartGridW = 0
cs.LowPowerCycles = 0
}
// Turn-off failed: just restore Active=true. ActivatedAt is preserved
// (shutdown code doesn't reset it), so min-runtime stays correct.
}
// isHeatingPeriod returns true if heating is appropriate given the current month
// and outdoor temperature. If HeatingMinAmbientC is configured (> 0), ambient
// temperatures above that threshold suppress SG-Ready even within the heating months.