Repository navigation
Fix the half-known flag crash, and make diagnostics answer the question - #276
Merged
Poshy163 merged 4 commits intoAug 25, 2026
Merged
Conversation
dragon2611 hit a TypeError moving a switch on CharlesGillanders#267: grid_flag = int(draft["charge"].get("gridCharge", grid_flag)) TypeError: int() argument must be ... not 'NoneType' The entity view now reports None for an enable flag nobody has answered for, and .get returns that None rather than the default, because the key is present. Reaching it needs exactly half an answer: one switch restored a usable state and the other did not, so _committed_enable_flags returns None while the entity view still reports time-based control active from the side that is known, which is enough to enter the last-timer guard. The guard now runs only when both committed flags are actually known. Falling back to (1, 1) was the same class of guess this branch removed everywhere else - it claimed the other timer was on in order to decide whether this one was the last. A staged half of None is likewise skipped rather than cast. Nothing escapes as a result: a write still needs both flags, so a half-known pair is refused before it reaches the API, with the message that names the two switches.
Poshy163
marked this pull request as ready for review
August 24, 2026 11:33
Collaborator
Author
|
@CharlesGillanders bump, need this for b4 |
The periodic patcher still carried "cycle"/"cycle_field" entries pointing at gridChargeCycle and ctrDisCycle. Nothing reads them since those fields stopped being part of the resource, and leaving a mapping there that names the write-only pair is an invitation to send them from a read again. Two gaps in the README from the same testing: AlphaESS will store an empty period list even though it refuses to accept one, so a system with no discharge periods cannot be written to at all until it has one; and installs predating the serial-prefixed rename keep IDs carrying the device name, which is why the entity names in the automation section did not match.
Every question asked on CharlesGillanders#267 needed something the download did not carry, so it took three rounds of signed API requests written by hand to establish what the integration already had in memory. The download now includes, per inverter: which store governs and why, the capability flags behind each unavailable control, the write-only enable pair that nothing else can report, the periodic and legacy snapshots as they were read, any open draft with its dirty fields and in-flight state, the duration cooldowns, and the consecutive poll error count. Alongside it, how the client is pacing calls and whether the fast lane is still on, which is what to look at when something else shares the API account. That one file would have answered the whole report: an empty dischargeTimeList, a 0/0 enable pair with nothing recorded, and the capability flags explaining the locked UI. Serials stay anonymised and credentials stay redacted.
Working out what this API does has twice meant asking a user to write and sign their own requests, because nothing in the integration reported what it sent or what came back. Every call already goes through one gate, so that is where the trace belongs. With debug on, each request is written out as it would be typed, with its full response, how long it took and how long it waited for the call spacing. Rejections carry the return code, transport failures the exception, and a retry after a 6053 says so. Nothing is rendered when debug is off, and the clock is not even read then, so the cost is a level check per call. Long responses are capped rather than dropped - one day of power data is thousands of samples. Three additions around it: the write log now carries the enable pair it sent, which is the field that decides the inverter's working mode and the one that was missing when it mattered; staging logs each field and the resulting draft; and a lock or unlock of the schedule surface is announced once, with the read state, the timed-control answer and the recorded flags that decided it. A user reporting that the controls vanished should be answerable from the log alone. README says how to turn it on, and warns that the log carries the serial.
CharlesGillanders
approved these changes
Aug 24, 2026
Owner
|
@Poshy163 What permission changes need to be made so that you can run your own reviews? I'm a roadblock too often at the moment. |
Collaborator
Author
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Three things out of the testing on #267.
1. Crash moving a schedule switch
Reported by @dragon2611 on
main:Since #275 the entity view reports
Nonefor an enable flag nobody has answered for, and.get(key, default)returns thatNonerather than the default — the key is present, its value is unknown.Reaching it needs exactly half an answer: one switch restored a usable state and the other did not.
_committed_enable_flags()then returnsNone(it requires both), whileTime Based Control Activestill reads true from the side that is known — enough to enter the last-timer guard, which casts both.The guard now runs only when both committed flags are actually known. Its
or (1, 1)fallback was the same class of guess #275 removed everywhere else: assuming the other timer was on in order to decide whether this one was the last. A staged half ofNoneis skipped rather than cast. A write still needs both flags, so a half-known pair is refused before it reaches the API.2. Dead mappings left behind
The periodic patcher still carried
"cycle": "gridChargeCycle"/"cycle_field": "gridCharge"entries. Nothing has read them since those fields stopped being part of the resource, and leaving a mapping there that names the write-only pair is an invitation to send them from a read again.3. Diagnostics that answer the question
Every question on #267 needed something the download did not carry, so it took three rounds of hand-signed API requests to establish what the integration already had in memory.
Download diagnosticsnow includes, per inverter:governing_storeandperiodic_read— which surface the system is oncapabilities— the exact flags behind every unavailable control, so a locked UI explains itselfenable_intent/enable_last_sent— the write-only pair, which nothing else can reportperiodic_snapshotandlegacy_snapshot— what each store actually held, including an empty period list or a stale window the app no longer showsdraft— what is staged, which fields are dirty, whether an Apply is in flightPlus a
coordinator.apisection with the current call spacing and whether the fast lane is still on — worth checking now that Predbat's native integration can share the same API account.That one file would have answered the whole report on its own: the empty
dischargeTimeList, the0/0pair with nothing recorded, and the capability flags explaining the locked controls. Serials stay anonymised asinverter_Nand credentials stay redacted.Two README gaps from the same testing are closed too: AlphaESS will store an empty period list even though it refuses to accept one, so a system with no discharge periods cannot be written to until it has one; and installs predating the serial-prefixed rename keep IDs carrying the device name, which is why the entity names in the automation section did not match for him.
Tests
573, 100% coverage, ruff clean. Regressions for the reported crash sequence and for the half-known pair still refusing the write, plus four covering the new diagnostics sections across periodic, backup, drafted and no-store systems.