Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 12 additions & 15 deletions homeassistant/components/binary_sensor/zha.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down Expand Up @@ -131,7 +128,7 @@ def cluster_command(self, tsn, command_id, args):

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'],
Expand All @@ -141,7 +138,7 @@ async def async_update(self):
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
Expand Down
6 changes: 3 additions & 3 deletions homeassistant/components/sensor/zha.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Copy link
Copy Markdown

@ryanwinter ryanwinter Sep 10, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You dropped the min and max report parameters.

Reverting to the default means the max will change from 600 to 900. Is this important?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The max_report interval is more of a "keep_alive" update, which is sent even if the attribute hasn't changed. IMO doesn't really matter if it 10min or 15min, but it is more graceful on battery operated devices. The only downside, it is going to be 5 min longer for the status to synchronize if the device was offline for whatever reason.

Once/If this PR gets merged, I'm planning to submit another one where min/max_report interval will be part of base Sensor class, allowing child classes to override those to match sensor specifics.

)

return sensor
Expand Down
18 changes: 12 additions & 6 deletions homeassistant/components/switch/zha.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,23 @@
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):
Expand Down
39 changes: 39 additions & 0 deletions homeassistant/components/zha/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -435,3 +435,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:
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should the method stop execution if this fails?

Copy link
Copy Markdown
Contributor Author

@Adminiuga Adminiuga Sep 12, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is the same idea as behind zha.safe_read() Better to have an entity which may not push external updates (eg manually toggled switch), but still works otherwise and could be controlled, rather than no entity at all.
Some vendors (eg Xiaomi) will throw, but still would send attribute reports.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the long run I'm thinking about having it as a "service call" on a device, like in a "repair_device" service call

_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)
)