zha: Handle both input and output clusters (#8410)

bellows 0.3.0 changes the API to have both, renaming the attribute which used
to be for input clusters in the process.

This is in preparation for remotes.
This commit is contained in:
Russell Cloran 2017-07-10 21:16:44 -07:00 committed by Paulus Schoutsen
parent 7a4cc8e082
commit 2f474a0ed8
6 changed files with 42 additions and 28 deletions

View file

@ -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:

View file

@ -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

View file

@ -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,

View file

@ -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,
)

View file

@ -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])

View file

@ -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