|
44 | 44 | SUNSYNK_CAPACITY_AH_FIELD, |
45 | 45 | SUNSYNK_CHARGE_VOLT_FIELD, |
46 | 46 | SUNSYNK_CHARGE_CURRENT_FIELDS, |
47 | | - SUNSYNK_POWER_LIMIT_FIELD, |
| 47 | + SUNSYNK_EXPORT_LIMIT_FIELD, |
48 | 48 | SUNSYNK_RATED_POWER_FIELD, |
49 | 49 | SUNSYNK_BATTERY_LOW_CAP_FIELD, |
50 | 50 | LIFEPO4_CELL_COUNTS, |
@@ -583,15 +583,26 @@ def battery_rate_max(self, sn): |
583 | 583 | return amps * volts |
584 | 584 |
|
585 | 585 | def inverter_limit(self, sn): |
586 | | - """Return the inverter's usable AC limit in watts, or 0 when unknown. |
| 586 | + """Return the inverter's AC power rating in watts, or 0 when unknown. |
587 | 587 |
|
588 | | - The hardware rating (ratePower) is not the whole story: an installer-set power |
589 | | - limiter (pvMaxLimit) can cap the inverter below it, and that cap is what actually |
590 | | - binds. A real system was seen with ratePower 8000 and pvMaxLimit 7000, so taking the |
591 | | - rating alone would have Predbat plan a kilowatt the inverter will never deliver. |
592 | | - Whichever is lower wins; if only one is known, that one is used. |
| 588 | + This is the hardware rating (ratePower) only. It is deliberately NOT reduced by |
| 589 | + pvMaxLimit: despite that setting's "Inverter Power Limiter" label in the app, Sunsynk |
| 590 | + documents it as an EXPORT cap, so the inverter can still deliver its full rating to |
| 591 | + the house. See export_limit. |
593 | 592 | """ |
594 | | - limits = [value for value in (self.device_rated_power.get(sn, 0.0), self._as_float(self.device_settings.get(sn, {}).get(SUNSYNK_POWER_LIMIT_FIELD))) if value > 0] |
| 593 | + return self.device_rated_power.get(sn, 0.0) |
| 594 | + |
| 595 | + def export_limit(self, sn): |
| 596 | + """Return the maximum export power in watts, or 0 when unknown. |
| 597 | +
|
| 598 | + Predbat's inverter.py defaults export_limit to 99999W - effectively unlimited - so |
| 599 | + leaving this unmapped lets it plan an export the inverter will simply clip. A real |
| 600 | + system had a 7000W export cap behind an 8000W inverter. |
| 601 | +
|
| 602 | + Bounded by the inverter rating too: whatever the setting nominally allows, the |
| 603 | + inverter cannot export more AC than it can produce. |
| 604 | + """ |
| 605 | + limits = [value for value in (self.inverter_limit(sn), self._as_float(self.device_settings.get(sn, {}).get(SUNSYNK_EXPORT_LIMIT_FIELD))) if value > 0] |
595 | 606 | return min(limits) if limits else 0.0 |
596 | 607 |
|
597 | 608 | def battery_reserve_min(self, sn): |
@@ -974,6 +985,9 @@ async def publish_data(self): |
974 | 985 | rated_power = self.inverter_limit(sn) |
975 | 986 | if rated_power > 0: |
976 | 987 | self.dashboard_item(self._sensor_name(sn, "inverter_limit"), state=rated_power, attributes={"unit_of_measurement": "W", "friendly_name": f"Sunsynk {sn} Inverter Limit"}, app="sunsynk") |
| 988 | + export_cap = self.export_limit(sn) |
| 989 | + if export_cap > 0: |
| 990 | + self.dashboard_item(self._sensor_name(sn, "export_limit"), state=export_cap, attributes={"unit_of_measurement": "W", "friendly_name": f"Sunsynk {sn} Export Limit"}, app="sunsynk") |
977 | 991 | floor = self.battery_reserve_min(sn) |
978 | 992 | if floor > 0: |
979 | 993 | self.dashboard_item(self._sensor_name(sn, "battery_reserve_min"), state=floor, attributes={"unit_of_measurement": "%", "friendly_name": f"Sunsynk {sn} Battery Reserve Min"}, app="sunsynk") |
@@ -1343,20 +1357,15 @@ async def automatic_config(self): |
1343 | 1357 | self.set_arg_auto("inverter_limit", [self._sensor_name(sn, "inverter_limit") for sn in devices]) |
1344 | 1358 | else: |
1345 | 1359 | self.log("Warn: Sunsynk no ratePower reported, inverter_limit must be set manually in apps.yaml") |
1346 | | - # export_limit is deliberately NOT auto-mapped. The Sunsynk app has an "Export power |
1347 | | - # limiter" in Grid Settings which can legitimately sit BELOW the inverter limit - |
1348 | | - # a G98/G99 site is commonly capped at 3.68kW behind a much larger inverter - but no |
1349 | | - # settings field could be identified that carries it: on the one system inspected the |
1350 | | - # app showed 7000 for both the inverter and export limiters while pvMaxLimit was the |
1351 | | - # only field holding 7000, so the two could not be told apart. |
1352 | | - # |
1353 | | - # Guessing it from pvMaxLimit would be worse than leaving it alone, because |
1354 | | - # set_arg_auto always beats apps.yaml: a user who correctly set export_limit to their |
1355 | | - # real 3.68kW cap would have it silently replaced by the inverter limit. Left unset, |
1356 | | - # Predbat keeps its own default and the user's apps.yaml value is honoured. |
1357 | | - self.log( |
1358 | | - "Info: Sunsynk cannot identify the inverter's export power limiter from the settings object, so export_limit is left for apps.yaml. If your Export power limiter (app: Grid Settings) is lower than the inverter limit - a G98/G99 export cap, for instance - set export_limit manually." |
1359 | | - ) |
| 1360 | + # Without this Predbat falls back to inverter.py's effectively unlimited 99999W |
| 1361 | + # default and plans exports the inverter simply clips. pvMaxLimit is the export cap |
| 1362 | + # despite its "Inverter Power Limiter" label in the app - a G98/G99 site capped at |
| 1363 | + # 3.68kW behind a much larger inverter is exactly the case this protects. |
| 1364 | + # See SUNSYNK_EXPORT_LIMIT_FIELD. |
| 1365 | + if all(self.export_limit(sn) > 0 for sn in self.device_list): |
| 1366 | + self.set_arg_auto("export_limit", [self._sensor_name(sn, "export_limit") for sn in devices]) |
| 1367 | + else: |
| 1368 | + self.log("Warn: Sunsynk no export power limit available, export_limit must be set manually in apps.yaml") |
1360 | 1369 | if all(self.battery_reserve_min(sn) > 0 for sn in self.device_list): |
1361 | 1370 | self.set_arg_auto("battery_min_soc", [self._sensor_name(sn, "battery_reserve_min") for sn in devices]) |
1362 | 1371 |
|
|
0 commit comments