153153EVC_BLACKLIST_COMMANDS = ["installation-mode" , "perform-factory-reset" , "rename-id-tag" , "delete-id-tags" , "change-randomised-delay-duration" ]
154154
155155TIMEOUT = 240
156- RETRIES = 5
156+ RETRIES = 10
157+ RETRY_FACTOR = 1
157158MAX_THREADS = 2
159+ MAX_START_TIME = 10 * 60
158160
159161attribute_table = {
160162 "time" : {"friendly_name" : "Time" , "icon" : "mdi:clock" , "unit_of_measurement" : "Time" , "state_class" : "timestamp" },
@@ -224,7 +226,7 @@ def wait_api_started(self):
224226 """
225227 self .log ("GECloud: Waiting for API to start" )
226228 count = 0
227- while not self .api_started and count < 240 :
229+ while not self .api_started and count < MAX_START_TIME :
228230 time .sleep (1 )
229231 count += 1
230232 if not self .api_started :
@@ -372,6 +374,7 @@ async def publish_info(self, device, device_info):
372374 attributes = {}
373375 if key == "info" :
374376 info = device_info [key ]
377+ last_updated = device_info .get ("last_updated" , None )
375378 cap = info .get ("battery" , {}).get ("nominal_capacity" , None )
376379 volt = info .get ("battery" , {}).get ("nominal_voltage" , None )
377380 dod = info .get ("battery" , {}).get ("depth_of_discharge" , None )
@@ -389,6 +392,7 @@ async def publish_info(self, device, device_info):
389392 self .base .dashboard_item (entity_name + "_battery_size" , capacity , attributes = attribute_table .get ("battery_size" , {}), app = "gecloud" )
390393 self .base .dashboard_item (entity_name + "_max_charge_rate" , max_charge_rate , attributes = attribute_table .get ("max_charge_rate" , {}), app = "gecloud" )
391394 self .base .dashboard_item (entity_name + "_battery_dod" , dod , attributes = attribute_table .get ("_battery_dod" , {}), app = "gecloud" )
395+ self .base .dashboard_item (entity_name + "_last_updated" , last_updated , attributes = attribute_table .get ("time" , {}), app = "gecloud" )
392396
393397 async def publish_evc_data (self , serial , evc_data ):
394398 """
@@ -642,6 +646,7 @@ async def async_automatic_config(self, devices):
642646 return
643647
644648 batteries = devices ["battery" ]
649+ batteries_real = devices ["battery" ]
645650 num_inverters = len (batteries )
646651
647652 if devices ["gateway" ]:
@@ -670,21 +675,34 @@ async def async_automatic_config(self, devices):
670675 self .base .args ["charge_limit" ] = ["number.predbat_gecloud_" + device + "_ac_charge_upper_percent_limit" for device in batteries ]
671676 self .base .args ["discharge_start_time" ] = ["select.predbat_gecloud_" + device + "_dc_discharge_1_start_time" for device in batteries ]
672677 self .base .args ["discharge_end_time" ] = ["select.predbat_gecloud_" + device + "_dc_discharge_1_end_time" for device in batteries ]
673- self .base .args ["discharge_target_soc" ] = ["select .predbat_gecloud_" + device + "_dc_discharge_1_lower_soc_percent_limit" for device in batteries ]
678+ self .base .args ["discharge_target_soc" ] = ["number .predbat_gecloud_" + device + "_dc_discharge_1_lower_soc_percent_limit" for device in batteries ]
674679 self .base .args ["scheduled_charge_enable" ] = ["switch.predbat_gecloud_" + device + "_ac_charge_enable" for device in batteries ]
675680 self .base .args ["scheduled_discharge_enable" ] = ["switch.predbat_gecloud_" + device + "_enable_dc_discharge" for device in batteries ]
676681 self .base .args ["pause_mode" ] = ["select.predbat_gecloud_" + device + "_pause_battery" for device in batteries ]
677682 self .base .args ["pause_start_time" ] = ["select.predbat_gecloud_" + device + "_pause_battery_start_time" for device in batteries ]
678683 self .base .args ["pause_end_time" ] = ["select.predbat_gecloud_" + device + "_pause_battery_end_time" for device in batteries ]
684+
685+ # Do we have a charge power percentage setting?
686+ for device in batteries :
687+ registers = self .settings .get (device , {})
688+ for key in registers :
689+ reg_name = registers [key ].get ("name" , None )
690+ value = registers [key ].get ("value" , None )
691+ if "inverter_charge_power_percentage" in reg_name .lower ():
692+ self .log ("GECloud: Found charge power percentage setting for {}, using this" .format (device ))
693+ self .base .args ["charge_rate_percent" ] = ["number.predbat_gecloud_" + device + "_inverter_charge_power_percentage" for device in batteries ]
694+ self .base .args ["discharge_rate_percent" ] = ["number.predbat_gecloud_" + device + "_inverter_discharge_power_percentage" for device in batteries ]
695+ break
696+
679697 if "givtcp_rest" in self .base .args :
680698 del self .base .args ["givtcp_rest" ]
681-
699+
682700 # Use the first battery serial for the ge_cloud_serial (for status)
683701 self .base .args ["ge_cloud_serial" ] = devices ["battery" ][0 ]
684702
685703 # reconfigure for EMS
686704 if devices ["ems" ]:
687- self .log ("GECloud: EMS detected" )
705+ self .log ("GECloud: EMS detected, using this for control " )
688706 ems = devices ["ems" ]
689707 self .base .args ["inverter_type" ] = ["GEE" for _ in range (num_inverters )]
690708 self .base .args ["ge_cloud_serial" ] = ems
@@ -747,9 +765,6 @@ async def start(self):
747765 for device in device_list :
748766 self .pending_writes [device ] = []
749767
750- if self .automatic :
751- await self .async_automatic_config (devices_dict )
752-
753768 seconds = 0
754769 while not self .stop_cloud and not self .base .fatal_error :
755770 try :
@@ -774,8 +789,10 @@ async def start(self):
774789 self .settings [device ] = await self .async_get_inverter_settings (device , first = False , previous = self .settings .get (device , {}))
775790 await self .publish_registers (device , self .settings [device ])
776791
777- # Real time control disable?
792+ # One shot tasks
778793 if seconds == 0 :
794+ if self .automatic :
795+ await self .async_automatic_config (devices_dict )
779796 for device in device_list :
780797 await self .enable_real_time_control (device , self .settings [device ])
781798
@@ -813,7 +830,7 @@ async def async_send_evc_command(self, uuid, command, params):
813830 data = None
814831 if data :
815832 break
816- await asyncio .sleep (1 * (retry + 1 ))
833+ await asyncio .sleep (RETRY_FACTOR * (retry + 1 ))
817834 if data is None :
818835 self .log ("Error: GECloud: Failed to send EVC command {} params {}" .format (command , params ))
819836 return data
@@ -835,7 +852,7 @@ async def async_read_inverter_setting(self, serial, setting_id):
835852 if serial in self .pending_writes :
836853 for pending in self .pending_writes [serial ]:
837854 if pending ["setting_id" ] == setting_id :
838- return {"value" : pending ["value" ]}
855+ return {"value" : pending ["value" ], "context" : "predbat" }
839856
840857 for retry in range (RETRIES ):
841858 data = await self .async_get_inverter_data (GE_API_INVERTER_READ_SETTING , serial , setting_id , post = True )
@@ -847,12 +864,12 @@ async def async_read_inverter_setting(self, serial, setting_id):
847864 elif data and data_value in [- 1 , - 2 , - 5 , - 6 ]:
848865 data = None
849866 # Inverter timeout, try to spread requests out
850- await asyncio .sleep (random .random () * 2 )
867+ await asyncio .sleep (random .random () * ( 3 + retry ) )
851868 if data :
852869 break
853- await asyncio .sleep (1 * (retry + 1 ))
870+ await asyncio .sleep (RETRY_FACTOR * (retry + 1 ))
854871 if data is None :
855- self .log ("Warn: GECloud: Device {} Failed to read inverter setting id {}" .format (serial , setting_id ))
872+ self .log ("Warn: GECloud: Device {} Failed to read inverter setting id {} got {} " .format (serial , setting_id , data ))
856873 return data
857874
858875 async def async_write_inverter_setting (self , serial , setting_id , value ):
@@ -865,15 +882,15 @@ async def async_write_inverter_setting(self, serial, setting_id, value):
865882 serial ,
866883 setting_id ,
867884 post = True ,
868- datain = {"value" : str (value ), "context" : "homeassistant " },
885+ datain = {"value" : str (value ), "context" : "predbat " },
869886 )
870887 if data and "success" in data :
871888 if not data ["success" ]:
872889 data = None
873890 if data :
874891 self .pending_writes [serial ].append ({"setting_id" : setting_id , "value" : value })
875892 break
876- await asyncio .sleep (1 * (retry + 1 ))
893+ await asyncio .sleep (RETRY_FACTOR * (retry + 1 ))
877894 if data is None :
878895 self .log ("Warn: GECloud: Failed to write setting id {} value {}" .format (setting_id , value ))
879896 return data
@@ -1169,6 +1186,7 @@ async def async_get_devices(self):
11691186 self .log ("GECloud: Found device {}" .format (device ))
11701187 inverter = device .get ("inverter" , {})
11711188 serial = inverter .get ("serial" , None )
1189+ last_updated = inverter .get ("last_updated" , None )
11721190 model = inverter .get ("info" , {}).get ("model" , "" ).lower ()
11731191 batteries = inverter .get ("connections" , {}).get ("batteries" , [])
11741192 if serial :
@@ -1202,7 +1220,7 @@ async def async_get_inverter_data_retry(self, endpoint, serial="", setting_id=""
12021220 data = await self .async_get_inverter_data (endpoint , serial , setting_id , post , datain , uuid , meter_ids , start_time = start_time , end_time = end_time , command = command , measurands = measurands )
12031221 if data is not None :
12041222 break
1205- await asyncio .sleep (1 * (retry + 1 ))
1223+ await asyncio .sleep (RETRY_FACTOR * (retry + 1 ))
12061224 if data is None :
12071225 self .log ("Warn: GECloud: Failed to get data from {}" .format (endpoint ))
12081226 return data
@@ -1237,7 +1255,7 @@ async def async_get_inverter_data(self, endpoint, serial="", setting_id="", post
12371255 self .log (f"Warn: GECloud: Exception during request to { url } : { e } " )
12381256 self .failures_total += 1
12391257 return None
1240-
1258+
12411259 try :
12421260 data = response .json ()
12431261 except requests .exceptions .JSONDecodeError :
@@ -1397,7 +1415,6 @@ def download_ge_data(self, now_utc):
13971415 self .export_today_now = self .export_today .get (0 , 0 ) - self .export_today .get (self .minutes_now , 0 )
13981416 self .pv_today_now = self .pv_today .get (0 , 0 ) - self .pv_today .get (self .minutes_now , 0 )
13991417
1400- self .log ("GE GECloud load_last_period is {} kW" .format (dp2 (self .load_last_period )))
14011418 # More up to date sensors for current values if set
14021419 if "load_today" in self .args :
14031420 load_minutes , load_minutes_age = self .minute_data_load (self .now_utc , "load_today" , self .max_days_previous , required_unit = "kWh" , load_scaling = self .load_scaling )
0 commit comments