Summary
Debug bundle bess-debug-2026-07-22-120026.md (reporter restarted the UI and
saw GRID CHARGING / Battery First / Grid Charge ON for the current
period, but the physical inverter's TOU period list had no period covering
that time window — screenshots confirm the mismatch).
Root cause 1: failed TOU hardware writes are silently treated as success
GrowattMinController.write_schedule_to_hardware (core/bess/growatt_min_controller.py)
catches each per-segment hardware write exception internally and only logs
it — it never re-raises. BatterySystemManager._apply_schedule
(core/bess/battery_system_manager.py:2481-2507) already has retry logic
built for exactly this case (self._hardware_write_pending = True on
exception, forcing a retry next cycle), but it never sees the exception
because it was swallowed one layer down. So:
2026-07-22 11:30:13 — write of TOU segment 6 (15:30-15:44) is attempted.
2026-07-22 11:30:55 — fails after 4 retries with 500 Server Error from
growatt_server/update_time_segment (HA core itself was returning 500s
intermittently most of the day — external instability, not a BESS Manager
bug).
growatt_min_controller:1316 - FAILED: Failed to update TOU segment is
logged, but write_schedule_to_hardware returns normally.
_apply_schedule's except never fires, _hardware_write_pending is
incorrectly cleared to False, "Schedule applied successfully" is
logged.
- Because the in-memory controller (holding the intended schedule) had
already been swapped in before the write (by design, so strategic intents
stay available across a write failure), and schedule-diffing compares
against this same in-memory intended state rather than a hardware
readback, the failed write is now indistinguishable from a successful one
on every later comparison — it's never retried, and the dashboard shows
the intended schedule as fact.
Root cause 2: dashboard/inverter-status endpoints crash on unavailable battery_soc
Same debug bundle shows repeated Error generating dashboard data: unsupported operand type(s) for /: 'NoneType' and 'float' and Error getting inverter status: ... whenever sensor.battery_soc goes
unavailable (same HA-instability window).
backend/api_dataclasses.py:953 — battery_soe = (battery_soc / 100.0) * battery_capacity — battery_soc comes from
controller.get_battery_soc() (core/bess/ha_api_controller.py:1129),
which returns None when the sensor is unavailable.
backend/api.py:1543 — same pattern in get_inverter_status.
Both endpoints return an opaque 500 with a TypeError message instead of a
clear "sensor unavailable" error.
Fix
write_schedule_to_hardware: collect per-segment failures and raise once
after attempting all segments, so the existing retry plumbing in
_apply_schedule actually engages.
- Raise a clear error before the division in both call sites when
battery_soc is None, instead of an opaque TypeError.
Summary
Debug bundle
bess-debug-2026-07-22-120026.md(reporter restarted the UI andsaw
GRID CHARGING/Battery First/Grid Charge ONfor the currentperiod, but the physical inverter's TOU period list had no period covering
that time window — screenshots confirm the mismatch).
Root cause 1: failed TOU hardware writes are silently treated as success
GrowattMinController.write_schedule_to_hardware(core/bess/growatt_min_controller.py)catches each per-segment hardware write exception internally and only logs
it — it never re-raises.
BatterySystemManager._apply_schedule(
core/bess/battery_system_manager.py:2481-2507) already has retry logicbuilt for exactly this case (
self._hardware_write_pending = Trueonexception, forcing a retry next cycle), but it never sees the exception
because it was swallowed one layer down. So:
2026-07-22 11:30:13— write of TOU segment 6 (15:30-15:44) is attempted.2026-07-22 11:30:55— fails after 4 retries with500 Server Errorfromgrowatt_server/update_time_segment(HA core itself was returning 500sintermittently most of the day — external instability, not a BESS Manager
bug).
growatt_min_controller:1316 - FAILED: Failed to update TOU segmentislogged, but
write_schedule_to_hardwarereturns normally._apply_schedule'sexceptnever fires,_hardware_write_pendingisincorrectly cleared to
False,"Schedule applied successfully"islogged.
already been swapped in before the write (by design, so strategic intents
stay available across a write failure), and schedule-diffing compares
against this same in-memory intended state rather than a hardware
readback, the failed write is now indistinguishable from a successful one
on every later comparison — it's never retried, and the dashboard shows
the intended schedule as fact.
Root cause 2: dashboard/inverter-status endpoints crash on unavailable battery_soc
Same debug bundle shows repeated
Error generating dashboard data: unsupported operand type(s) for /: 'NoneType' and 'float'andError getting inverter status: ...wheneversensor.battery_socgoesunavailable(same HA-instability window).backend/api_dataclasses.py:953—battery_soe = (battery_soc / 100.0) * battery_capacity—battery_soccomes fromcontroller.get_battery_soc()(core/bess/ha_api_controller.py:1129),which returns
Nonewhen the sensor is unavailable.backend/api.py:1543— same pattern inget_inverter_status.Both endpoints return an opaque 500 with a
TypeErrormessage instead of aclear "sensor unavailable" error.
Fix
write_schedule_to_hardware: collect per-segment failures and raise onceafter attempting all segments, so the existing retry plumbing in
_apply_scheduleactually engages.battery_socisNone, instead of an opaqueTypeError.