From f2203e52efd03ab957ad6488ba97364b45d4dca1 Mon Sep 17 00:00:00 2001
From: Alexei Chetroi <lexoid@gmail.com>
Date: Thu, 13 Sep 2018 03:11:47 -0400
Subject: [PATCH] Add configure_reporting() method to zha component (#16487)

* Add zha.configure_reporting() method.

Binds a cluster and configures reporting for the specified attribute.

* git add homeassistant/components/binary_sensor/zha.py

* Refactor sensor.zha to use new 'configure_reporting() method.

* Zha configure reporting - switch (#1)

* use configure_reporting for zha switch

* lint fixes

* Rename variables/classes to properly reflect the content
---
 homeassistant/components/binary_sensor/zha.py | 27 ++++++-------
 homeassistant/components/sensor/zha.py        |  6 +--
 homeassistant/components/switch/zha.py        | 18 ++++++---
 homeassistant/components/zha/__init__.py      | 39 +++++++++++++++++++
 4 files changed, 66 insertions(+), 24 deletions(-)

diff --git a/homeassistant/components/binary_sensor/zha.py b/homeassistant/components/binary_sensor/zha.py
index cabbbd704a0..0842ab7db6e 100644
--- a/homeassistant/components/binary_sensor/zha.py
+++ b/homeassistant/components/binary_sensor/zha.py
@@ -65,28 +65,25 @@ async def _async_setup_iaszone(hass, config, async_add_entities,
 async def _async_setup_remote(hass, config, async_add_entities,
                               discovery_info):
 
-    async def safe(coro):
-        """Run coro, catching ZigBee delivery errors, and ignoring them."""
-        import zigpy.exceptions
-        try:
-            await coro
-        except zigpy.exceptions.DeliveryError as exc:
-            _LOGGER.warning("Ignoring error during setup: %s", exc)
+    remote = Remote(**discovery_info)
 
     if discovery_info['new_join']:
         from zigpy.zcl.clusters.general import OnOff, LevelControl
         out_clusters = discovery_info['out_clusters']
         if OnOff.cluster_id in out_clusters:
             cluster = out_clusters[OnOff.cluster_id]
-            await safe(cluster.bind())
-            await safe(cluster.configure_reporting(0, 0, 600, 1))
+            await zha.configure_reporting(
+                remote.entity_id, cluster, 0, min_report=0, max_report=600,
+                reportable_change=1
+            )
         if LevelControl.cluster_id in out_clusters:
             cluster = out_clusters[LevelControl.cluster_id]
-            await safe(cluster.bind())
-            await safe(cluster.configure_reporting(0, 1, 600, 1))
+            await zha.configure_reporting(
+                remote.entity_id, cluster, 0, min_report=1, max_report=600,
+                reportable_change=1
+            )
 
-    sensor = Switch(**discovery_info)
-    async_add_entities([sensor], update_before_add=True)
+    async_add_entities([remote], update_before_add=True)
 
 
 class BinarySensor(zha.Entity, BinarySensorDevice):
@@ -131,7 +128,7 @@ class BinarySensor(zha.Entity, BinarySensorDevice):
 
     async def async_update(self):
         """Retrieve latest state."""
-        from bellows.types.basic import uint16_t
+        from zigpy.types.basic import uint16_t
 
         result = await zha.safe_read(self._endpoint.ias_zone,
                                      ['zone_status'],
@@ -141,7 +138,7 @@ class BinarySensor(zha.Entity, BinarySensorDevice):
             self._state = result.get('zone_status', self._state) & 3
 
 
-class Switch(zha.Entity, BinarySensorDevice):
+class Remote(zha.Entity, BinarySensorDevice):
     """ZHA switch/remote controller/button."""
 
     _domain = DOMAIN
diff --git a/homeassistant/components/sensor/zha.py b/homeassistant/components/sensor/zha.py
index 6202f8cb7ef..0a6710f8f6c 100644
--- a/homeassistant/components/sensor/zha.py
+++ b/homeassistant/components/sensor/zha.py
@@ -57,9 +57,9 @@ def make_sensor(discovery_info):
 
     if discovery_info['new_join']:
         cluster = list(in_clusters.values())[0]
-        yield from cluster.bind()
-        yield from cluster.configure_reporting(
-            sensor.value_attribute, 300, 600, sensor.min_reportable_change,
+        yield from zha.configure_reporting(
+            sensor.entity_id, cluster, sensor.value_attribute,
+            reportable_change=sensor.min_reportable_change
         )
 
     return sensor
diff --git a/homeassistant/components/switch/zha.py b/homeassistant/components/switch/zha.py
index 9f780b631b6..7ac93180cd7 100644
--- a/homeassistant/components/switch/zha.py
+++ b/homeassistant/components/switch/zha.py
@@ -17,17 +17,23 @@ DEPENDENCIES = ['zha']
 async def async_setup_platform(hass, config, async_add_entities,
                                discovery_info=None):
     """Set up the Zigbee Home Automation switches."""
+    from zigpy.zcl.clusters.general import OnOff
+
     discovery_info = zha.get_discovery_info(hass, discovery_info)
     if discovery_info is None:
         return
 
-    from zigpy.zcl.clusters.general import OnOff
-    in_clusters = discovery_info['in_clusters']
-    cluster = in_clusters[OnOff.cluster_id]
-    await cluster.bind()
-    await cluster.configure_reporting(0, 0, 600, 1,)
+    switch = Switch(**discovery_info)
+
+    if discovery_info['new_join']:
+        in_clusters = discovery_info['in_clusters']
+        cluster = in_clusters[OnOff.cluster_id]
+        await zha.configure_reporting(
+            switch.entity_id, cluster, switch.value_attribute,
+            min_report=0, max_report=600, reportable_change=1
+        )
 
-    async_add_entities([Switch(**discovery_info)], update_before_add=True)
+    async_add_entities([switch], update_before_add=True)
 
 
 class Switch(zha.Entity, SwitchDevice):
diff --git a/homeassistant/components/zha/__init__.py b/homeassistant/components/zha/__init__.py
index 48c2ad20be0..4c1109f685d 100644
--- a/homeassistant/components/zha/__init__.py
+++ b/homeassistant/components/zha/__init__.py
@@ -399,3 +399,42 @@ async def safe_read(cluster, attributes, allow_cache=True):
         return result
     except Exception:  # pylint: disable=broad-except
         return {}
+
+
+async def configure_reporting(entity_id, cluster, attr, skip_bind=False,
+                              min_report=300, max_report=900,
+                              reportable_change=1):
+    """Configure attribute reporting for a cluster.
+
+    while swallowing the DeliverError exceptions in case of unreachable
+    devices.
+    """
+    from zigpy.exceptions import DeliveryError
+
+    attr_name = cluster.attributes.get(attr, [attr])[0]
+    cluster_name = cluster.ep_attribute
+    if not skip_bind:
+        try:
+            res = await cluster.bind()
+            _LOGGER.debug(
+                "%s: bound  '%s' cluster: %s", entity_id, cluster_name, res[0]
+            )
+        except DeliveryError as ex:
+            _LOGGER.debug(
+                "%s: Failed to bind '%s' cluster: %s",
+                entity_id, cluster_name, str(ex)
+            )
+
+    try:
+        res = await cluster.configure_reporting(attr, min_report,
+                                                max_report, reportable_change)
+        _LOGGER.debug(
+            "%s: reporting '%s' attr on '%s' cluster: %d/%d/%d: Status: %s",
+            entity_id, attr_name, cluster_name, min_report, max_report,
+            reportable_change, res[0][0].status
+        )
+    except DeliveryError as ex:
+        _LOGGER.debug(
+            "%s: failed to set reporting for '%s' attr on '%s' cluster: %s",
+            entity_id, attr_name, cluster_name, str(ex)
+        )
-- 
GitLab