diff --git a/homeassistant/components/zha/core/channels/base.py b/homeassistant/components/zha/core/channels/base.py index 0dd6169373b..50546b7720c 100644 --- a/homeassistant/components/zha/core/channels/base.py +++ b/homeassistant/components/zha/core/channels/base.py @@ -8,7 +8,7 @@ import logging from typing import Any import zigpy.exceptions -from zigpy.zcl.foundation import ConfigureReportingResponseRecord, Status +from zigpy.zcl.foundation import CommandSchema, ConfigureReportingResponseRecord, Status from homeassistant.const import ATTR_COMMAND from homeassistant.core import callback @@ -20,6 +20,7 @@ from ..const import ( ATTR_ATTRIBUTE_ID, ATTR_ATTRIBUTE_NAME, ATTR_CLUSTER_ID, + ATTR_PARAMS, ATTR_TYPE, ATTR_UNIQUE_ID, ATTR_VALUE, @@ -354,14 +355,27 @@ class ZigbeeChannel(LogMixin): """Handle ZDO commands on this cluster.""" @callback - def zha_send_event(self, command: str, args: int | dict) -> None: + def zha_send_event(self, command: str, arg: list | dict | CommandSchema) -> None: """Relay events to hass.""" + + if isinstance(arg, CommandSchema): + args = [a for a in arg if a is not None] + params = arg.as_dict() + elif isinstance(arg, (list, dict)): + # Quirks can directly send lists and dicts to ZHA this way + args = arg + params = {} + else: + raise TypeError(f"Unexpected zha_send_event {command!r} argument: {arg!r}") + self._ch_pool.zha_send_event( { ATTR_UNIQUE_ID: self.unique_id, ATTR_CLUSTER_ID: self.cluster.cluster_id, ATTR_COMMAND: command, + # Maintain backwards compatibility with the old zigpy response format ATTR_ARGS: args, + ATTR_PARAMS: params, } ) diff --git a/homeassistant/components/zha/core/const.py b/homeassistant/components/zha/core/const.py index b3f06b9eba6..1e249ebd52b 100644 --- a/homeassistant/components/zha/core/const.py +++ b/homeassistant/components/zha/core/const.py @@ -43,6 +43,7 @@ ATTR_NEIGHBORS = "neighbors" ATTR_NODE_DESCRIPTOR = "node_descriptor" ATTR_NWK = "nwk" ATTR_OUT_CLUSTERS = "out_clusters" +ATTR_PARAMS = "params" ATTR_POWER_SOURCE = "power_source" ATTR_PROFILE_ID = "profile_id" ATTR_QUIRK_APPLIED = "quirk_applied" diff --git a/tests/components/zha/test_device_action.py b/tests/components/zha/test_device_action.py index 9bc52a784f6..e31f6b50fb5 100644 --- a/tests/components/zha/test_device_action.py +++ b/tests/components/zha/test_device_action.py @@ -140,3 +140,13 @@ async def test_action(hass, device_ias): assert calls[0].domain == DOMAIN assert calls[0].service == "warning_device_warn" assert calls[0].data["ieee"] == ieee_address + + +async def test_invalid_zha_event_type(hass, device_ias): + """Test that unexpected types are not passed to `zha_send_event`.""" + zigpy_device, zha_device = device_ias + channel = zha_device.channels.pools[0].client_channels["1:0x0006"] + + # `zha_send_event` accepts only zigpy responses, lists, and dicts + with pytest.raises(TypeError): + channel.zha_send_event(COMMAND_SINGLE, 123)