Repository navigation
fix(inverter): seed the dummy reserve of a register-less inverter in percent, not kWh (#5850) - #5870
Conversation
…percent, not kWh (#5850) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
springfall2008
left a comment
There was a problem hiding this comment.
Automated comment from the triage bot.
Review of 222d7ad: the seeding fix is sound — 1 finding inline, 0 in the body.
What was checked: the seed/comparison mechanics (unit_conversion returns a float for numeric states, so the != read-back is stable and cannot rewrite every cycle), the created_attributes lookup (set unconditionally in create_entity(), so no KeyError), the claim in the new comment about who writes the dummy (all three adjust_reserve(0) sites clamp up to reserve_percent; the hold/freeze sites are gated on set_reserve_enable, which execute.py forces off for these types; self_test runs only under INVERTER_TEST), device bounds for the dummy (no min/max attributes, so (None, None)), the new test's registration, red/green design and restore/teardown, and the literal (GH#5003) interaction.
…rrects it (#5850) When refresh_config() rewrites a dummy reserve still holding the old kWh seed, re-read reserve_percent_current (and reserve_current) so the balancer sees the corrected percent in that cycle rather than one cycle later. Gated on the write, so a fresh dummy and the GH#5003 literal-reserve case still report the arg they replaced on construction. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This is an automated draft PR generated from issue #5850 — a maintainer should review it before merging.
Fixes #5850
Summary
For inverter types with no reserve register (
has_reserve_soc: False— GS, SE, SX4, SF, SFMB, HU, SK, SIG, SIGCLOUD),refresh_config()seeded the dummyreserveentity withself.reserve(kWh) while labelling it%. The next refresh read that back asreserve_percent_current, whichbalance_inverters()uses for its above-reserve check.create_missing_argdefault) is now seeded withself.reserve_percent.create_entity()never overwrites an existing state, so an install that already has the kWh seed would keep it. After creating the entity, if what it reads back (via the sameget_arg(..., required_unit="%")that line 774 uses) differs fromreserve_percent, the percent is written. Nothing on the device needs preserving: these inverters have no reserve register, so Predbat owns the value. Withset_reserve_enableforced off for these types (execute.py:1460), the only writes Predbat makes to this entity areadjust_reserve(0)on the calibration and reset paths, and those are clamped up toreserve_percentanyway.Testing
coverage/./run_pre_commit: all hooks passed, and the--quicksuite it then runs passed.New
test_reserve_dummy_percentintests/test_inverter.py, with two cases. Both use an HU inverter withsoc_max10 kWh,set_reserve_min50 andbattery_min_soc3.battery_min_socis set low because the triage found its floor hides the kWh value in default configs.reserve_dummy_seeded_percent: a fresh dummy.reserve_dummy_kwh_seed_corrected: a dummy that already holds the old kWh seed of5.0.Both cases check the dummy's own state and unit (
50,%) andreserve_percent_current == 50after a refresh.Fails without the fix, passes with it. With only
inverter.pystashed,tools/triage_test.sh inverterfailed on both cases (dummy reserve should be 50 % got 5.0 %,reserve_percent_current should be 50 got 5). With the fix restored, the wholeinvertermodule passes.Notes
reserve_percent_current. On the first refresh that corrects an old kWh seed, that refresh'sreserve_percent_currentis still the stale value. It reads correctly from the next refresh, one plan cycle later. I chose not to also overwritereserve_percent_currentin the same pass, becausereserve_literal_huawei(GH#5003) deliberately expects a hard-wired apps.yaml reserve literal to be reported as the current reserve on construction.self_test()callsadjust_reserve(100/6/4). On these types the next refresh now resets the dummy back toreserve_percent. Since the dummy controls no device, that seems right.impact(refresh_config, upstream)is LOW (2 direct callers,Inverter.__init__and the per-cycle refresh).detect_changesshows onlyrefresh_configchanged outside the tests.🤖 Generated with Claude Code