diff --git a/homeassistant/components/zha/api.py b/homeassistant/components/zha/api.py index b5b29534ed90..aedc32ac94b4 100644 --- a/homeassistant/components/zha/api.py +++ b/homeassistant/components/zha/api.py @@ -54,6 +54,7 @@ from .core.const import ( WARNING_DEVICE_SQUAWK_MODE_ARMED, WARNING_DEVICE_STROBE_HIGH, WARNING_DEVICE_STROBE_YES, + ZHA_CHANNEL_MSG, ZHA_CONFIG_SCHEMAS, ) from .core.group import GroupMember @@ -468,34 +469,21 @@ async def websocket_reconfigure_node(hass, connection, msg): zha_gateway: ZhaGatewayType = hass.data[DATA_ZHA][DATA_ZHA_GATEWAY] ieee = msg[ATTR_IEEE] device: ZhaDeviceType = zha_gateway.get_device(ieee) - ieee_str = str(device.ieee) - nwk_str = device.nwk.__repr__() - - class DeviceLogFilterer(logging.Filter): - """Log filterer that limits messages to the specified device.""" - - def filter(self, record): - message = record.getMessage() - return nwk_str in message or ieee_str in message - - filterer = DeviceLogFilterer() async def forward_messages(data): """Forward events to websocket.""" connection.send_message(websocket_api.event_message(msg["id"], data)) remove_dispatcher_function = async_dispatcher_connect( - hass, "zha_gateway_message", forward_messages + hass, ZHA_CHANNEL_MSG, forward_messages ) @callback def async_cleanup() -> None: - """Remove signal listener and turn off debug mode.""" - zha_gateway.async_disable_debug_mode(filterer=filterer) + """Remove signal listener.""" remove_dispatcher_function() connection.subscriptions[msg["id"]] = async_cleanup - zha_gateway.async_enable_debug_mode(filterer=filterer) _LOGGER.debug("Reconfiguring node with ieee_address: %s", ieee) hass.async_create_task(device.async_configure()) diff --git a/homeassistant/components/zha/core/channels/__init__.py b/homeassistant/components/zha/core/channels/__init__.py index 289f1c36d4df..e6d2d722f615 100644 --- a/homeassistant/components/zha/core/channels/__init__.py +++ b/homeassistant/components/zha/core/channels/__init__.py @@ -130,6 +130,13 @@ class Channels: await self.zdo_channel.async_configure() self.zdo_channel.debug("'async_configure' stage succeeded") await asyncio.gather(*(pool.async_configure() for pool in self.pools)) + async_dispatcher_send( + self.zha_device.hass, + const.ZHA_CHANNEL_MSG, + { + const.ATTR_TYPE: const.ZHA_CHANNEL_CFG_DONE, + }, + ) @callback def async_new_entity( diff --git a/homeassistant/components/zha/core/channels/base.py b/homeassistant/components/zha/core/channels/base.py index 4238707656df..4d1e71e884ea 100644 --- a/homeassistant/components/zha/core/channels/base.py +++ b/homeassistant/components/zha/core/channels/base.py @@ -11,6 +11,7 @@ import zigpy.exceptions from homeassistant.const import ATTR_COMMAND from homeassistant.core import callback +from homeassistant.helpers.dispatcher import async_dispatcher_send from .. import typing as zha_typing from ..const import ( @@ -18,10 +19,15 @@ from ..const import ( ATTR_ATTRIBUTE_ID, ATTR_ATTRIBUTE_NAME, ATTR_CLUSTER_ID, + ATTR_TYPE, ATTR_UNIQUE_ID, ATTR_VALUE, CHANNEL_ZDO, SIGNAL_ATTR_UPDATED, + ZHA_CHANNEL_MSG, + ZHA_CHANNEL_MSG_BIND, + ZHA_CHANNEL_MSG_CFG_RPT, + ZHA_CHANNEL_MSG_DATA, ) from ..helpers import LogMixin, safe_read @@ -148,10 +154,34 @@ class ZigbeeChannel(LogMixin): try: res = await self.cluster.bind() self.debug("bound '%s' cluster: %s", self.cluster.ep_attribute, res[0]) + async_dispatcher_send( + self._ch_pool.hass, + ZHA_CHANNEL_MSG, + { + ATTR_TYPE: ZHA_CHANNEL_MSG_BIND, + ZHA_CHANNEL_MSG_DATA: { + "cluster_name": self.cluster.name, + "cluster_id": self.cluster.cluster_id, + "success": res[0] == 0, + }, + }, + ) except (zigpy.exceptions.ZigbeeException, asyncio.TimeoutError) as ex: self.debug( "Failed to bind '%s' cluster: %s", self.cluster.ep_attribute, str(ex) ) + async_dispatcher_send( + self._ch_pool.hass, + ZHA_CHANNEL_MSG, + { + ATTR_TYPE: ZHA_CHANNEL_MSG_BIND, + ZHA_CHANNEL_MSG_DATA: { + "cluster_name": self.cluster.name, + "cluster_id": self.cluster.cluster_id, + "success": False, + }, + }, + ) async def configure_reporting(self) -> None: """Configure attribute reporting for a cluster. @@ -159,6 +189,7 @@ class ZigbeeChannel(LogMixin): This also swallows ZigbeeException exceptions that are thrown when devices are unreachable. """ + event_data = {} kwargs = {} if self.cluster.cluster_id >= 0xFC00 and self._ch_pool.manufacturer_code: kwargs["manufacturer"] = self._ch_pool.manufacturer_code @@ -167,6 +198,14 @@ class ZigbeeChannel(LogMixin): attr = report["attr"] attr_name = self.cluster.attributes.get(attr, [attr])[0] min_report_int, max_report_int, reportable_change = report["config"] + event_data[attr_name] = { + "min": min_report_int, + "max": max_report_int, + "id": attr, + "name": attr_name, + "change": reportable_change, + } + try: res = await self.cluster.configure_reporting( attr, min_report_int, max_report_int, reportable_change, **kwargs @@ -180,6 +219,9 @@ class ZigbeeChannel(LogMixin): reportable_change, res, ) + event_data[attr_name]["success"] = ( + res[0][0].status == 0 or res[0][0].status == 134 + ) except (zigpy.exceptions.ZigbeeException, asyncio.TimeoutError) as ex: self.debug( "failed to set reporting for '%s' attr on '%s' cluster: %s", @@ -187,6 +229,20 @@ class ZigbeeChannel(LogMixin): self.cluster.ep_attribute, str(ex), ) + event_data[attr_name]["success"] = False + + async_dispatcher_send( + self._ch_pool.hass, + ZHA_CHANNEL_MSG, + { + ATTR_TYPE: ZHA_CHANNEL_MSG_CFG_RPT, + ZHA_CHANNEL_MSG_DATA: { + "cluster_name": self.cluster.name, + "cluster_id": self.cluster.cluster_id, + "attributes": event_data, + }, + }, + ) async def async_configure(self) -> None: """Set cluster binding and attribute reporting.""" diff --git a/homeassistant/components/zha/core/const.py b/homeassistant/components/zha/core/const.py index 2576aa9f463e..7df850909f48 100644 --- a/homeassistant/components/zha/core/const.py +++ b/homeassistant/components/zha/core/const.py @@ -339,6 +339,11 @@ WARNING_DEVICE_SQUAWK_MODE_ARMED = 0 WARNING_DEVICE_SQUAWK_MODE_DISARMED = 1 ZHA_DISCOVERY_NEW = "zha_discovery_new_{}" +ZHA_CHANNEL_MSG = "zha_channel_message" +ZHA_CHANNEL_MSG_BIND = "zha_channel_bind" +ZHA_CHANNEL_MSG_CFG_RPT = "zha_channel_configure_reporting" +ZHA_CHANNEL_MSG_DATA = "zha_channel_msg_data" +ZHA_CHANNEL_CFG_DONE = "zha_channel_cfg_done" ZHA_GW_MSG = "zha_gateway_message" ZHA_GW_MSG_DEVICE_FULL_INIT = "device_fully_initialized" ZHA_GW_MSG_DEVICE_INFO = "device_info" diff --git a/tests/components/zha/common.py b/tests/components/zha/common.py index eeffa3fb9112..45caed95ae6a 100644 --- a/tests/components/zha/common.py +++ b/tests/components/zha/common.py @@ -93,7 +93,11 @@ def patch_cluster(cluster): return (result,) cluster.bind = AsyncMock(return_value=[0]) - cluster.configure_reporting = AsyncMock(return_value=[0]) + cluster.configure_reporting = AsyncMock( + return_value=[ + [zcl_f.ConfigureReportingResponseRecord(zcl_f.Status.SUCCESS, 0x00, 0xAABB)] + ] + ) cluster.deserialize = Mock() cluster.handle_cluster_request = Mock() cluster.read_attributes = AsyncMock(wraps=cluster.read_attributes)