Skip to content

Commit c0d901d

Browse files
docs(sigenergy): fix rate automation sensor names, re-apply mode on restart, stop zeroing import limit in freeze (#5464)
* docs(sigenergy): fix rate automation sensor names, re-apply mode on restart, stop zeroing import limit in freeze (#5459) - Rate automations: cap against the plant-level sensor.sigen_plant_ess_rated_charging/discharging_power (what the template's battery_rate_max already uses, and what matches the plant-level limits being written). The inverter prefix with "-ing" suffix used here doesn't exist in the integration. Skip the write if the rate input isn't a number (rather than writing 0kW), and treat an unavailable rated-power sensor as no cap. - Rework the renamed-entities note around the plant/inverter split, and warn that renaming charge_rate/discharge_rate in apps.yaml but not in the automations silently stops them firing. - Restarts: drop initial: from the three helpers so HA restores them instead of resetting them without a state change, and re-apply the mode on HA start and when Remote EMS goes from off to on. A restart at the end of a force export otherwise left the plant on Command Discharging while Predbat already wanted Demand and wrote nothing. Wait for the select and SoC sensor before writing, and make the Freeze Charging pin fall back to 0, not 100: a re-run with SoC unreadable would otherwise pin the cut-off at 99.75% and trigger the documented forced-import firmware bug. - Stop setting grid_import_limitation to 0 in both freeze modes (#4911): the cut-offs enforce the freeze, 0 contradicts Freeze Charging's own definition, and it stalls Sigenergy EV chargers behind the plant CT. Tell existing users to reset it once. - Say that Remote EMS must be on, fix the enable-list entry to the daily_load_consumption name the template uses, and add an optional Remote EMS watchdog. All of this is running on a live Sigenergy system: the import-limit change since 9 Sep, the rest since 9 Oct. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(sigenergy): correct enable list and battery_rate_max claim (#5459) The enable list named sensor.sigen_plant_max_active_power, which the integration enables by default; the template's inverter_limit uses sensor.sigen_plant_available_max_active_power, which is disabled by default. The rate-automation note also said battery_rate_max uses both plant rated-power sensors; it uses only the discharging one. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(sigenergy): re-apply rate limits after restart or Remote EMS re-enable (#5459) The rate automations fired only when the input number changed. Now that the helpers restore their state across a restart, a limit write lost to a restart or an integration outage was never retried: Predbat saw the input number already at the value it wanted. Both rate automations now also trigger on Home Assistant start and on Remote EMS going from off to on, wait for the plant limit entity before writing, and run in restart mode so the latest rate wins. The condition reads the input number directly, as trigger.to_state does not exist for the new triggers. Also tell new users to set the rate helpers once, as without initial: they start at 0 W, which Predbat reads as the battery being held. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(sigenergy): don't re-apply a 0 W rate on restart, and correct what the re-apply covers (#5459) On a new install the rate input numbers start at 0 W, so the new Home Assistant start trigger would write a 0 kW charge and discharge limit to the plant on the first restart, blocking the battery. The re-apply triggers now skip a 0 W rate; a real rate change still writes 0. The comment also claimed the triggers retry a write lost to an integration outage, which they don't (recovery is unavailable to available, which nothing triggers on). It now names what they do cover: a write lost to a restart, and a limit changed by another service while Remote EMS was off. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(sigenergy): re-apply 0 W holds after a restart again (#5459) The previous commit skipped a 0 W rate on the re-apply triggers to spare new installs a 0 kW limit on their first restart. That also stopped a lost or overwritten hold (Predbat's deliberate rate 0) from being put back, which is the main case the triggers exist for: Predbat only writes the rate when it reads a different value, so it never re-sends a hold it already sees. A new install needs no special case, because Predbat writes its non-zero rate on its first cycle. Drop the guard and say in the new-install note that the restart writes the 0 W default to the plant. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(sigenergy): correct the rate guard rationale and the Axle watchdog condition (#5459) The rate automation comments said the is_number condition and the float(99) fallback avoid writing a 0 kW limit. In current Home Assistant '| float' with no default raises on 'unavailable', so the old behaviour was a template error and no write; the comments now say so. The watchdog's optional Axle condition only works with Predbat's own Axle integration enabled, under the configured prefix (axle.py builds the entity as binary_sensor.<prefix>_axle_event). Without it the condition never passes and the watchdog never runs; the comment now says this. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
1 parent c000b53 commit c0d901d

1 file changed

Lines changed: 123 additions & 31 deletions

File tree

‎docs/inverter-setup.md‎

Lines changed: 123 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1885,15 +1885,22 @@ To integrate your Sigenergy Sigenstor inverter with Predbat, you will need to fo
18851885
- All the Sigenergy entities referenced in `apps.yaml` need to be enabled for Predbat to use them. The following are disabled by default and will need enabling:
18861886

18871887
- sensor.sigen_plant_available_max_discharging_capacity
1888-
- sensor.sigen_plant_daily_consumed_energy
1888+
- sensor.sigen_plant_daily_load_consumption
18891889
- number.sigen_plant_ess_backup_state_of_charge
18901890
- number.sigen_plant_ess_charge_cut_off_state_of_charge
18911891
- number.sigen_plant_ess_discharge_cut_off_state_of_charge
18921892
- number.sigen_plant_ess_max_charging_limit
18931893
- number.sigen_plant_ess_max_discharging_limit
1894-
- sensor.sigen_plant_max_active_power
1894+
- sensor.sigen_plant_available_max_active_power
18951895

1896-
- The following additions are needed to facilitate integration with Predbat and need to be put into Home Assistant's `configuration.yaml` or configured via the HA user interface:
1896+
- Turn on **switch.sigen_plant_remote_ems_controlled_by_home_assistant** ("Remote EMS (Controlled by Home Assistant)"). While it is off the plant ignores **select.sigen_plant_remote_ems_control_mode** entirely and runs its own logic,
1897+
so none of the mode changes made by the automations below take effect. The cut-off and limit numbers are still obeyed without it, which can make a setup look partly working.
1898+
1899+
- The following additions are needed to facilitate integration with Predbat and need to be put into Home Assistant's `configuration.yaml` or configured via the HA user interface.
1900+
They deliberately have no `initial:` value, so Home Assistant restores their last state on a restart. With `initial:` they are reset on every restart without a state change,
1901+
so the automations below don't fire and the plant can be left in a mode, or at a rate limit, that no longer matches what Predbat sees.
1902+
Without `initial:`, a new **input_number.charge_rate** / **input_number.discharge_rate** starts at 0 W, which Predbat reads as the battery being held, and which the rate automations below write to the plant on a restart.
1903+
On a new install, set each once to your plant's rated charging / discharging power in W.
18971904

18981905
```yaml
18991906
input_select:
@@ -1905,13 +1912,11 @@ input_select:
19051912
- "Freeze Charging"
19061913
- "Discharging"
19071914
- "Freeze Discharging"
1908-
initial: "Demand"
19091915
icon: mdi:battery-unknown
19101916
19111917
input_number:
19121918
charge_rate:
19131919
name: Battery charge rate
1914-
initial: 6950
19151920
min: 0
19161921
max: 20000
19171922
step: 1
@@ -1920,7 +1925,6 @@ input_number:
19201925
19211926
discharge_rate:
19221927
name: Battery discharge rate
1923-
initial: 8000
19241928
min: 0
19251929
max: 20000
19261930
step: 1
@@ -1939,8 +1943,26 @@ Add the following automations to `automations.yaml` (or configure via the UI):
19391943
- trigger: state
19401944
entity_id:
19411945
- input_select.predbat_requested_mode
1946+
# Re-apply the mode after a Home Assistant restart, and when Remote EMS is switched back on (by you,
1947+
# or after another service turned it off), as the plant may be in a different mode by then.
1948+
# Without the restart trigger, a restart at the end of a force export can leave the plant on
1949+
# "Command Discharging" if Predbat already wants "Demand": Predbat sees nothing to change, writes
1950+
# nothing, and the battery keeps exporting past its target.
1951+
- trigger: homeassistant
1952+
event: start
1953+
- trigger: state
1954+
entity_id:
1955+
- switch.sigen_plant_remote_ems_controlled_by_home_assistant
1956+
from: "off"
1957+
to: "on"
19421958
conditions: []
19431959
actions:
1960+
# After a restart, give the Sigenergy integration time to load before writing to it
1961+
- wait_template: >-
1962+
{{ states('select.sigen_plant_remote_ems_control_mode') not in ['unknown', 'unavailable']
1963+
and states('sensor.sigen_plant_battery_state_of_charge') | is_number }}
1964+
timeout: "00:05:00"
1965+
continue_on_timeout: true
19441966
- action: select.select_option
19451967
metadata: {}
19461968
target:
@@ -1967,7 +1989,7 @@ Add the following automations to `automations.yaml` (or configure via the UI):
19671989
# discharge_cut_off_state_of_charge is pinned once here, to current SoC minus a small
19681990
# margin, not a hardcoded value and not continuously re-pinned. Sigenergy has confirmed a
19691991
# firmware bug: if this is set above current SoC, the inverter actively imports from grid
1970-
# to reach it - even with grid_import_limitation at 0 below - so the target must never sit
1992+
# to reach it - even if grid_import_limitation is set to 0 - so the target must never sit
19711993
# above SoC. Setting it once, fixed, is what actually implements "frozen": any real deficit
19721994
# against that fixed point (house load, or even the inverter's own standby losses) gets
19731995
# corrected by grid import back up to the target, rather than the target chasing SoC
@@ -1989,12 +2011,11 @@ Add the following automations to `automations.yaml` (or configure via the UI):
19892011
target:
19902012
entity_id: number.sigen_plant_ess_discharge_cut_off_state_of_charge
19912013
data_template:
1992-
value: "{{ [(states('sensor.sigen_plant_battery_state_of_charge') | float(100)) - 0.25, 0] | max }}"
1993-
- action: number.set_value
1994-
target:
1995-
entity_id: number.sigen_plant_grid_import_limitation
1996-
data:
1997-
value: 0
2014+
# If SoC can't be read this falls back to 0 (no discharge block) - never to a high
2015+
# value, which would trigger the forced-import bug described above
2016+
value: "{{ [(states('sensor.sigen_plant_battery_state_of_charge') | float(0)) - 0.25, 0] | max }}"
2017+
# grid_import_limitation is deliberately not set to 0 in either freeze mode - see the note
2018+
# after these automations.
19982019
# Freeze Discharging
19992020
# Docs:
20002021
# Freeze exporting (mapped to Freeze Discharging in sigenergy_sigenstor.yaml) - The battery is in demand mode,
@@ -2025,12 +2046,7 @@ Add the following automations to `automations.yaml` (or configure via the UI):
20252046
entity_id: number.sigen_plant_ess_discharge_cut_off_state_of_charge
20262047
data:
20272048
value: 0
2028-
- action: number.set_value
2029-
target:
2030-
entity_id: number.sigen_plant_grid_import_limitation
2031-
data:
2032-
value: 0
2033-
# If neither of the above conditions are met, set the limits to the input numbers
2049+
# Any other mode: clear the freeze cut-offs and restore the import limit
20342050
- conditions:
20352051
- condition: not
20362052
conditions:
@@ -2063,49 +2079,125 @@ Add the following automations to `automations.yaml` (or configure via the UI):
20632079
triggers:
20642080
- trigger: state
20652081
entity_id: input_number.charge_rate
2082+
# Re-apply the limit after a Home Assistant restart, and when Remote EMS is switched back on, as for
2083+
# the mode automation above. Otherwise a write lost to a restart, or a limit another service changed
2084+
# while Remote EMS was off, is never corrected, because Predbat sees the input number already at
2085+
# the value it wants
2086+
- trigger: homeassistant
2087+
event: start
2088+
- trigger: state
2089+
entity_id: switch.sigen_plant_remote_ems_controlled_by_home_assistant
2090+
from: "off"
2091+
to: "on"
2092+
conditions:
2093+
# Skip quietly, rather than fail with a template error, while the input number is briefly
2094+
# unavailable. A real 0 W rate (Predbat holding the battery) is re-applied like any other
2095+
- condition: template
2096+
value_template: "{{ states('input_number.charge_rate') | is_number }}"
20662097
actions:
2098+
# After a restart, give the Sigenergy integration time to load; give up rather than write to an unavailable entity
2099+
- wait_template: "{{ states('number.sigen_plant_ess_max_charging_limit') not in ['unknown', 'unavailable'] }}"
2100+
timeout: "00:05:00"
2101+
continue_on_timeout: false
20672102
- action: number.set_value
20682103
target:
20692104
entity_id: number.sigen_plant_ess_max_charging_limit
20702105
data:
2106+
# The plant-level rated power caps the request; if that sensor is briefly unavailable the
2107+
# 99 fallback means no cap, rather than a template error and no write at all
20712108
value: '{{ [(states(''input_number.charge_rate'') | float / 1000) | round(2),
2072-
states(''sensor.sigen_inverter_ess_rated_charging_power'') | float] | min}}'
2073-
mode: single
2109+
states(''sensor.sigen_plant_ess_rated_charging_power'') | float(99)] | min}}'
2110+
# restart, so a rate change that arrives during the wait replaces the one waiting
2111+
mode: restart
20742112
20752113
- id: automation_sigen_ess_max_discharging_limit_input_number_action
20762114
alias: Predbat max discharging limit action
20772115
description: Mapper from input_number.discharge_rate to number.sigen_plant_ess_max_discharging_limit
20782116
triggers:
20792117
- trigger: state
20802118
entity_id: input_number.discharge_rate
2119+
- trigger: homeassistant
2120+
event: start
2121+
- trigger: state
2122+
entity_id: switch.sigen_plant_remote_ems_controlled_by_home_assistant
2123+
from: "off"
2124+
to: "on"
2125+
conditions:
2126+
- condition: template
2127+
value_template: "{{ states('input_number.discharge_rate') | is_number }}"
20812128
actions:
2129+
- wait_template: "{{ states('number.sigen_plant_ess_max_discharging_limit') not in ['unknown', 'unavailable'] }}"
2130+
timeout: "00:05:00"
2131+
continue_on_timeout: false
20822132
- action: number.set_value
20832133
target:
20842134
entity_id: number.sigen_plant_ess_max_discharging_limit
20852135
data:
20862136
value: '{{ [(states(''input_number.discharge_rate'') | float / 1000) | round(2),
2087-
states(''sensor.sigen_inverter_ess_rated_discharging_power'') | float] | min}}'
2088-
mode: single
2137+
states(''sensor.sigen_plant_ess_rated_discharging_power'') | float(99)] | min}}'
2138+
mode: restart
20892139
```
20902140

2091-
*Note:* Some Sigenergy Predbat users have reported that their Sigenergy modbus integration has created some of the entities that Predbat requires with different names
2092-
so you may need to adapt the above automations and `apps.yaml` (or rename your entities) to match:
2141+
*Note:* The rate automations cap the requested rate at the **plant**-level rated power, **sensor.sigen_plant_ess_rated_charging_power** and **sensor.sigen_plant_ess_rated_discharging_power**,
2142+
which match the plant-level limits being written (the template's `battery_rate_max` uses the same plant-level discharging sensor). Per-inverter versions, where your integration creates them, are named without the "-ing"
2143+
(e.g. sensor.sigen_inverter_ess_rated_charge_power); earlier versions of this page mixed the two spellings, giving entity names that don't exist.
2144+
Make sure both are enabled: if one is missing or disabled the automation runs without a cap. If you don't have sensor.sigen_plant_daily_load_consumption,
2145+
look for sensor.sigen_plant_daily_consumed_energy (the name earlier versions of this page used) and use that in `apps.yaml` instead.
20932146

2094-
- sensor.sigen_inverter_ess_rated_discharging_power is instead named sensor.sigen_inverter_ess_rated_discharge_power
2095-
- sensor.sigen_inverter_ess_rated_charging_power is sensor.sigen_inverter_ess_rated_charge_power
2096-
- sensor.sigen_plant_daily_consumed_energy is sensor.sigen_plant_daily_load_consumption
2147+
*Note:* If you rename **input_number.charge_rate** / **input_number.discharge_rate**, change them in `apps.yaml` **and** in the triggers and templates of the rate automations.
2148+
If the two disagree the automations never fire, and nothing reports it: the plant simply stays at whatever limit it last had, so Predbat's rate changes (including setting a rate of 0 to hold the battery) never reach the inverter.
20972149

2098-
*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,
2150+
*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 automation to any lower import limit that your electricity supplier may have imposed,
20992151
e.g. 18kW roughly corresponds to an 80A supply.
21002152

2101-
*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.
2153+
*Important:* Earlier versions of this page set **grid_import_limitation** to 0 in both freeze modes. That is no longer recommended (see [batpred#4911](https://github.com/springfall2008/batpred/issues/4911)): the freeze is
2154+
enforced by the charge and discharge cut-offs, not by the import limit, and a 0kW import limit contradicts Freeze Charging's own definition (house load beyond solar is met from the grid).
2155+
It also stalls a Sigenergy EV charger (EVAC/EVDC) behind the plant's CT clamp, which the plant throttles to the import limit - and one of the times Predbat uses Freeze Charging is to hold the battery while the car charges.
2156+
If you are updating from the older automation, set **number.sigen_plant_grid_import_limitation** back to your supply limit once, as a freeze mode may have left it at 0.
2157+
2158+
*Important:* Sigenergy have confirmed this is a known firmware bug on their side (not a Predbat or integration issue): even if **grid_import_limitation** is 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.
21022159

2103-
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.
2160+
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. (The pin is also re-taken if the automation re-runs during Freeze Charging - after a Home Assistant restart, or when Remote EMS is switched back on - which is a rare, one-off reset rather than continuous tracking.) 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.
21042161

21052162
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.
21062163

21072164
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.
21082165

2166+
*Optional:* other services that control the plant can turn **switch.sigen_plant_remote_ems_controlled_by_home_assistant** off and not turn it back on. On one user's system this happened after an Axle VPP event,
2167+
and again after connecting a cloud service to the Sigenergy account, with nothing in Home Assistant recording why. While it is off the plant ignores Predbat's mode changes (on that occasion for 7 hours,
2168+
running the battery empty overnight). This watchdog notifies you and turns it back on, and the mode and rate automations above then re-apply Predbat's current mode and limits.
2169+
Note that it will also undo a deliberate switch-off - disable the watchdog first if you want Remote EMS off for a while.
2170+
2171+
```yaml
2172+
- id: predbat_sigenergy_remote_ems_watchdog
2173+
alias: "Predbat Sigenergy Remote EMS Watchdog"
2174+
description: "Turn Remote EMS back on if something else has turned it off"
2175+
mode: single
2176+
triggers:
2177+
- trigger: time_pattern
2178+
minutes: /30
2179+
conditions:
2180+
- condition: state
2181+
entity_id: switch.sigen_plant_remote_ems_controlled_by_home_assistant
2182+
state: "off"
2183+
# If you take part in Axle VPP events through Predbat's Axle integration, uncomment this so the
2184+
# watchdog doesn't take control back from Axle in the middle of an event. The entity exists only
2185+
# when that integration is enabled (change "predbat" if you use a different prefix); with it
2186+
# missing, the condition never passes and the watchdog never runs:
2187+
# - condition: state
2188+
# entity_id: binary_sensor.predbat_axle_event
2189+
# state: "off"
2190+
actions:
2191+
- action: persistent_notification.create
2192+
data:
2193+
notification_id: sigenergy_remote_ems_watchdog
2194+
title: Sigenergy not in Remote EMS mode
2195+
message: "Remote EMS was off at {{ now().strftime('%H:%M') }} - turning it back on."
2196+
- action: switch.turn_on
2197+
target:
2198+
entity_id: switch.sigen_plant_remote_ems_controlled_by_home_assistant
2199+
```
2200+
21092201
## Sigenergy Cloud
21102202

21112203
**Experimental**

0 commit comments

Comments
 (0)