diff --git a/homeassistant/components/binary_sensor/zha.py b/homeassistant/components/binary_sensor/zha.py index 1d895fbf3a7..ad7c29badf9 100644 --- a/homeassistant/components/binary_sensor/zha.py +++ b/homeassistant/components/binary_sensor/zha.py @@ -34,10 +34,10 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None): from bellows.zigbee.zcl.clusters.security import IasZone - clusters = discovery_info['clusters'] + in_clusters = discovery_info['in_clusters'] device_class = None - cluster = [c for c in clusters if isinstance(c, IasZone)][0] + cluster = in_clusters[IasZone.cluster_id] if discovery_info['new_join']: yield from cluster.bind() ieee = cluster.endpoint.device.application.ieee @@ -64,7 +64,7 @@ class BinarySensor(zha.Entity, BinarySensorDevice): super().__init__(**kwargs) self._device_class = device_class from bellows.zigbee.zcl.clusters.security import IasZone - self._ias_zone_cluster = self._clusters[IasZone.cluster_id] + self._ias_zone_cluster = self._in_clusters[IasZone.cluster_id] @property def is_on(self) -> bool: diff --git a/homeassistant/components/light/zha.py b/homeassistant/components/light/zha.py index 650d9f909b1..619723d3168 100644 --- a/homeassistant/components/light/zha.py +++ b/homeassistant/components/light/zha.py @@ -46,10 +46,10 @@ class Light(zha.Entity, light.Light): self._brightness = None import bellows.zigbee.zcl.clusters as zcl_clusters - if zcl_clusters.general.LevelControl.cluster_id in self._clusters: + if zcl_clusters.general.LevelControl.cluster_id in self._in_clusters: self._supported_features |= light.SUPPORT_BRIGHTNESS self._brightness = 0 - if zcl_clusters.lighting.Color.cluster_id in self._clusters: + if zcl_clusters.lighting.Color.cluster_id in self._in_clusters: # Not sure all color lights necessarily support this directly # Should we emulate it? self._supported_features |= light.SUPPORT_COLOR_TEMP diff --git a/homeassistant/components/sensor/zha.py b/homeassistant/components/sensor/zha.py index 7ac76a35b79..ca4ab6bbff3 100644 --- a/homeassistant/components/sensor/zha.py +++ b/homeassistant/components/sensor/zha.py @@ -31,17 +31,16 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None): @asyncio.coroutine def make_sensor(discovery_info): """Create ZHA sensors factory.""" - from bellows.zigbee import zcl - if isinstance(discovery_info['clusters'][0], - zcl.clusters.measurement.TemperatureMeasurement): + from bellows.zigbee.zcl.clusters.measurement import TemperatureMeasurement + in_clusters = discovery_info['in_clusters'] + if TemperatureMeasurement.cluster_id in in_clusters: sensor = TemperatureSensor(**discovery_info) else: sensor = Sensor(**discovery_info) - clusters = discovery_info['clusters'] attr = sensor.value_attribute if discovery_info['new_join']: - cluster = clusters[0] + cluster = list(in_clusters.values())[0] yield from cluster.bind() yield from cluster.configure_reporting( attr, 300, 600, sensor.min_reportable_change, diff --git a/homeassistant/components/zha/__init__.py b/homeassistant/components/zha/__init__.py index 5937f1865f5..e397b7d042a 100644 --- a/homeassistant/components/zha/__init__.py +++ b/homeassistant/components/zha/__init__.py @@ -14,7 +14,7 @@ from homeassistant import const as ha_const from homeassistant.helpers import discovery, entity from homeassistant.util import slugify -REQUIREMENTS = ['bellows==0.2.7'] +REQUIREMENTS = ['bellows==0.3.2'] DOMAIN = 'zha' @@ -128,6 +128,10 @@ class ApplicationListener: """Handle device joined and basic information discovered.""" self._hass.async_add_job(self.async_device_initialized(device, True)) + def device_left(self, device): + """Handle device leaving the network.""" + pass + @asyncio.coroutine def async_device_initialized(self, device, join): """Handle device joined and basic information discovered (async).""" @@ -142,7 +146,7 @@ class ApplicationListener: discovered_info = yield from _discover_endpoint_info(endpoint) component = None - used_clusters = [] + profile_clusters = ([], []) device_key = '%s-%s' % (str(device.ieee), endpoint_id) node_config = self._config[DOMAIN][CONF_DEVICE_CONFIG].get( device_key, {}) @@ -152,20 +156,25 @@ class ApplicationListener: if zha_const.DEVICE_CLASS.get(endpoint.profile_id, {}).get(endpoint.device_type, None): - used_clusters = profile.CLUSTERS[endpoint.device_type] + profile_clusters = profile.CLUSTERS[endpoint.device_type] profile_info = zha_const.DEVICE_CLASS[endpoint.profile_id] component = profile_info[endpoint.device_type] if ha_const.CONF_TYPE in node_config: component = node_config[ha_const.CONF_TYPE] - used_clusters = zha_const.COMPONENT_CLUSTERS[component] + profile_clusters = zha_const.COMPONENT_CLUSTERS[component] if component: - clusters = [endpoint.clusters[c] for c in used_clusters if c in - endpoint.clusters] + in_clusters = [endpoint.in_clusters[c] + for c in profile_clusters[0] + if c in endpoint.in_clusters] + out_clusters = [endpoint.out_clusters[c] + for c in profile_clusters[1] + if c in endpoint.out_clusters] discovery_info = { 'endpoint': endpoint, - 'clusters': clusters, + 'in_clusters': {c.cluster_id: c for c in in_clusters}, + 'out_clusters': {c.cluster_id: c for c in out_clusters}, 'new_join': join, } discovery_info.update(discovered_info) @@ -179,9 +188,9 @@ class ApplicationListener: self._config, ) - for cluster_id, cluster in endpoint.clusters.items(): + for cluster_id, cluster in endpoint.in_clusters.items(): cluster_type = type(cluster) - if cluster_id in used_clusters: + if cluster_id in profile_clusters[0]: continue if cluster_type not in zha_const.SINGLE_CLUSTER_DEVICE_CLASS: continue @@ -189,7 +198,8 @@ class ApplicationListener: component = zha_const.SINGLE_CLUSTER_DEVICE_CLASS[cluster_type] discovery_info = { 'endpoint': endpoint, - 'clusters': [cluster], + 'in_clusters': {cluster.cluster_id: cluster}, + 'out_clusters': {}, 'new_join': join, } discovery_info.update(discovered_info) @@ -210,7 +220,8 @@ class Entity(entity.Entity): _domain = None # Must be overriden by subclasses - def __init__(self, endpoint, clusters, manufacturer, model, **kwargs): + def __init__(self, endpoint, in_clusters, out_clusters, manufacturer, + model, **kwargs): """Init ZHA entity.""" self._device_state_attributes = {} ieeetail = ''.join([ @@ -234,10 +245,13 @@ class Entity(entity.Entity): ieeetail, endpoint.endpoint_id, ) - for cluster in clusters: + for cluster in in_clusters.values(): + cluster.add_listener(self) + for cluster in out_clusters.values(): cluster.add_listener(self) self._endpoint = endpoint - self._clusters = {c.cluster_id: c for c in clusters} + self._in_clusters = in_clusters + self._out_clusters = out_clusters self._state = ha_const.STATE_UNKNOWN def attribute_updated(self, attribute, value): @@ -261,13 +275,13 @@ def _discover_endpoint_info(endpoint): 'manufacturer': None, 'model': None, } - if 0 not in endpoint.clusters: + if 0 not in endpoint.in_clusters: return extra_info @asyncio.coroutine def read(attributes): """Read attributes and update extra_info convenience function.""" - result, _ = yield from endpoint.clusters[0].read_attributes( + result, _ = yield from endpoint.in_clusters[0].read_attributes( attributes, allow_cache=True, ) diff --git a/homeassistant/components/zha/const.py b/homeassistant/components/zha/const.py index ed06f18c1f5..b1659536e32 100644 --- a/homeassistant/components/zha/const.py +++ b/homeassistant/components/zha/const.py @@ -46,6 +46,7 @@ def populate_data(): profile = PROFILES[profile_id] for device_type, component in classes.items(): if component not in COMPONENT_CLUSTERS: - COMPONENT_CLUSTERS[component] = set() + COMPONENT_CLUSTERS[component] = (set(), set()) clusters = profile.CLUSTERS[device_type] - COMPONENT_CLUSTERS[component].update(clusters) + COMPONENT_CLUSTERS[component][0].update(clusters[0]) + COMPONENT_CLUSTERS[component][1].update(clusters[1]) diff --git a/requirements_all.txt b/requirements_all.txt index d4e3add1774..37e3f8502f9 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -93,7 +93,7 @@ batinfo==0.4.2 beautifulsoup4==4.6.0 # homeassistant.components.zha -bellows==0.2.7 +bellows==0.3.2 # homeassistant.components.blink blinkpy==0.6.0