@@ -240,6 +240,7 @@ class GatewayMQTT(ComponentBase):
240240 _inverter_slot_serials = ()
241241 _slots_without_capacity = frozenset ()
242242 _last_ratings = {} # replaced, never changed in place
243+ _assumed_ratings = frozenset ()
243244 gateway_device_id = None
244245
245246 def initialize (self , gateway_device_id = None , mqtt_host = None , mqtt_port = 8883 , mqtt_token = None , gateway_inverter_serial = None , gateway_evc_automatic = False , gateway_evc_control = False , gateway_shared_ct = True , gateway_integrate_power = False , ** kwargs ):
@@ -347,6 +348,7 @@ def initialize(self, gateway_device_id=None, mqtt_host=None, mqtt_port=8883, mqt
347348 self ._pv_only_serials = () # listed inverters with no battery: counted for PV, never written to
348349 self ._slots_without_capacity = frozenset () # slots bound with no battery size sensor, re-bound once the hub reports a size
349350 self ._last_ratings = {} # (entity suffix, rating name) -> the last real value the hub reported
351+ self ._assumed_ratings = frozenset () # entity suffixes of inverters whose maximum rate has never been reported
350352 self ._discovery = {} # upper-case serial -> what the hub last reported it to be, for gateway_inverter_serial
351353 self ._discovery_dirty = False
352354 self ._discovery_save_retry_at = 0
@@ -1253,7 +1255,13 @@ def _inject_inverter_entities(self, inv, suffix, site_energy=None):
12531255 # ratings unpopulated (seen for a few minutes after a hub restart) otherwise turned a 3kW
12541256 # inverter into a 6kW one with no export limit, and those rates were written to it (#5478).
12551257 record = self ._discovery .get (inv .serial .upper (), {})
1256- battery_rate_max = self ._last_rating (suffix , "battery_rate_max" , bat .rate_max_w ) or record .get ("rate_max_w" ) or 6000
1258+ battery_rate_max = self ._last_rating (suffix , "battery_rate_max" , bat .rate_max_w ) or record .get ("rate_max_w" )
1259+ # With no rating ever reported the plan still needs a figure, but it is only an assumption:
1260+ # rate writes for the inverter are held until the real one arrives (see _rate_write_held)
1261+ assumed = not battery_rate_max and inv .type not in GATEWAY_COORDINATOR_TYPES
1262+ if assumed != (suffix in self ._assumed_ratings ):
1263+ self ._assumed_ratings = self ._assumed_ratings | {suffix } if assumed else self ._assumed_ratings - {suffix }
1264+ battery_rate_max = battery_rate_max or 6000
12571265 self .dashboard_item (f"sensor.{ pfx } _battery_rate_max" , battery_rate_max , attributes = GATEWAY_ATTRIBUTE_TABLE .get ("battery_rate_max" , {}), app = "gateway" )
12581266
12591267 self .dashboard_item (f"sensor.{ pfx } _pv_power" , inv .pv .power_w , attributes = GATEWAY_ATTRIBUTE_TABLE .get ("pv_power" , {}), app = "gateway" )
@@ -2412,6 +2420,9 @@ async def _check_rate_caps(self):
24122420 for serial , caps in self ._inverter_rate_caps ().items ():
24132421 if self ._rate_caps_sent .get (serial ) == caps :
24142422 continue
2423+ if _serial_suffix (serial ) in self ._assumed_ratings :
2424+ # The cap comes from a rating the inverter has not reported yet (see _rate_write_held)
2425+ continue
24152426 charge_cap_w , discharge_cap_w = caps
24162427 await self .publish_command ("set_rate_cap" , serial = serial , charge_cap_w = charge_cap_w , discharge_cap_w = discharge_cap_w )
24172428 self ._rate_caps_sent [serial ] = caps
@@ -2643,6 +2654,8 @@ async def number_event(self, entity_id, value):
26432654 if serial is None :
26442655 self .log (f"Warn: GatewayMQTT: number_event: cannot resolve serial for entity '{ entity_id } ' — command not sent" )
26452656 return
2657+ if ("_discharge_rate" in entity_id or "_charge_rate" in entity_id ) and self ._rate_write_held (serial , val ):
2658+ return
26462659 if "_discharge_rate" in entity_id :
26472660 await self ._send_control (entity_id , "set_discharge_rate" , power_w = val , serial = serial )
26482661 elif "_charge_rate" in entity_id :
@@ -2652,6 +2665,27 @@ async def number_event(self, entity_id, value):
26522665 elif "_target_soc" in entity_id :
26532666 await self ._send_control (entity_id , "set_target_soc" , target_soc = val , serial = serial )
26542667
2668+ def _rate_write_held (self , serial , power_w ):
2669+ """Whether a charge or discharge rate write must wait for the inverter's rating.
2670+
2671+ Until an inverter has reported its maximum rate, the rate Predbat works to is the 6000W
2672+ default, and writing that to a 3kW inverter is the fault in #5478. Such a write is not
2673+ sent. Predbat reads the unchanged rate back as a write that did not complete and tries
2674+ again, so it goes through once the rating is known. A rate of zero is safe on any
2675+ inverter and is always sent, so a charge or discharge can still be stopped.
2676+
2677+ Args:
2678+ serial: The inverter the write is for.
2679+ power_w: The rate being written.
2680+
2681+ Returns:
2682+ bool: True if the write is held.
2683+ """
2684+ if power_w <= 0 or _serial_suffix (serial ) not in self ._assumed_ratings :
2685+ return False
2686+ self .log (f"Warn: GatewayMQTT: inverter { serial } has not reported its maximum rate — rate write of { power_w } W held until it does" )
2687+ return True
2688+
26552689 async def switch_event (self , entity_id , service ):
26562690 """Handle switch entity service calls (charge/discharge enable).
26572691
0 commit comments