diff --git a/homeassistant/components/zha/api.py b/homeassistant/components/zha/api.py index 82c57abd82bdb29326872acc726ba9d498deb57e..91fe51f7793bdf1d7fa44ec53dfec89d889feb95 100644 --- a/homeassistant/components/zha/api.py +++ b/homeassistant/components/zha/api.py @@ -807,18 +807,12 @@ async def async_binding_operation(zha_gateway, source_ieee, target_ieee, operati clusters_to_bind = await get_matched_clusters(source_device, target_device) + zdo = source_device.device.zdo bind_tasks = [] - for cluster_pair in clusters_to_bind: - destination_address = zdo_types.MultiAddress() - destination_address.addrmode = 3 - destination_address.ieee = target_device.ieee - destination_address.endpoint = cluster_pair.target_cluster.endpoint.endpoint_id - - zdo = cluster_pair.source_cluster.endpoint.device.zdo - + for binding_pair in clusters_to_bind: op_msg = "cluster: %s %s --> [%s]" op_params = ( - cluster_pair.source_cluster.cluster_id, + binding_pair.source_cluster.cluster_id, operation.name, target_ieee, ) @@ -829,9 +823,9 @@ async def async_binding_operation(zha_gateway, source_ieee, target_ieee, operati zdo.request( operation, source_device.ieee, - cluster_pair.source_cluster.endpoint.endpoint_id, - cluster_pair.source_cluster.cluster_id, - destination_address, + binding_pair.source_cluster.endpoint.endpoint_id, + binding_pair.source_cluster.cluster_id, + binding_pair.destination_address, ), op_msg, op_params, diff --git a/homeassistant/components/zha/core/helpers.py b/homeassistant/components/zha/core/helpers.py index 9b62cef4ace2f99346960185a1a74e2bb3e58b1f..47911fc1078498f0da5251003a82384e6f99c63c 100644 --- a/homeassistant/components/zha/core/helpers.py +++ b/homeassistant/components/zha/core/helpers.py @@ -7,7 +7,7 @@ https://home-assistant.io/integrations/zha/ import asyncio import binascii -import collections +from dataclasses import dataclass import functools import itertools import logging @@ -19,13 +19,29 @@ import voluptuous as vol import zigpy.exceptions import zigpy.types import zigpy.util +import zigpy.zdo.types as zdo_types from homeassistant.core import State, callback from .const import CLUSTER_TYPE_IN, CLUSTER_TYPE_OUT, DATA_ZHA, DATA_ZHA_GATEWAY from .registries import BINDABLE_CLUSTERS +from .typing import ZhaDeviceType, ZigpyClusterType -ClusterPair = collections.namedtuple("ClusterPair", "source_cluster target_cluster") + +@dataclass +class BindingPair: + """Information for binding.""" + + source_cluster: ZigpyClusterType + target_ieee: zigpy.types.EUI64 + target_ep_id: int + + @property + def destination_address(self) -> zdo_types.MultiAddress: + """Return a ZDO multi address instance.""" + return zdo_types.MultiAddress( + addrmode=3, ieee=self.target_ieee, endpoint=self.target_ep_id + ) async def safe_read( @@ -49,7 +65,9 @@ async def safe_read( return {} -async def get_matched_clusters(source_zha_device, target_zha_device): +async def get_matched_clusters( + source_zha_device: ZhaDeviceType, target_zha_device: ZhaDeviceType +) -> List[BindingPair]: """Get matched input/output cluster pairs for 2 devices.""" source_clusters = source_zha_device.async_get_std_clusters() target_clusters = target_zha_device.async_get_std_clusters() @@ -59,15 +77,26 @@ async def get_matched_clusters(source_zha_device, target_zha_device): for cluster_id in source_clusters[endpoint_id][CLUSTER_TYPE_OUT]: if cluster_id not in BINDABLE_CLUSTERS: continue + if target_zha_device.nwk == 0x0000: + cluster_pair = BindingPair( + source_cluster=source_clusters[endpoint_id][CLUSTER_TYPE_OUT][ + cluster_id + ], + target_ieee=target_zha_device.ieee, + target_ep_id=target_zha_device.device.application.get_endpoint_id( + cluster_id, is_server_cluster=True + ), + ) + clusters_to_bind.append(cluster_pair) + continue for t_endpoint_id in target_clusters: if cluster_id in target_clusters[t_endpoint_id][CLUSTER_TYPE_IN]: - cluster_pair = ClusterPair( + cluster_pair = BindingPair( source_cluster=source_clusters[endpoint_id][CLUSTER_TYPE_OUT][ cluster_id ], - target_cluster=target_clusters[t_endpoint_id][CLUSTER_TYPE_IN][ - cluster_id - ], + target_ieee=target_zha_device.ieee, + target_ep_id=t_endpoint_id, ) clusters_to_bind.append(cluster_pair) return clusters_to_bind @@ -76,6 +105,9 @@ async def get_matched_clusters(source_zha_device, target_zha_device): @callback def async_is_bindable_target(source_zha_device, target_zha_device): """Determine if target is bindable to source.""" + if target_zha_device.nwk == 0x0000: + return True + source_clusters = source_zha_device.async_get_std_clusters() target_clusters = target_zha_device.async_get_std_clusters()