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
18 changes: 3 additions & 15 deletions homeassistant/components/zha/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
WARNING_DEVICE_SQUAWK_MODE_ARMED,
WARNING_DEVICE_STROBE_HIGH,
WARNING_DEVICE_STROBE_YES,
ZHA_CHANNEL_MSG,
ZHA_CONFIG_SCHEMAS,
)
from .core.group import GroupMember
Expand Down Expand Up @@ -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())
Expand Down
7 changes: 7 additions & 0 deletions homeassistant/components/zha/core/channels/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,13 @@ async def async_configure(self) -> None:
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(
Expand Down
56 changes: 56 additions & 0 deletions homeassistant/components/zha/core/channels/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,23 @@

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 (
ATTR_ARGS,
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

Expand Down Expand Up @@ -148,17 +154,42 @@ async def bind(self):
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.

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
Expand All @@ -167,6 +198,14 @@ async def configure_reporting(self) -> None:
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
Expand All @@ -180,13 +219,30 @@ async def configure_reporting(self) -> None:
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",
attr_name,
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."""
Expand Down
5 changes: 5 additions & 0 deletions homeassistant/components/zha/core/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,11 @@ def description(self) -> str:
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"
Expand Down
6 changes: 5 additions & 1 deletion tests/components/zha/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,11 @@ async def _read_attribute_raw(attributes, *args, **kwargs):
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)
Expand Down