Skip to content
Merged
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
60 changes: 44 additions & 16 deletions docs/inverter-setup.md
Original file line number Diff line number Diff line change
Expand Up @@ -1654,6 +1654,18 @@ Add the following automations to `automations.yaml` (or configure via the UI):
# Solar power to meet house load, the excess house load is met from grid import, but if there is excess Solar
# power above the house load, the excess solar will be used to charge the battery
# In Sigenergy, this is effectively "self consumption" mode with discharging prohibited
#
# discharge_cut_off_state_of_charge is pinned once here, to current SoC minus a small
# margin, not a hardcoded value and not continuously re-pinned. Sigenergy has confirmed a
# firmware bug: if this is set above current SoC, the inverter actively imports from grid
# to reach it - even with grid_import_limitation at 0 below - so the target must never sit
# above SoC. Setting it once, fixed, is what actually implements "frozen": any real deficit
# against that fixed point (house load, or even the inverter's own standby losses) gets
# corrected by grid import back up to the target, rather than the target chasing SoC
# downward and never enforcing anything. The small margin exists only to stop ordinary
# sensor-reading noise around the target from triggering a real (if tiny) grid import to
# "correct" a fluctuation that was never a real deficit - see the note after this
# automation for the full reasoning.
- conditions:
- condition: state
entity_id: input_select.predbat_requested_mode
Expand All @@ -1667,8 +1679,8 @@ Add the following automations to `automations.yaml` (or configure via the UI):
- action: number.set_value
target:
entity_id: number.sigen_plant_ess_discharge_cut_off_state_of_charge
data:
value: 100
data_template:
value: "{{ [(states('sensor.sigen_plant_battery_state_of_charge') | float(100)) - 0.25, 0] | max }}"
- action: number.set_value
target:
entity_id: number.sigen_plant_grid_import_limitation
Expand All @@ -1681,6 +1693,14 @@ Add the following automations to `automations.yaml` (or configure via the UI):
# excess solar generated, the current SoC level will be held and the excess solar will be exported. If there is
# a shortfall of generated solar power to meet the house load, the battery will discharge to meet the extra load.
# In Sigenergy, this is effectively "self consumption" mode with charging prohibited
#
# charge_cut_off_state_of_charge is left as a simple hardcoded 0 here, unlike the
# discharge cut-off above. A mirrored bug (SoC above charge_cut_off forcing extra
# discharge/export) was considered - 0 is always below current SoC by the same
# structural shape as the confirmed discharge-side bug - but it was never
# vendor-confirmed, and 0 has been in real use across the wider community template
# for months without anyone reporting the kind of dramatic, easily-noticed symptom
# a real mirrored bug would produce. Kept simple rather than adding unproven complexity.
- conditions:
- condition: state
entity_id: input_select.predbat_requested_mode
Expand Down Expand Up @@ -1728,20 +1748,20 @@ Add the following automations to `automations.yaml` (or configure via the UI):
data:
value: 100

- id: automation_sigen_ess_max_charging_limit_input_number_action
alias: Predbat max charging limit action
description: Mapper from input_number.charge_rate to number sigen_plant_ess_max_charging_limit
triggers:
- trigger: state
entity_id: input_number.charge_rate
actions:
- action: number.set_value
target:
entity_id: number.sigen_plant_ess_max_charging_limit
data:
value: '{{ [(states(''input_number.charge_rate'') | float / 1000) | round(2),
states(''sensor.sigen_inverter_ess_rated_charging_power'') | float] | min}}'
mode: single
- id: automation_sigen_ess_max_charging_limit_input_number_action
alias: Predbat max charging limit action
description: Mapper from input_number.charge_rate to number sigen_plant_ess_max_charging_limit
triggers:
- trigger: state
entity_id: input_number.charge_rate
actions:
- action: number.set_value
target:
entity_id: number.sigen_plant_ess_max_charging_limit
data:
value: '{{ [(states(''input_number.charge_rate'') | float / 1000) | round(2),
states(''sensor.sigen_inverter_ess_rated_charging_power'') | float] | min}}'
mode: single

- id: automation_sigen_ess_max_discharging_limit_input_number_action
alias: Predbat max discharging limit action
Expand Down Expand Up @@ -1769,6 +1789,14 @@ so you may need to adapt the above automations and `apps.yaml` (or rename your e
*Important:* Depending upon your electricity supply, you may need to change where **number.sigen_plant_grid_import_limitation** is set to 100 in the first integration to any lower import limit that your electricity supplier may have imposed,
e.g. 18kW roughly corresponds to an 80A supply.

*Important:* Sigenergy have confirmed this is a known firmware bug on their side (not a Predbat or integration issue): even with **grid_import_limitation** set to 0kW, the inverter will still import from the grid to charge the battery if the current SoC is below **discharge_cut_off_state_of_charge**. In practice this has been observed importing several kW, not just a trickle, when the gap between SoC and the cut-off is large - continuing unattended until the target is reached. **grid_import_limitation** is therefore not a reliable backstop against this: the fix is keeping **discharge_cut_off_state_of_charge** pinned so it's never above current SoC, as the automation above does.

The pin is set once, when Freeze Charging starts, rather than continuously updated as SoC changes - and this matters, not just as a simplification. "Frozen" means holding a fixed point; if the target itself kept moving to track live SoC, any downward drift (from real losses or otherwise) would just relocate the target to wherever the battery ended up, with nothing ever correcting it back. A fixed target is what makes the correction mechanism (the same import behaviour that caused the original bug) actually useful: it holds the line against any real deficit, including the inverter's own standby losses, not just customer load. The small margin (0.25 percentage points) below the pinned value exists to cover possible imprecision in that one reading - not ongoing noise tolerance, since the pin is fixed rather than re-sampled, so only the single initial reading matters. It still matters because the underlying mechanism only ever corrects one way: a reading that's a hair low at the moment of pinning would cost a real, if tiny, import to "correct" a gap that was never really there, while a reading that's a hair high costs nothing - so even a one-off imprecise read isn't self-cancelling without some margin.

The margin is clamped at 0 (`[value, 0] | max`) rather than allowed to go negative. This isn't just tidiness: `discharge_cut_off_state_of_charge` is an unsigned 16-bit Modbus register on the wire, and the integration's own write encoding has no guard against a negative value - it would silently wrap around into a huge, nonsensical raw value rather than being rejected. At very low SoC (below the margin) an unclamped template could produce exactly that.

See [batpred#4375](https://github.com/springfall2008/batpred/issues/4375) and the wider [Sigenergy setup discussion](https://github.com/springfall2008/batpred/issues/2077) for the full investigation, including a more advanced (currently experimental, untested) variant that ratchets the target up in response to confirmed solar surplus over each period rather than using a fixed one-off value.

## Sigenergy Cloud

**Experimental**
Expand Down
Loading