From e416f17e4d31269086d3a839e399c005030b65c8 Mon Sep 17 00:00:00 2001 From: Alexei Chetroi Date: Wed, 4 Mar 2020 19:27:37 -0500 Subject: [PATCH] ZHA: Successful pairing feedback (#32456) --- homeassistant/components/zha/core/const.py | 6 ++++ homeassistant/components/zha/core/device.py | 7 +++++ homeassistant/components/zha/light.py | 7 +++-- tests/components/zha/common.py | 1 + tests/components/zha/conftest.py | 4 ++- tests/components/zha/test_discover.py | 34 +++++++++++++++++++++ 6 files changed, 56 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/zha/core/const.py b/homeassistant/components/zha/core/const.py index 3204fa76e2a..4b5a5a0c6a1 100644 --- a/homeassistant/components/zha/core/const.py +++ b/homeassistant/components/zha/core/const.py @@ -251,3 +251,9 @@ ZHA_GW_MSG_LOG_OUTPUT = "log_output" ZHA_GW_MSG_RAW_INIT = "raw_device_initialized" ZHA_GW_RADIO = "radio" ZHA_GW_RADIO_DESCRIPTION = "radio_description" + +EFFECT_BLINK = 0x00 +EFFECT_BREATHE = 0x01 +EFFECT_OKAY = 0x02 + +EFFECT_DEFAULT_VARIANT = 0x00 diff --git a/homeassistant/components/zha/core/device.py b/homeassistant/components/zha/core/device.py index 94eb16fc417..76685180ea2 100644 --- a/homeassistant/components/zha/core/device.py +++ b/homeassistant/components/zha/core/device.py @@ -48,6 +48,8 @@ from .const import ( CLUSTER_COMMANDS_SERVER, CLUSTER_TYPE_IN, CLUSTER_TYPE_OUT, + EFFECT_DEFAULT_VARIANT, + EFFECT_OKAY, POWER_BATTERY_OR_UNKNOWN, POWER_MAINS_POWERED, SIGNAL_AVAILABLE, @@ -342,6 +344,11 @@ class ZHADevice(LogMixin): entry = self.gateway.zha_storage.async_create_or_update(self) self.debug("stored in registry: %s", entry) + if self._channels.identify_ch is not None: + await self._channels.identify_ch.trigger_effect( + EFFECT_OKAY, EFFECT_DEFAULT_VARIANT + ) + async def async_initialize(self, from_cache=False): """Initialize channels.""" self.debug("started initialization") diff --git a/homeassistant/components/zha/light.py b/homeassistant/components/zha/light.py index 68032001816..634bf50001e 100644 --- a/homeassistant/components/zha/light.py +++ b/homeassistant/components/zha/light.py @@ -19,6 +19,9 @@ from .core.const import ( CHANNEL_ON_OFF, DATA_ZHA, DATA_ZHA_DISPATCHERS, + EFFECT_BLINK, + EFFECT_BREATHE, + EFFECT_DEFAULT_VARIANT, SIGNAL_ADD_ENTITIES, SIGNAL_ATTR_UPDATED, SIGNAL_SET_LEVEL, @@ -38,7 +41,7 @@ UPDATE_COLORLOOP_DIRECTION = 0x2 UPDATE_COLORLOOP_TIME = 0x4 UPDATE_COLORLOOP_HUE = 0x8 -FLASH_EFFECTS = {light.FLASH_SHORT: 0x00, light.FLASH_LONG: 0x01} +FLASH_EFFECTS = {light.FLASH_SHORT: EFFECT_BLINK, light.FLASH_LONG: EFFECT_BREATHE} UNSUPPORTED_ATTRIBUTE = 0x86 SCAN_INTERVAL = timedelta(minutes=60) @@ -287,7 +290,7 @@ class Light(ZhaEntity, light.Light): if flash is not None and self._supported_features & light.SUPPORT_FLASH: result = await self._identify_channel.trigger_effect( - FLASH_EFFECTS[flash], 0 # effect identifier, effect variant + FLASH_EFFECTS[flash], EFFECT_DEFAULT_VARIANT ) t_log["trigger_effect"] = result diff --git a/tests/components/zha/common.py b/tests/components/zha/common.py index dfa0c455649..8e99a51f1f9 100644 --- a/tests/components/zha/common.py +++ b/tests/components/zha/common.py @@ -29,6 +29,7 @@ class FakeEndpoint: self.model = model self.profile_id = zigpy.profiles.zha.PROFILE_ID self.device_type = None + self.request = CoroutineMock() def add_input_cluster(self, cluster_id): """Add an input cluster.""" diff --git a/tests/components/zha/conftest.py b/tests/components/zha/conftest.py index e3a8f6bf4dc..53ffb121291 100644 --- a/tests/components/zha/conftest.py +++ b/tests/components/zha/conftest.py @@ -166,7 +166,9 @@ def zha_device_restored(hass, zigpy_app_controller, setup_zha): @pytest.fixture(params=["zha_device_joined", "zha_device_restored"]) def zha_device_joined_restored(request): """Join or restore ZHA device.""" - return request.getfixturevalue(request.param) + named_method = request.getfixturevalue(request.param) + named_method.name = request.param + return named_method @pytest.fixture diff --git a/tests/components/zha/test_discover.py b/tests/components/zha/test_discover.py index c8f2eb0dd7c..9515de32fcd 100644 --- a/tests/components/zha/test_discover.py +++ b/tests/components/zha/test_discover.py @@ -3,11 +3,14 @@ import re from unittest import mock +import asynctest import pytest import zigpy.quirks +import zigpy.types import zigpy.zcl.clusters.closures import zigpy.zcl.clusters.general import zigpy.zcl.clusters.security +import zigpy.zcl.foundation as zcl_f import homeassistant.components.zha.binary_sensor import homeassistant.components.zha.core.channels as zha_channels @@ -48,6 +51,12 @@ def channels_mock(zha_device_mock): return _mock +@asynctest.patch( + "zigpy.zcl.clusters.general.Identify.request", + new=asynctest.CoroutineMock( + return_value=[mock.sentinel.data, zcl_f.Status.SUCCESS] + ), +) @pytest.mark.parametrize("device", DEVICES) async def test_devices( device, hass, zigpy_device_mock, monkeypatch, zha_device_joined_restored @@ -66,6 +75,10 @@ async def test_devices( node_descriptor=device["node_descriptor"], ) + cluster_identify = _get_first_identify_cluster(zigpy_device) + if cluster_identify: + cluster_identify.request.reset_mock() + orig_new_entity = zha_channels.ChannelPool.async_new_entity _dispatch = mock.MagicMock(wraps=orig_new_entity) try: @@ -81,6 +94,21 @@ async def test_devices( ent for ent in entity_ids if ent.split(".")[0] in zha_const.COMPONENTS } + if cluster_identify: + called = int(zha_device_joined_restored.name == "zha_device_joined") + assert cluster_identify.request.call_count == called + assert cluster_identify.request.await_count == called + if called: + assert cluster_identify.request.call_args == mock.call( + False, + 64, + (zigpy.types.uint8_t, zigpy.types.uint8_t), + 2, + 0, + expect_reply=True, + manufacturer=None, + ) + event_channels = { ch.id for pool in zha_dev.channels.pools for ch in pool.relay_channels.values() } @@ -108,6 +136,12 @@ async def test_devices( assert entity_cls.__name__ == entity_map[key]["entity_class"] +def _get_first_identify_cluster(zigpy_device): + for endpoint in list(zigpy_device.endpoints.values())[1:]: + if hasattr(endpoint, "identify"): + return endpoint.identify + + @mock.patch( "homeassistant.components.zha.core.discovery.ProbeEndpoint.discover_by_device_type" )