From 8bfd525d9e320a87d813fd8424b08cef3a3717dd Mon Sep 17 00:00:00 2001 From: Fredrik Ahlgren Date: Wed, 29 Jul 2026 07:54:06 +0200 Subject: [PATCH] test(sungrow): confirm the battery before commanding it TestSungrowZeroBatteryCommandForcesIdle sent a battery setpoint to a freshly loaded driver with no poll in between. The mock answers 0 everywhere, so register 4999 gives no device type, detection settles on "unknown", and device-drivers #40 refuses the write. The refusal is right. Sungrow ships two families behind one driver and an SG string inverter implements none of 13049-13051; writing them anyway is the SG12RT bug, and the read-back that would catch it fails on that device too, so the driver reports success. Nothing is lost by confirming first. The test's own assertions show it was never testing a release: it expects EMS mode 2 -- forced -- with command 0xCC and setpoint 0. That is a dispatch to a battery pinned at zero, not a device handed back. The release is driver_default_mode, which FTW reaches through SendDefault on shutdown, lease expiry and the watchdog, and which no guard touches. So the mock now answers 4999 as an SH8.0RT and the driver polls once before the command. What the test checks is unchanged. Co-Authored-By: Claude Opus 5 Signed-off-by: Fredrik Ahlgren --- .../sungrow-zero-test-confirms-the-battery.md | 5 +++++ go/internal/drivers/sungrow_driver_test.go | 14 ++++++++++++++ 2 files changed, 19 insertions(+) create mode 100644 .changeset/sungrow-zero-test-confirms-the-battery.md diff --git a/.changeset/sungrow-zero-test-confirms-the-battery.md b/.changeset/sungrow-zero-test-confirms-the-battery.md new file mode 100644 index 00000000..3ac5c077 --- /dev/null +++ b/.changeset/sungrow-zero-test-confirms-the-battery.md @@ -0,0 +1,5 @@ +--- +"ftw": patch +--- + +The Sungrow zero-power test confirms the battery before commanding, matching the driver's refusal to write EMS registers a string inverter does not implement. diff --git a/go/internal/drivers/sungrow_driver_test.go b/go/internal/drivers/sungrow_driver_test.go index eb2271fc..fa4be710 100644 --- a/go/internal/drivers/sungrow_driver_test.go +++ b/go/internal/drivers/sungrow_driver_test.go @@ -56,12 +56,26 @@ func TestSungrowZeroBatteryCommandForcesIdle(t *testing.T) { env := NewHostEnv("sungrow", tel).WithModbus(modbus) env.BatteryCapacityWh = 9600 + // Answer register 4999 as an SH8.0RT so the driver classifies this as a + // hybrid. Sungrow ships two families behind one driver and an SG string + // inverter implements none of 13049-13051, so the driver refuses a battery + // setpoint until the model has named itself a hybrid or a battery register + // has answered. Without this the mock answers 0 everywhere, detection + // settles on "unknown", and the refusal is correct rather than a bug -- + // see srcfl/device-drivers#40 and #43. + modbus.regs[4999] = 0x0E0E + d, err := NewLuaDriver("../../../drivers/sungrow.lua", env) if err != nil { t.Fatalf("load: %v", err) } defer d.Cleanup() + if _, err := d.Poll(context.Background()); err != nil { + t.Fatalf("poll: %v", err) + } + modbus.resetWrites() + cmd, _ := json.Marshal(map[string]any{"action": "battery", "power_w": 0}) if err := d.Command(context.Background(), cmd); err != nil { t.Fatalf("command: %v", err)