@@ -61,6 +61,8 @@ def load_ota_versions():
6161settings = load_settings ()
6262ota_versions = load_ota_versions ()
6363
64+
65+
6466UNIT_MAPPING = {
6567 "unbalancelinecurrents" : PERCENTAGE ,
6668 "powerfactor" : None ,
@@ -83,7 +85,7 @@ def load_ota_versions():
8385 "rssi" : SensorStateClass .MEASUREMENT ,
8486}
8587
86- EXCLUDE_FIELDS = {"post" , " Post" , "Config485" , "MqttStatus" , "productModel" , " ProductModel" , "Serialnumber " , "SerialNumber " , "devicetype " , "DeviceType " , "FWVersion " }
88+ EXCLUDE_FIELDS = {"Post" , "Config485" , "MqttStatus" , "ProductModel" , "SerialNumber " , "DeviceType " , "FWVersion " , "MCUVersion " , "Manufactor " }
8789
8890async def async_setup_entry (hass , entry , async_add_entities ):
8991 """Set up sensor platform."""
@@ -92,6 +94,12 @@ async def async_setup_entry(hass, entry, async_add_entities):
9294 coordinator = BituoDataUpdateCoordinator (hass , host_ip , current_scan_interval )
9395 await coordinator .async_config_entry_first_refresh ()
9496
97+ # Store the coordinator so it can be accessed by other platforms like button
98+ hass .data .setdefault (DOMAIN , {})
99+ hass .data [DOMAIN ][entry .entry_id ] = {
100+ 'sensor_coordinator' : coordinator
101+ }
102+
95103 # Fetch device model and firmware version
96104 try :
97105 device_info = await coordinator .fetch_device_info ()
@@ -101,11 +109,11 @@ async def async_setup_entry(hass, entry, async_add_entities):
101109
102110 # Create sensor entities for each data field
103111 sensors = [
104- BituoSensor (coordinator , host_ip , field , device_info .get ("model" , "Unknown Model" ), device_info .get ("fw_version" , "Unknown" ))
112+ BituoSensor (coordinator , host_ip , field , device_info .get ("model" , "Unknown Model" ), device_info .get ("fw_version" , "Unknown" ), device_info . get ( "manufacturer" , "Unknown" ), device_info . get ( "mcu_version" , "Unknown" ) )
105113 for field in coordinator .data .keys ()
106114 if field not in EXCLUDE_FIELDS
107115 ]
108- ota_sensor = BituoOTASensor (coordinator , host_ip , device_info .get ("model" , "Unknown Model" ), device_info .get ("fw_version" , "Unknown" ))
116+ ota_sensor = BituoOTASensor (coordinator , host_ip , device_info .get ("model" , "Unknown Model" ), device_info .get ("fw_version" , "Unknown" ), device_info . get ( "manufacturer" , "Unknown" ), device_info . get ( "mcu_version" , "Unknown" ) )
109117 sensors .append (ota_sensor )
110118
111119 # Assign the OTA sensor to the coordinator
@@ -195,8 +203,10 @@ async def fetch_device_info(self):
195203 response .raise_for_status ()
196204 data = response .json ()
197205 return {
198- "model" : data .get ("productModel" ) or data .get ("ProductModel" , "Unknown Model" ),
199- "fw_version" : data .get ("FWVersion" ) or data .get ("fwVersion" , "Unknown" ),
206+ "model" : data .get ("ProductModel" , "Unknown Model" ),
207+ "fw_version" : data .get ("FWVersion" , "Unknown" ),
208+ "manufacturer" : data .get ("Manufactor" , "Unknown" ),
209+ "mcu_version" : data .get ("MCUVersion" , "Unknown" ),
200210 }
201211 except Exception as err :
202212 raise UpdateFailed (f"Error fetching device info: { err } " )
@@ -229,19 +239,20 @@ async def _schedule_ota_update_checks(self):
229239class BituoSensor (CoordinatorEntity , SensorEntity ):
230240 """Representation of a Sensor."""
231241
232- def __init__ (self , coordinator , host_ip , field , model , fw_version ):
242+ def __init__ (self , coordinator , host_ip , field , model , fw_version , manufacturer , mcu_version ):
233243 """Initialize the sensor."""
234244 super ().__init__ (coordinator )
235245 self ._field = field
236246 self ._attr_name = self .format_field_name (field )
237247 self ._attr_unique_id = f"{ host_ip } _{ field } "
248+ self .entity_id = f"sensor.{ host_ip .replace ('.' , '_' )} _{ self .format_field_entity_id (field )} "
238249 self ._attr_device_info = DeviceInfo (
239250 identifiers = {(DOMAIN , host_ip )},
240251 name = f"{ model } - { host_ip } " ,
241- manufacturer = "BituoTechnik" ,
252+ manufacturer = manufacturer ,
242253 model = model ,
243- sw_version = fw_version ,
244- configuration_url = f"http://{ host_ip } " # 新增的配置 URL
254+ sw_version = f"S { fw_version } _M { self . format_version ( mcu_version ) } " ,
255+ configuration_url = f"http://{ host_ip } " # embed URL
245256 )
246257 self ._host_ip = host_ip
247258 self ._native_unit_of_measurement = self .get_initial_unit_of_measurement ()
@@ -299,6 +310,31 @@ def format_field_name(field):
299310 formatted_name = '' .join ([' ' + char if char .isupper () else char for char in field ]).title ().strip ()
300311 formatted_name = formatted_name .replace ("X" , " X" ).replace ("Y" , " Y" ).replace ("Z" , " Z" )
301312 return formatted_name
313+
314+ @staticmethod
315+ def format_field_entity_id (field ):
316+ """Format field name to be more suitable for unique_id."""
317+ formatted_name = '' .join (['_' + char .lower () if char .isupper () else char for char in field ])
318+ formatted_name = formatted_name .replace ("x" , "_x" ).replace ("y" , "_y" ).replace ("z" , "_z" )
319+ return formatted_name .strip ('_' )
320+
321+ @staticmethod
322+ def format_version (version ):
323+ if version .lower () == "unknown" :
324+ return version
325+ parts = version .split ('.' )
326+ formatted_parts = []
327+ for part in parts :
328+ if part .strip (): # 检查部分是否为空
329+ try :
330+ formatted_parts .append (str (int (part )))
331+ except ValueError :
332+ formatted_parts .append ('unknown' )
333+ else :
334+ formatted_parts .append ('unknown' ) # 如果部分为空,设置为 'unknown'
335+
336+ formatted_version = '.' .join (formatted_parts )
337+ return formatted_version
302338
303339 @property
304340 def native_value (self ):
@@ -318,23 +354,42 @@ def device_class(self):
318354class BituoOTASensor (CoordinatorEntity , SensorEntity ):
319355 """Representation of an OTA status sensor."""
320356
321- def __init__ (self , coordinator , host_ip , model , fw_version ):
357+ def __init__ (self , coordinator , host_ip , model , fw_version , manufacturer , mcu_version ):
322358 """Initialize the OTA status sensor."""
323359 super ().__init__ (coordinator )
324360 self ._attr_name = "OTA Status"
325361 self ._attr_unique_id = f"{ host_ip } _ota_status"
326362 self ._attr_device_info = DeviceInfo (
327363 identifiers = {(DOMAIN , host_ip )},
328364 name = f"{ model } - { host_ip } " ,
329- manufacturer = "BituoTechnik" ,
365+ manufacturer = manufacturer ,
330366 model = model ,
331- sw_version = fw_version ,
367+ sw_version = f"S{ fw_version } _M{ self .format_version (mcu_version )} " ,
368+ configuration_url = f"http://{ host_ip } " # embed URL
332369 )
333370 self ._host_ip = host_ip
334- self ._attr_state = "Unknown "
371+ self ._attr_state = "unknown "
335372 coordinator .ota_entity = self # 存储自身的引用到协调器中
336373 self ._attr_icon = "mdi:update"
337374 self .entity_id = f"sensor.{ self ._attr_unique_id .replace ('.' , '_' )} "
375+
376+ @staticmethod
377+ def format_version (version ):
378+ if version .lower () == "unknown" :
379+ return version
380+ parts = version .split ('.' )
381+ formatted_parts = []
382+ for part in parts :
383+ if part .strip (): # 检查部分是否为空
384+ try :
385+ formatted_parts .append (str (int (part )))
386+ except ValueError :
387+ formatted_parts .append ('unknown' )
388+ else :
389+ formatted_parts .append ('unknown' ) # 如果部分为空,设置为 'unknown'
390+
391+ formatted_version = '.' .join (formatted_parts )
392+ return formatted_version
338393
339394 @property
340395 def native_value (self ):
0 commit comments