@@ -456,7 +456,9 @@ def _appliance_info_finder(self, appliance: etree, appl: Munch) -> Munch:
456456 if not self ._opentherm_device and not self ._on_off_device :
457457 return None
458458
459- self ._heater_id = appliance .attrib ["id" ]
459+ # Find the valid heater_central
460+ self ._heater_id = self ._check_heater_central ()
461+
460462 # Info for On-Off device
461463 if self ._on_off_device :
462464 appl .name = "OnOff"
@@ -498,6 +500,34 @@ def _appliance_info_finder(self, appliance: etree, appl: Munch) -> Munch:
498500
499501 return appl
500502
503+ def _check_heater_central (self ) -> str :
504+ """Find the valid heater_central, helper-function for _appliance_info_finder().
505+
506+ Solution for Core Issue #104433,
507+ for a system that has two heater_central appliances.
508+ """
509+ locator = "./appliance[type='heater_central']"
510+ hc_count = 0
511+ hc_list : list [dict [str , bool ]] = []
512+ for heater_central in self ._appliances .findall (locator ):
513+ hc_count += 1
514+ hc_id : str = heater_central .attrib ["id" ]
515+ has_actuators : bool = (
516+ heater_central .find ("actuator_functionalities/" ) is not None
517+ )
518+ hc_list .append ({hc_id : has_actuators })
519+
520+ heater_central_id = list (hc_list [0 ].keys ())[0 ]
521+ if hc_count > 1 :
522+ for item in hc_list :
523+ for key , value in item .items ():
524+ if value :
525+ heater_central_id = key
526+ # Stop when a valid id is found
527+ break
528+
529+ return heater_central_id
530+
501531 def _p1_smartmeter_info_finder (self , appl : Munch ) -> None :
502532 """Collect P1 DSMR Smartmeter info."""
503533 loc_id = next (iter (self ._loc_data .keys ()))
@@ -570,21 +600,15 @@ def _all_appliances(self) -> None:
570600 # Legacy P1 has no more devices
571601 return
572602
573- hc_count = 0
574603 for appliance in self ._appliances .findall ("./appliance" ):
575604 appl = Munch ()
576605 appl .pwclass = appliance .find ("type" ).text
577- # Count amount of heater_central's
578- if appl .pwclass == "heater_central" :
579- hc_count += 1
580- # Mark heater_central and thermostat that don't have actuator_functionalities,
581- # could be an orphaned device (Core #81712, #104433)
582- appl .has_actuators = True
606+ # Skip thermostats that have this key, should be an orphaned device (Core #81712)
583607 if (
584- appl .pwclass in [ "heater_central" , " thermostat"]
608+ appl .pwclass == " thermostat"
585609 and appliance .find ("actuator_functionalities/" ) is None
586610 ):
587- appl . has_actuators = False
611+ continue
588612
589613 appl .location = None
590614 if (appl_loc := appliance .find ("location" )) is not None :
@@ -610,6 +634,10 @@ def _all_appliances(self) -> None:
610634 if not (appl := self ._appliance_info_finder (appliance , appl )):
611635 continue
612636
637+ # Skip orphaned heater_central (Core Issue #104433)
638+ if appl .pwclass == "heater_central" and appl .dev_id != self ._heater_id :
639+ continue
640+
613641 # P1: for gateway and smartmeter switch device_id - part 1
614642 # This is done to avoid breakage in HA Core
615643 if appl .pwclass == "gateway" and self .smile_type == "power" :
@@ -628,7 +656,6 @@ def _all_appliances(self) -> None:
628656 for key , value in {
629657 "firmware" : appl .firmware ,
630658 "hardware" : appl .hardware ,
631- "has_actuators" : appl .has_actuators ,
632659 "location" : appl .location ,
633660 "mac_address" : appl .mac ,
634661 "model" : appl .model ,
@@ -641,22 +668,6 @@ def _all_appliances(self) -> None:
641668 self .gw_devices [appl .dev_id ][appl_key ] = value
642669 self ._count += 1
643670
644- # Remove thermostat with empty actuator_functionalities (Core #81712), remove heater_central
645- # with empty actuator_functionalities but only when there are more than one (Core #104433).
646- for dev_id , device in dict (self .gw_devices ).items ():
647- if device ["dev_class" ] == "thermostat" or (
648- device ["dev_class" ] == "heater_central" and hc_count > 1
649- ):
650- if not self .gw_devices [dev_id ]["has_actuators" ]:
651- self ._count -= len (self .gw_devices [dev_id ])
652- self .gw_devices .pop (dev_id )
653- else :
654- self .gw_devices [dev_id ].pop ("has_actuators" )
655- self ._count -= 1
656- elif "has_actuators" in self .gw_devices [dev_id ]:
657- self .gw_devices [dev_id ].pop ("has_actuators" )
658- self ._count -= 1
659-
660671 # For non-legacy P1 collect the connected SmartMeter info
661672 if self .smile_type == "power" :
662673 self ._p1_smartmeter_info_finder (appl )
0 commit comments