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:
@@ -88,11 +88,11 @@ func TestExecuteTurnOnSGReady(t *testing.T) {
|
||||
|
||||
act := testActuator(t, ipFrom(sgSrv), ipFrom(dummySrv), ipFrom(dummySrv))
|
||||
|
||||
err := act.Execute(context.Background(), []engine.Action{
|
||||
errs := act.Execute(context.Background(), []engine.Action{
|
||||
{Consumer: engine.ConsumerSGReady, TurnOn: true, Reason: "test"},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Execute failed: %v", err)
|
||||
if errs[0] != nil {
|
||||
t.Fatalf("Execute failed: %v", errs[0])
|
||||
}
|
||||
|
||||
if !sg.state {
|
||||
@@ -112,11 +112,11 @@ func TestExecuteTurnOffWallboxA(t *testing.T) {
|
||||
|
||||
act := testActuator(t, ipFrom(dummySrv), ipFrom(wbASrv), ipFrom(dummySrv))
|
||||
|
||||
err := act.Execute(context.Background(), []engine.Action{
|
||||
errs := act.Execute(context.Background(), []engine.Action{
|
||||
{Consumer: engine.ConsumerWallboxA, TurnOn: false, Reason: "import"},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Execute failed: %v", err)
|
||||
if errs[0] != nil {
|
||||
t.Fatalf("Execute failed: %v", errs[0])
|
||||
}
|
||||
|
||||
if wbA.state {
|
||||
@@ -164,13 +164,14 @@ func TestExecuteMultipleActions(t *testing.T) {
|
||||
|
||||
act := testActuator(t, ipFrom(sgSrv), ipFrom(wbASrv), ipFrom(wbBSrv))
|
||||
|
||||
err := act.Execute(context.Background(), []engine.Action{
|
||||
for i, err := range act.Execute(context.Background(), []engine.Action{
|
||||
{Consumer: engine.ConsumerSGReady, TurnOn: true},
|
||||
{Consumer: engine.ConsumerWallboxA, TurnOn: true},
|
||||
{Consumer: engine.ConsumerWallboxB, TurnOn: true},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Execute failed: %v", err)
|
||||
}) {
|
||||
if err != nil {
|
||||
t.Fatalf("Execute action %d failed: %v", i, err)
|
||||
}
|
||||
}
|
||||
|
||||
if !sg.state || !wbA.state || !wbB.state {
|
||||
|
||||
Reference in New Issue
Block a user