@@ -315,12 +315,16 @@ def __init__(self):
315315 self ._thermo_locs : dict [str , Any ] = {}
316316
317317 self .cooling_active = False
318+ self .smile_fw_version : str | None = None
318319 self .gateway_id : str | None = None
319320 self .gw_data : dict [str , Any ] = {}
320321 self .gw_devices : dict [str , Any ] = {}
322+ self .smile_hw_version : str | None = None
323+ self .smile_mac_address : str | None = None
321324 self .smile_name : str | None = None
322325 self .smile_type : str | None = None
323326 self .smile_version : list [str ] = []
327+ self .smile_zigbee_mac_address : str | None = None
324328
325329 def _locations_legacy (self ) -> None :
326330 """Helper-function for _all_locations().
@@ -414,18 +418,32 @@ def _get_module_data(
414418 Collect requested info from MODULES.
415419 """
416420 appl_search = appliance .find (locator )
421+ model_data = {
422+ "contents" : False ,
423+ "vendor_name" : None ,
424+ "vendor_model" : None ,
425+ "hardware_version" : None ,
426+ "firmware_version" : None ,
427+ "zigbee_mac_address" : None ,
428+ }
417429 if appl_search is not None :
418430 link_id = appl_search .attrib ["id" ]
419431 locator = f".//{ mod_type } [@id='{ link_id } ']...."
420432 module = self ._modules .find (locator )
421433 if module is not None :
422- v_name = module .find ("vendor_name" ).text
423- v_model = module .find ("vendor_model" ).text
424- hw_version = module .find ("hardware_version" ).text
425- fw_version = module .find ("firmware_version" ).text
426-
427- return [v_name , v_model , hw_version , fw_version ]
428- return [None , None , None , None ]
434+ model_data ["contents" ] = True
435+ model_data ["vendor_name" ] = module .find ("vendor_name" ).text
436+ model_data ["vendor_model" ] = module .find ("vendor_model" ).text
437+ model_data ["hardware_version" ] = module .find ("hardware_version" ).text
438+ model_data ["firmware_version" ] = module .find ("firmware_version" ).text
439+ # Adam
440+ if found := module .find (".//protocols/zig_bee_node" ):
441+ model_data ["zigbee_mac_address" ] = found .find ("mac_address" ).text
442+ # Stretches
443+ if found := module .find (".//protocols/network_router" ):
444+ model_data ["zigbee_mac_address" ] = found .find ("mac_address" ).text
445+
446+ return model_data
429447
430448 def _energy_device_info_finder (self , appliance : etree , appl : Munch ) -> Munch :
431449 """Helper-function for _appliance_info_finder().
@@ -435,33 +453,47 @@ def _energy_device_info_finder(self, appliance: etree, appl: Munch) -> Munch:
435453 locator = ".//services/electricity_point_meter"
436454 mod_type = "electricity_point_meter"
437455 module_data = self ._get_module_data (appliance , locator , mod_type )
438- appl .v_name = module_data [0 ]
456+ if not module_data ["contents" ]:
457+ return None
458+
459+ appl .v_name = module_data ["vendor_name" ]
439460 if appl .model != "Switchgroup" :
440461 appl .model = None
441- if module_data [2 ] is not None :
442- hw_version = module_data [2 ].replace ("-" , "" )
462+ appl .hw = module_data ["hardware_version" ]
463+ if appl .hw :
464+ hw_version = module_data ["hardware_version" ].replace ("-" , "" )
443465 appl .model = version_to_model (hw_version )
444- appl .fw = module_data [3 ]
466+ appl .fw = module_data ["firmware_version" ]
467+ appl .zigbee_mac = module_data ["zigbee_mac_address" ]
445468 return appl
446469
447470 if self .smile_type != "stretch" and "plug" in appl .types :
448471 locator = ".//logs/point_log/electricity_point_meter"
449472 mod_type = "electricity_point_meter"
450473 module_data = self ._get_module_data (appliance , locator , mod_type )
451- appl .v_name = module_data [0 ]
452- appl .model = version_to_model (module_data [1 ])
453- appl .fw = module_data [3 ]
474+ appl .v_name = module_data ["vendor_name" ]
475+ appl .model = version_to_model (module_data ["vendor_model" ])
476+ appl .hw = module_data ["hardware_version" ]
477+ appl .fw = module_data ["firmware_version" ]
478+ appl .zigbee_mac = module_data ["zigbee_mac_address" ]
454479 return appl
455480
456481 def _appliance_info_finder (self , appliance : etree , appl : Munch ) -> Munch :
457482 """Collect device info (Smile/Stretch, Thermostats, OpenTherm/On-Off): firmware, model and vendor name."""
458483 # Find gateway and heater_central devices
459484 if appl .pwclass == "gateway" :
460485 self .gateway_id = appliance .attrib ["id" ]
461- appl .fw = self .smile_version [0 ]
486+ appl .fw = self .smile_fw_version
487+ appl .mac = self .smile_mac_address
462488 appl .model = appl .name = self .smile_name
463489 appl .v_name = "Plugwise B.V."
464490
491+ # Adam: check for ZigBee mac address
492+ if self .smile_name == "Adam" and (
493+ found := self ._domain_objects .find (".//protocols/zig_bee_coordinator" )
494+ ):
495+ appl .zigbee_mac = found .find ("mac_address" ).text
496+
465497 # Adam: check for cooling capability and active heating/cooling operation-mode
466498 mode_list : list [str ] = []
467499 locator = "./actuator_functionalities/regulation_mode_control_functionality"
@@ -480,9 +512,10 @@ def _appliance_info_finder(self, appliance: etree, appl: Munch) -> Munch:
480512 locator = ".//logs/point_log[type='thermostat']/thermostat"
481513 mod_type = "thermostat"
482514 module_data = self ._get_module_data (appliance , locator , mod_type )
483- appl .v_name = module_data [0 ]
484- appl .model = check_model (module_data [1 ], appl .v_name )
485- appl .fw = module_data [3 ]
515+ appl .v_name = module_data ["vendor_name" ]
516+ appl .model = check_model (module_data ["vendor_model" ], appl .v_name )
517+ appl .hw = module_data ["hardware_version" ]
518+ appl .fw = module_data ["firmware_version" ]
486519
487520 return appl
488521
@@ -506,10 +539,11 @@ def _appliance_info_finder(self, appliance: etree, appl: Munch) -> Munch:
506539 locator2 = ".//services/boiler_state"
507540 mod_type = "boiler_state"
508541 module_data = self ._get_module_data (appliance , locator1 , mod_type )
509- if module_data == [ None , None , None , None ]:
542+ if not module_data [ "contents" ]:
510543 module_data = self ._get_module_data (appliance , locator2 , mod_type )
511- appl .v_name = module_data [0 ]
512- appl .model = check_model (module_data [1 ], appl .v_name )
544+ appl .v_name = module_data ["vendor_name" ]
545+ appl .hw = module_data ["hardware_version" ]
546+ appl .model = check_model (module_data ["vendor_model" ], appl .v_name )
513547 if appl .model is None :
514548 appl .model = (
515549 "Generic heater/cooler"
@@ -519,7 +553,9 @@ def _appliance_info_finder(self, appliance: etree, appl: Munch) -> Munch:
519553 return appl
520554
521555 # Handle stretches
522- self ._energy_device_info_finder (appliance , appl )
556+ appl = self ._energy_device_info_finder (appliance , appl )
557+ if not appl :
558+ return None
523559
524560 # Cornercase just return existing dict-object
525561 return appl # pragma: no cover
@@ -560,7 +596,9 @@ def _all_appliances(self) -> None:
560596 if self ._smile_legacy :
561597 self ._appl_data [self ._home_location ] = {
562598 "class" : "gateway" ,
563- "fw" : self .smile_version [0 ],
599+ "fw" : self .smile_fw_version ,
600+ "hw" : self .smile_hw_version ,
601+ "mac_address" : self .smile_mac_address ,
564602 "location" : self ._home_location ,
565603 "vendor" : "Plugwise B.V." ,
566604 }
@@ -580,7 +618,11 @@ def _all_appliances(self) -> None:
580618
581619 if self .smile_type == "stretch" :
582620 self ._appl_data [self ._home_location ].update (
583- {"model" : "Stretch" , "name" : "Stretch" }
621+ {
622+ "model" : "Stretch" ,
623+ "name" : "Stretch" ,
624+ "zigbee_mac_address" : self .smile_zigbee_mac_address ,
625+ }
584626 )
585627
586628 # The presence of either indicates a local active device, e.g. heat-pump or gas-fired heater
@@ -609,25 +651,42 @@ def _all_appliances(self) -> None:
609651 appl .name = appliance .find ("name" ).text
610652 appl .model = appl .pwclass .replace ("_" , " " ).title ()
611653 appl .fw = None
654+ appl .hw = None
655+ appl .mac = None
656+ appl .zigbee_mac = None
612657 appl .v_name = None
613658
614659 # Determine types for this appliance
615660 appl = self ._appliance_types_finder (appliance , appl )
616661
617662 # Determine class for this appliance
618663 appl = self ._appliance_info_finder (appliance , appl )
619- # Skip on heater_central when no active device present
664+ # Skip on heater_central when no active device present or on orphaned stretch devices
620665 if not appl :
621666 continue
622667
668+ if appl .pwclass == "gateway" :
669+ appl .fw = self .smile_fw_version
670+ appl .hw = self .smile_hw_version
671+
623672 self ._appl_data [appl .dev_id ] = {
624673 "class" : appl .pwclass ,
625674 "fw" : appl .fw ,
675+ "hw" : appl .hw ,
626676 "location" : appl .location ,
677+ "mac_address" : appl .mac ,
627678 "model" : appl .model ,
628679 "name" : appl .name ,
629680 "vendor" : appl .v_name ,
630681 }
682+
683+ if appl .zigbee_mac :
684+ self ._appl_data [appl .dev_id ].update (
685+ {
686+ "zigbee_mac_address" : appl .zigbee_mac ,
687+ }
688+ )
689+
631690 if (
632691 not self ._smile_legacy
633692 and appl .pwclass == "thermostat"
@@ -777,6 +836,22 @@ def _appliance_measurements(
777836 measure = appliance .find (i_locator ).text
778837 data [name ] = format_measure (measure , ENERGY_WATT_HOUR )
779838
839+ t_locator = (
840+ f".//actuator_functionalities/thermostat_functionality/{ measurement } "
841+ )
842+ t_functions = appliance .find (t_locator )
843+ if t_functions is not None and t_functions .text :
844+ # Thermostat actuator measurements
845+
846+ try :
847+ measurement = attrs [ATTR_NAME ]
848+ except KeyError :
849+ pass
850+
851+ data [measurement ] = format_measure (
852+ t_functions .text , attrs [ATTR_UNIT_OF_MEASUREMENT ]
853+ )
854+
780855 return data
781856
782857 def _get_appliance_data (self , d_id : str ) -> dict [str , Any ]:
@@ -900,20 +975,20 @@ def _scan_thermostats(self, debug_text="missing text") -> None:
900975 if thermo_matching [appl_class ] > high_prio :
901976 high_prio = thermo_matching [appl_class ]
902977
903- def _temperature_uri_legacy (self ) -> str :
904- """Helper-function for _temperature_uri ().
978+ def _thermostat_uri_legacy (self ) -> str :
979+ """Helper-function for _thermostat_uri ().
905980 Determine the location-set_temperature uri - from APPLIANCES.
906981 """
907982 locator = ".//appliance[type='thermostat']"
908983 appliance_id = self ._appliances .find (locator ).attrib ["id" ]
909984
910985 return f"{ APPLIANCES } ;id={ appliance_id } /thermostat"
911986
912- def _temperature_uri (self , loc_id : str ) -> str :
987+ def _thermostat_uri (self , loc_id : str ) -> str :
913988 """Helper-function for smile.py: set_temperature().
914989 Determine the location-set_temperature uri - from LOCATIONS."""
915990 if self ._smile_legacy :
916- return self ._temperature_uri_legacy ()
991+ return self ._thermostat_uri_legacy ()
917992
918993 locator = f'location[@id="{ loc_id } "]/actuator_functionalities/thermostat_functionality'
919994 thermostat_functionality_id = self ._locations .find (locator ).attrib ["id" ]
@@ -1214,7 +1289,7 @@ def _get_lock_state(self, xml: str) -> dict[str, Any]:
12141289 data : dict [str , Any ] = {}
12151290 actuator = "actuator_functionalities"
12161291 func_type = "relay_functionality"
1217- if self .smile_type == "stretch" and self . smile_version [ 1 ]. major == 2 :
1292+ if self ._stretch_v2 :
12181293 actuator = "actuators"
12191294 func_type = "relay"
12201295 appl_class = xml .find ("type" ).text
0 commit comments