Skip to content

Stop posting the write-only enable flags back at the inverter - #275

Merged
Poshy163 merged 2 commits into
CharlesGillanders:mainfrom
Poshy163:fix/self-consumption-deadlock
Aug 23, 2026
Merged

Stop posting the write-only enable flags back at the inverter#275
Poshy163 merged 2 commits into
CharlesGillanders:mainfrom
Poshy163:fix/self-consumption-deadlock

Conversation

@Poshy163

@Poshy163 Poshy163 commented Aug 23, 2026

Copy link
Copy Markdown
Collaborator

Follow-up to #274, from testing on #267. Two commits: the second is the important one.

What #267 turned up

@dragon2611 probed setTimeChargeBySn directly and established what the two enable flags actually do:

sent effect on the inverter
1 / 0 schedule active, scheduled discharging off
1 / 1 both schedules enabled
0 / 1 scheduled discharging only
0 / 0 switches the inverter to self-consumption

And that getTimeChargeBySn answers 0 for both however the inverter is set — he wrote 1 and read back 0 half a minute later, across a schedule rewrite, a switch to Self-Consumption and a switch back to Time-Based Control. They are write-only.

The bug that follows

main reads those flags, keeps them in the snapshot, and sends them back on every write. On a system whose read is always 0/0 that means every schedule change Home Assistant makes posts 0/0 and asks the inverter to leave timed control. The lockout then reads the same zeros and reports a self-consumption mode — which by then is true, because we put it there.

The only thing that kept it from being worse is the bug that hid it: with the read always 0/0, is_time_based_control_active() is always False, so the lockout refused every write. It jammed far more often than it fired. Removing either half on its own makes things worse, so both are fixed here.

The fix

  • The read is no longer part of the snapshot. Nothing can replay it.
  • Every write sends a value that came from the user — this request first, then what was last recorded — and is refused when neither has an answer, rather than choosing a working mode on someone's behalf.
  • The two switches are the record. They restore their published state on startup and hand it back to the coordinator, and read unknown rather than off until they have one.
  • A recorded 0/0 is a real request — it is how the API is told to run self-consumption — so it still locks the time-based controls, and the switches stay available to undo it (that is the first commit).
  • Backup mode is untouched. There gridCharge/ctrDis are ordinary read-write fields, live-probed in Move scheduling onto the periodic API, with staged edits and Apply #274, and behave exactly as before.

Also confirmed from his probes: an empty period list answers 6001 and a missing one 10001, so the both-lists rule stands — and the error now names the workaround, which is to give the side you don't want a period and turn its switch off.

Consequence worth flagging

setbatterycharge carries only the charge flag, so on periodic systems both switches must be set once before any service call is accepted. Documented in services.yaml and in the Predbat section of the README.

Tests

554 → 568, 100% coverage, ruff clean. TestWriteOnlyEnableFlags pins the regression directly: a system answering 0/0 while running a schedule gets 1/0 posted back, not the zeros. Plus a switch change beating the stored answer, refusal on unknown and half-known state, a flag-only change still reaching the API, and the normaliser stripping the read. TestSwitchStateSurvivesRestart covers restoration.

Both enable flags at 0 means one of two things and the OpenAPI cannot say which:
a non-timed working mode is running, or both timers are simply switched off.
Treating it as the first locked every time-based control, the two switches that
set those flags included — so a system in the second state had no way back to a
timed mode from Home Assistant at all.

A tester on CharlesGillanders#267 hit exactly that: Time-Based Control selected in the app, both
schedule toggles off, every control unavailable, and the services refusing with a
message about a self-consumption mode he was not in.

Scheduled Charging and Scheduled Discharging now stay available in that state,
and turning one on goes out immediately rather than becoming a draft — Apply is
unavailable there too, so a staged flag could never be sent. Raising a flag is
the one write that is safe under either reading: a self-consumption inverter
ignores it, and the next poll simply locks the controls again. Everything else
stays locked, turning a switch off there is still refused, and HA still will not
author the 0/0 state itself.

The refusals now name both possibilities instead of asserting the one HA cannot
verify, and point at the switches that undo it.
@Poshy163
Poshy163 marked this pull request as ready for review August 23, 2026 10:01
@Poshy163

Copy link
Copy Markdown
Collaborator Author

@CharlesGillanders need this to go for a b3 release

@CharlesGillanders
CharlesGillanders requested review from CharlesGillanders and removed request for CharlesGillanders August 23, 2026 11:20
@CharlesGillanders

Copy link
Copy Markdown
Owner

@CharlesGillanders need this to go for a b3 release

Something odd happening with GitHub mobile, no request for a review of this and adding myself as a reviewer doesn't seem to give me an option to review?!? Can you please request a review and I can do so?

@Poshy163

Copy link
Copy Markdown
Collaborator Author

Cool, done, see how that goes

dragon2611 probed setTimeChargeBySn directly on CharlesGillanders#267 and found what the two
enable flags actually do:

    1/0  schedule active, scheduled discharging off
    1/1  both schedules enabled
    0/1  scheduled discharging only
    0/0  switches the inverter to self-consumption

and that getTimeChargeBySn answers 0 for both however the inverter is set. He
wrote 1 and read back 0 half a minute later. They are write-only.

We were reading them, keeping them in the snapshot, and sending them back on
every write. On his system that meant every schedule change Home Assistant made
posted 0/0 and asked the inverter to leave timed control. Then the lockout read
the same zeros and told him he was in self-consumption, which by then he was.
The only reason it was not worse is the bug that hid it: with the read always
0/0 the lockout refused every write, so it jammed far more often than it fired.
Removing either half alone would have made things worse, so both go here.

The read is no longer part of the snapshot. Every write now sends a value that
came from the user - this request first, then what was last recorded - and
refuses outright when neither has an answer, rather than picking a working mode
on their behalf. The two switches are the only place that answer can live, so
they restore their published state on startup and hand it back, and they read
unknown rather than off until they have one.

A recorded 0/0 is a real request, so it still locks the time-based controls and
still leaves the switches available to undo it. Backup mode is untouched: there
the flags are ordinary read-write fields and behave as before.

Also from his probes: an empty list answers 6001 and a missing one 10001, so the
both-lists rule stays, and the error now names the workaround - give the side
you do not want a period and turn its switch off.
@Poshy163 Poshy163 changed the title Leave a way back when both timers read off Stop posting the write-only enable flags back at the inverter Aug 23, 2026
@Poshy163
Poshy163 merged commit a3ead36 into CharlesGillanders:main Aug 23, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants