Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
22 changes: 22 additions & 0 deletions apps/predbat/sigenergy.py
Original file line number Diff line number Diff line change
Expand Up @@ -1768,6 +1768,15 @@ def _get_inverter_max_power_kw(self, system_id):
power += _safe_float(attr.get("ratedActivePower", 0))
return power

def _get_export_limit_w(self, system_id):
Comment thread
springfall2008 marked this conversation as resolved.
"""Return the grid export limit in W, or 99999 (no limit) when the system has not reported one.

The limit is the grid max backfeed power, which only arrives in an ``openapi/change`` MQTT
message (see _handle_mqtt_change). A reported 0 is skipped there, so it also reads as no limit.
Comment thread
springfall2008 marked this conversation as resolved.
Outdated
"""
backfeed_kw = _safe_float(self.systems.get(system_id, {}).get("gridMaxBackfeedPower", 0))
return round(backfeed_kw * 1000) if backfeed_kw > 0 else 99999

# -----------------------------------------------------------------------
# MQTT message handlers
# -----------------------------------------------------------------------
Expand Down Expand Up @@ -2288,6 +2297,18 @@ async def publish_system_entities(self, system_id):
app="sigenergy",
)

# --- Export limit (W) ---
self.dashboard_item(
"sensor.{}_sigenergy_{}_export_limit".format(self.prefix, slug),
state=self._get_export_limit_w(system_id),
attributes={
"friendly_name": "Sigenergy {} Export Limit".format(system_name),
"unit_of_measurement": "W",
"device_class": "power",
},
app="sigenergy",
)

# --- Operational mode (string) ---
# flow_status (from MQTT) is preferred as it's the freshest, but fall back to the
# REST-sourced current_mode (fetch_current_mode) so this doesn't read "Unknown" before
Expand Down Expand Up @@ -2380,6 +2401,7 @@ async def automatic_config(self):
self.set_arg("battery_power", ["sensor.{}_sigenergy_{}_battery_power".format(self.prefix, s) for s in slugs])
self.set_arg("battery_rate_max", ["sensor.{}_sigenergy_{}_battery_rate_max".format(self.prefix, s) for s in slugs])
self.set_arg("inverter_limit", ["sensor.{}_sigenergy_{}_inverter_limit".format(self.prefix, s) for s in slugs])
self.set_arg("export_limit", ["sensor.{}_sigenergy_{}_export_limit".format(self.prefix, s) for s in slugs])
Comment thread
springfall2008 marked this conversation as resolved.
Outdated
Comment thread
springfall2008 marked this conversation as resolved.
Outdated
self.set_arg("pv_power", ["sensor.{}_sigenergy_{}_pv_power".format(self.prefix, s) for s in slugs])
self.set_arg("grid_power", ["sensor.{}_sigenergy_{}_grid_power".format(self.prefix, s) for s in slugs])
self.set_arg("load_power", ["sensor.{}_sigenergy_{}_load_power".format(self.prefix, s) for s in slugs])
Expand Down
43 changes: 43 additions & 0 deletions apps/predbat/tests/test_sigenergy.py
Original file line number Diff line number Diff line change
Expand Up @@ -504,6 +504,47 @@ def test_sigenergy_automatic_config(my_predbat):
assert api.set_args.get("load_today") == ["sensor.predbat_sigenergy_sig001_load_lifetime", "sensor.predbat_sigenergy_sig002_load_lifetime"], "load_today wired to lifetime totals"
assert api.set_args.get("import_today") == ["sensor.predbat_sigenergy_sig001_grid_import_lifetime", "sensor.predbat_sigenergy_sig002_grid_import_lifetime"], "import_today wired to lifetime totals"
assert api.set_args.get("export_today") == ["sensor.predbat_sigenergy_sig001_grid_export_lifetime", "sensor.predbat_sigenergy_sig002_grid_export_lifetime"], "export_today wired to lifetime totals"
assert api.set_args.get("export_limit") == ["sensor.predbat_sigenergy_sig001_export_limit", "sensor.predbat_sigenergy_sig002_export_limit"], "export_limit wired to the grid backfeed limit sensors"

return failed


def test_sigenergy_publish_export_limit(my_predbat):
"""The grid backfeed limit from an MQTT change message is published as the export limit sensor in W."""
failed = False
api = MockSigenergyAPI()

system_id = "SIG12345"
slug = api._system_slug(system_id)
api.systems[system_id] = {"systemName": "My Site"}
api.devices[system_id] = [{"deviceType": "Inverter", "attrMap": {"ratedActivePower": 12.0}}]

api._handle_mqtt_change(system_id, {"inverterMaxActivePowerW": "12000.0", "gridMaxBackfeedPowerW": "5000.0"})
run_async(api.publish_system_entities(system_id))

key = "sensor.predbat_sigenergy_{}_export_limit".format(slug)
assert key in api.dashboard_items, "Export limit entity published"
assert api.dashboard_items[key]["state"] == 5000, "Backfeed limit 5kW published as 5000W, got {}".format(api.dashboard_items[key]["state"])
assert api.dashboard_items[key]["attributes"].get("unit_of_measurement") == "W", "Export limit is in W so get_arg(required_unit='W') accepts it"

return failed


def test_sigenergy_publish_export_limit_unknown(my_predbat):
"""With no backfeed limit reported yet the export limit sensor reads 99999 W, i.e. no limit beyond the inverter's own."""
failed = False
api = MockSigenergyAPI()

system_id = "SIG12345"
slug = api._system_slug(system_id)
api.systems[system_id] = {"systemName": "My Site"}
api.devices[system_id] = [{"deviceType": "Inverter", "attrMap": {"ratedActivePower": 12.0}}]

run_async(api.publish_system_entities(system_id))

key = "sensor.predbat_sigenergy_{}_export_limit".format(slug)
assert key in api.dashboard_items, "Export limit entity published"
assert api.dashboard_items[key]["state"] == 99999, "Unknown backfeed limit published as 99999W, got {}".format(api.dashboard_items[key]["state"])

return failed

Expand Down Expand Up @@ -4294,6 +4335,8 @@ def run_sigenergy_tests(my_predbat):
("publish_system_entities_operational_mode_fallback", test_sigenergy_publish_system_entities_operational_mode_fallback),
("publish_system_entities_operational_mode_invalid_value", test_sigenergy_publish_system_entities_operational_mode_invalid_value),
("automatic_config", test_sigenergy_automatic_config),
("publish_export_limit", test_sigenergy_publish_export_limit),
("publish_export_limit_unknown", test_sigenergy_publish_export_limit_unknown),
("fetch_controls", test_sigenergy_fetch_controls),
("publish_controls", test_sigenergy_publish_controls),
("parse_entity_system", test_sigenergy_parse_entity_system),
Expand Down
1 change: 1 addition & 0 deletions docs/components.md
Original file line number Diff line number Diff line change
Expand Up @@ -1360,6 +1360,7 @@ Supports real-time monitoring (SOC, power flows, operational mode) and full char
- The Sigenergy Developer Portal application must have **VPP Mode** enabled or charge/discharge commands will be rejected
- On first startup Sigenergy sends an **onboarding approval email** — you must click the approval link before live MQTT data starts flowing
- MQTT certificates (CA, client cert, client key) are required for TLS-authenticated connections to the broker
- With `automatic: true` Predbat models your grid export limit (the plant's grid max backfeed power) from `sensor.predbat_sigenergy_<system>_export_limit`. Sigenergy only reports it when the plant's configuration changes, so until then the sensor reads 99999 W and exports are capped by the inverter limit alone

#### Configuration Options (sigenergy)

Expand Down
Loading