Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion apps/predbat/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -2460,7 +2460,8 @@
"has_charge_enable_time": True,
"has_discharge_enable_time": True,
"has_target_soc": True,
"has_reserve_soc": False,
# The Battery Reserve SOC (CID 157), a floor while the component keeps the Battery Reserve bit on
"has_reserve_soc": True,
Comment thread
springfall2008 marked this conversation as resolved.
Comment thread
springfall2008 marked this conversation as resolved.
"has_timed_pause": False,
"charge_time_format": "HH:MM:SS",
"charge_time_entity_is_option": True,
Expand Down
224 changes: 179 additions & 45 deletions apps/predbat/solis.py

Large diffs are not rendered by default.

5 changes: 3 additions & 2 deletions apps/predbat/tests/test_discovery_contract.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,8 +120,9 @@ def test_agreement_names_each_disagreement():


def test_dummied_settings_follow_the_row():
"""SolisCloud has no reserve, and no cloud type has a GE mode entity."""
assert "reserve" in dummied_settings("SolisCloud")
"""GS has no reserve, SolisCloud now does (its Battery Reserve SOC), and no cloud type has a GE mode entity."""
assert "reserve" in dummied_settings("GS")
assert "reserve" not in dummied_settings("SolisCloud")
assert "reserve" not in dummied_settings("SunsynkCloud")
assert "inverter_mode" in dummied_settings("SunsynkCloud")
assert "inverter_mode" not in dummied_settings("GE")
Expand Down
333 changes: 279 additions & 54 deletions apps/predbat/tests/test_solis.py

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions docs/components.md
Original file line number Diff line number Diff line change
Expand Up @@ -1212,6 +1212,9 @@ Integrates with Solis inverters for monitoring and controlling Solis battery sys
- Manually set `soc_max` in `apps.yaml` with your battery capacity in kWh (recommended), or
- Leave `soc_max` unset and allow Predbat to automatically detect battery size from historical charging data (requires several days of data)
- Supports both V1 (older firmware) and V2 (newer firmware) time window formats
- **Reserve and holds**: Predbat's reserve is the inverter's **Battery Reserve SOC**, and Predbat keeps the inverter's Battery Reserve switched on so that SOC is always honoured as a discharge floor. With **switch.predbat_set_reserve_enable** on, Predbat also changes it - `set_reserve_min` normally, and just above the current SoC to hold the battery while a car or iBoost charges and for freeze or hold charging. With it off, Predbat leaves the Battery Reserve SOC as you set it and plans against it
- **Manual configuration** (`solis_automatic: false`): bind `reserve` to `number.predbat_solis_<serial>_reserve_soc`, not the over-discharge SOC. Predbat warns at startup if `reserve` still points at `..._over_discharge_soc`, as it would otherwise raise that safety floor to hold the battery
- **Freeze charging**: inside a charge slot with the battery already at the slot's target SOC, Predbat sets the storage mode to `Self-Use - No Grid Charging`, so the slot holds the battery without importing from the grid while solar can still charge it. It keeps the hold through a dip of 1% below the target before charging again. On V2 firmware this mode has the same register value as `Self-Use - No Timed Charge/Discharge`, which is how the storage mode select shows it
- Automatic configuration available - sets up all required Predbat sensors automatically
- **PV-only inverters**: an inverter Solis Cloud reports as having no battery is never managed as a battery inverter and is never written to, but its generation is still included in `pv_today` and `pv_power` so the array total covers the whole roof. Its load and grid readings are left out, as those registers can overlap with the battery inverter's on a shared-CT installation - set `pv_today`/`pv_power`/`load_today` manually with `solis_cloud_pv_load_ignore: true` if you need something different
- **Inverter timezone must match Predbat's `timezone` setting**: charge/discharge slot times are written to the inverter as plain `HH:MM` values with no timezone attached. The inverter interprets these using its own configured timezone, not Predbat's. If your inverter's timezone is set to UTC (or anything other than Predbat's `timezone`, `Europe/London` by default), the resulting charge/discharge windows will be offset by the difference - for example, a full hour out whenever British Summer Time is in effect. Set the inverter's own timezone to match Predbat's `timezone` setting to avoid this.
Expand Down
4 changes: 2 additions & 2 deletions docs/discovery-catalogue.md
Original file line number Diff line number Diff line change
Expand Up @@ -228,8 +228,8 @@ An inverter record holds enough to rebuild that inverter's definition - the per-
has a reserve, a target SoC, charge/discharge enable switches, idle times or a timed pause follows
from which settings `entities` binds to a writable (`rw`) entity; and protocol detail such as the time
format or whether the charge rate is set in watts or amps is read from those entities' descriptors.
A setting Predbat replaces with a placeholder for this inverter type - SolisCloud's `reserve`, say - is
left out of `entities`. See the design in
A setting Predbat replaces with a placeholder for this inverter type - the Solis Modbus (`GS`) type's
`reserve`, say - is left out of `entities`. See the design in
`docs/superpowers/specs/2026-09-24-discovery-inverter-record-vocabulary-design.md`.

### Writing a reporter
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ is the first to change behaviour.
| D6 | `ratings` are keyed by Predbat setting name, in Predbat's units. A sensor binding for a rating lives in `entities` under the same key. |
| D7 | A site-wide export limit is reported as each inverter's share of it. Predbat sums `export_limit` across inverters, so the shares add back up to the site figure. |
| D8 | `functions` reports what the component believes the device is, whether probed or assumed. There is no marker for an assumed value. |
| D9 | SolisCloud does not write the reserve (it often won't change); it presents the battery minimum SoC instead. Its record reports `battery_min_soc` and no `reserve` (section 3). |
| D9 | SolisCloud does not write the reserve (it often won't change); it presents the battery minimum SoC instead. Its record reports `battery_min_soc` and no `reserve` (section 3). **Superseded:** `reserve` was then bound to the over-discharge SOC. It is now the Battery Reserve SOC (CID 157), a floor while the component keeps the Battery Reserve bit on, so SolisCloud has a reserve and its record reports both `reserve` (rw) and `battery_min_soc` (r). |
| D10 | A record describes its own device, not the fleet. `automatic_config()` today binds some settings only when every inverter has them (GE Cloud: when any has them); the record states what this device has, and piece 3's coordinator applies whatever fleet rule it keeps. |
| D11 | A record describes the device, not the user's opt-outs. Settings that stop a binding (`automatic_ignore_pv`, `givtcp_rest_power_ignore`) do not remove entities from the record; piece 3's coordinator applies them. |
| D12 | A PV-only device's record carries its `pv_power` and `pv_today` as `access: r` entities - still with no `inverter_type` and no `capabilities` - so its generation is not lost when the coordinator configures from records (the GH#4922 regression). |
Expand Down Expand Up @@ -158,7 +158,7 @@ published, because its component publishes them itself before `build_discovery()
A setting that `inverter.py` replaces with a dummy entity for this inverter type is left out of `entities`,
because Predbat never reads or writes the component's binding for it. Today that means any setting whose
presence flag is False in the type's row (section 2), and `inverter_mode` when both `has_ge_inverter_mode`
and `has_ge_eco_toggle` are False. Solis's `reserve` is one such case (D9).
and `has_ge_eco_toggle` are False. The Solis Modbus (`GS`) type's `reserve` is one such case; SolisCloud's was until D9 was superseded.

A rating with a sensor appears twice: the number in `ratings`, the binding in `entities` with
`access: r`. The coordinator binds the entity where one exists and the rating's number otherwise.
Expand Down
2 changes: 1 addition & 1 deletion tools/debug-journal.md

Large diffs are not rendered by default.

Loading