Repository navigation
feat(solis): SolisCloud freeze charge and holds via the Battery Reserve - #5265
Merged
Merged
Conversation
Freeze charge: SolisCloud has no reserve, so Predbat freezes by keeping the charge window with the target at the current SoC. The component treated that as a normal charge on V2 (Self-Use, grid charging on - it topped up from the grid) and, on V1, zeroed the charge current at target so the 0A rule chose Feed-in priority, exporting PV during the freeze. - is_holding_in_charge_slot(): a charge slot open now with the battery at or above its target is only holding. Write 'Self-Use - No Grid Charging' (ENUM_SELF_USE_HOLD: 3 on V1, where bit 1 is the slot enable; 1 on V2) ahead of the 0A -> Feed-in rule Holds (car/iBoost, freeze or hold on reserve) did nothing on SolisCloud: has_reserve_soc was False and reserve was bound to the over-discharge SOC. - Bind reserve to the Battery Reserve SOC (CID 157) and declare has_reserve_soc, keeping battery_min_soc on the over-discharge SOC - set_storage_mode_if_needed() sets the Battery Reserve bit (CID 636 bit 4) while set_reserve_enable is on, so that SOC is a real discharge floor; it is left alone otherwise, as enabling it unmanaged would enforce whatever reserve SOC the inverter already held. Outside slots the component already writes a No-Grid-Charging mode, so a raised reserve is never imported to - Discovery record reports reserve (rw) as well as battery_min_soc; spec D9 marked superseded - Docs (Solis component notes, discovery catalogue) and debug journal Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
springfall2008
commented
Sep 27, 2026
springfall2008
commented
Sep 27, 2026
springfall2008
commented
Sep 27, 2026
springfall2008
commented
Sep 27, 2026
springfall2008
commented
Sep 27, 2026
springfall2008
commented
Sep 27, 2026
springfall2008
commented
Sep 27, 2026
springfall2008
commented
Sep 27, 2026
springfall2008
commented
Sep 27, 2026
springfall2008
commented
Sep 27, 2026
springfall2008
commented
Sep 27, 2026
springfall2008
commented
Sep 27, 2026
…s at once, hold hysteresis - Always set the Battery Reserve bit. set_reserve_enable only decides whether Predbat changes the Battery Reserve SOC; with it off inverter.py plans against the live reserve, so the inverter must enforce it too. Gating the bit left the plan flooring at a register the inverter ignored, a reserve pinned after switching off mid-hold, and a mixed-fleet hole - show_soc_limit_now(): a battery SOC limit change updates the cache and its entity at once; the write still happens from the event queue. Non-slot events wait for the next run(), far beyond Predbat's 2s write verify - 1% hysteresis on the charge-slot hold (SOLIS_HOLD_HYSTERESIS) so the mode does not flip across the target boundary, without stopping a charge short - V1 zeroes the slot current from the same hold check; one set_hold_storage_mode() for both firmware branches; SOLIS_SOC_LIMITS shared by publishing, the immediate update and the write handler - Warn at startup when reserve still points at the over-discharge SOC - Tests: hold hysteresis, hold values with the reserve bit (19 V1 / 17 V2), hold ending with the slot window, immediate SOC limit, the warning; existing storage-mode value tests now carry bit 4 - Docs: reserve always honoured, manual reserve binding, V2 hold label 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.
Summary
This is the SolisCloud counterpart to #5258 (Solis FB00 over Modbus). It fixes two things that didn't behave as the planner models them on SolisCloud: freeze charging, and holding the battery for a car or iBoost.
1. Freeze charge
SolisCloud has no reserve, so Predbat freezes by keeping the charge window, with the target set to the current SoC and a discharge rate of 0. The component didn't treat that as a hold:
Self-Usewith grid charging on, so the inverter topped the battery up from the grid.Fix:
is_holding_in_charge_slot()detects a charge slot that is open now with the battery at or above its target. In that case the component writesSelf-Use - No Grid Charging(newENUM_SELF_USE_HOLD) before the 0 A → Feed-in rule is considered:The slot stops the battery discharging, grid charging is off, and PV can still charge it. The same rule applies when a normal charge reaches its target.
2. Holds (car/iBoost, freeze or hold on reserve)
These did nothing on SolisCloud:
has_reserve_socwas False,execute.pyturned reserve functions off, andreservewas bound to the over-discharge SOC.reserveis now bound to the Battery Reserve SOC (CID 157), and the row declareshas_reserve_soc: True.battery_min_socstays the over-discharge SOC (CID 158).set_storage_mode_if_needed()now always sets bit 4, so the reserve is always honoured.set_reserve_enableonly decides whether Predbat changes it. With it off, the user manages the reserve, Predbat plans against it, and the inverter enforces it (changed in review round 1).show_soc_limit_now()) and are written from the event queue. Non-slot events are only handled at the nextrun(), far later than Predbat's 2 s write check.Other
reserve(rw) as well asbattery_min_soc(r). Spec D9 ("SolisCloud does not write the reserve") is marked superseded; it dated fromreservebeing bound to the over-discharge SOC.For review
set_reserve_enableon, Predbat sets it (set_reserve_minnormally, SoC + 1 during a hold). With it off, the value already on the inverter applies, and Predbat plans against it.SOLIS_HOLD_HYSTERESIS).reservestill points at the over-discharge SOC.Self-Use - No Grid Chargingis a new option in the component's storage-mode select.is_holding_in_charge_slot()compares the inverter's reported SoC with the slot's integer target. If the reported SoC is a fraction below the target (for example 61.6 against 62), the first cycle of a freeze may still top up that fraction before the hold starts.Live test (SolisCloud)
Self-Use - No Grid Charging(1 on V2, 3 on V1). No import, no discharge, and PV still charges the battery.set_reserve_min, and the battery discharges normally.Testing
./run_all --test solis: all pass. New tests:test_write_time_windows_hold_in_charge_slot: V1/V2, at, above and below target, including 0 A.test_compute_solis_mode_value_hold_and_reserve.test_storage_mode_sets_battery_reserve_with_set_reserve_enable.The hold test failed before the change (
Self-Usewhere the hold mode was expected). The Battery Reserve tests fail when bit 4 isn't applied (mutation-checked). The catalogue andautomatic_configtests now expectreserve→reserve_soc../run_all --test discovery_contractand the other discovery tests: pass. The dummied-reserve example is nowGS../run_all --quick: all passed.pre-commit: clean.
🤖 Generated with Claude Code