Repository navigation
fix(solis): apply slot events immediately so Predbat's read-back sees them - #5247
Merged
Merged
Conversation
… them Predbat writes a slot time to the Solis Cloud select and polls it for about 20s. The select callback only queued the event (#4875), and the queue is drained at the start of run(), which runs once a minute. So the select kept its old value, Predbat logged "Trying to write ... didn't complete", and the value then went through on the next cycle. - Slot times, SoCs, powers and enables of a discovered inverter now run their handler on the calling loop. Those handlers only change the in-memory schedule and make no API calls, so #4875's loop-affinity concern does not apply. Everything that calls the cloud is still queued, as are slot events that arrive before the inverter is discovered. - The slot handlers publish only the slot controls, through a new publish_slot_entities() moved unchanged out of publish_entities(), instead of republishing every entity from the calling thread. - read_and_write_cid() checks the read-back before reporting a failure: a write whose reply errored but whose value landed is a success. - In V2 pass 2, a slot enable or time the inverter refuses is restored to the value the inverter holds, so the select shows it and Predbat writes again on its next execute. It is only restored while the schedule still holds the attempted value, so a newer request that arrives during the write is not overwritten. Pass 1, V1, and slot SoC/current keep their existing retry-every-minute behaviour. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
springfall2008
commented
Sep 26, 2026
springfall2008
commented
Sep 26, 2026
springfall2008
commented
Sep 26, 2026
springfall2008
commented
Sep 26, 2026
springfall2008
commented
Sep 26, 2026
…ored writes
- Slot events now change the schedule through set_slot_schedule_field(),
under a threading.Lock that write_time_windows_if_changed() also holds
while it deep-copies the schedule and that restore_refused_slot_write()
holds for its check-and-update. Slot events arrive on another loop, so
the Solis loop is no longer the only writer. None of the locked sections
await.
- Slot events are applied immediately only once the inverter's schedule has
been decoded (schedules_loaded). The first run() discovers the inverter
before it decodes, and the decode replaces the schedule wholesale, so an
event applied in between was lost; until then it is queued.
- read_and_write_cid() takes the settled re-read on the write-error path
too. A value that lands a moment after a timed-out reply is a success,
instead of being rolled back and then overwritten with the old value.
- One route_event() helper replaces the three copies of the routing
branch, and routing uses self.prefix instead of get_arg('prefix') on
every event.
- restore_refused_slot_write() documents why the whole start-end pair is
restored, and the remaining window: a start event queued before the
first decode.
- The rollback tests now run the real read_and_write_cid() and read_cid()
against a fake inverter with a stale cache. Removing read_cid()'s cache
refresh makes them fail.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
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.
Problem
With Solis Cloud, Predbat sometimes logs that a discharge-time write failed, but the value then reaches the inverter anyway.
Predbat writes the slot time to
select.predbat_solis_<sn>_discharge_slot1_start_timeand polls the select for about 20 s (write_and_poll_sleep2 s ×INVERTER_MAX_RETRY10). Since #4875,SolisAPI.select_eventonly queues the event. The queue is drained at the start ofrun(), andComponentBaseonly callsrun()once a minute (seconds % 60 == 0). So the select keeps its old value past Predbat's poll, and Predbat logsTrying to write … didn't complete. On the next cycle the queue drains, the handler updates the schedule, andrun()writes the value to the inverter.A second, smaller problem:
read_and_write_cidreports a failure whenever the write call errors, even if the read-back it has just taken already shows the new value. That happens when the SolisCloud relay applies a write but its reply times out.Changes
charge_discharge_time_windowsand make no API calls, so the loop-affinity problem fix(solis): queue entity events for the component loop #4875 fixed doesn't apply. The cloud write still happens inrun().publish_slot_entities(inverter_sn)holds the slot loops, moved unchanged apart from indentation out ofpublish_entities(). Handlers call it instead of republishing every entity from the calling thread.read_and_write_cidchecks the read-back first. If the read-back matches, the write counts as a success, and the log notes the error reply.restore_refused_slot_writesets the schedule back to what the inverter holds (cached_values). The select then shows that value, and Predbat writes again on its next execute.Tests
Seven new tests in
test_solis.py:read_and_write_cidtreats a landed-but-errored write as a successtest_rate_setpoints_do_not_drift_with_battery_voltagenow callspublish_entities()after the slot handler. It had relied on the handler republishing the inverter-widemax_charge_power, whichrun()still does every minute../run_all --test solispasses. It failed on the five behaviour tests before the fix, and passes on unmodified main../run_all --quickpassespre-commitpasses🤖 Generated with Claude Code