From 3183ce7608740ced18119bffc6d884d2d3712b17 Mon Sep 17 00:00:00 2001 From: Jan Bouwhuis Date: Sun, 23 Jul 2023 20:16:46 +0200 Subject: [PATCH] Add doorbell event support to alexa (#97092) --- homeassistant/components/alexa/entities.py | 21 ++++++++ .../components/alexa/state_report.py | 7 ++- tests/components/alexa/test_smart_home.py | 51 +++++++++++++++---- 3 files changed, 67 insertions(+), 12 deletions(-) diff --git a/homeassistant/components/alexa/entities.py b/homeassistant/components/alexa/entities.py index 6ed071b8b9e..9a805b43c4f 100644 --- a/homeassistant/components/alexa/entities.py +++ b/homeassistant/components/alexa/entities.py @@ -14,6 +14,7 @@ from homeassistant.components import ( camera, climate, cover, + event, fan, group, humidifier, @@ -527,6 +528,26 @@ class CoverCapabilities(AlexaEntity): yield Alexa(self.entity) +@ENTITY_ADAPTERS.register(event.DOMAIN) +class EventCapabilities(AlexaEntity): + """Class to represent doorbel event capabilities.""" + + def default_display_categories(self) -> list[str] | None: + """Return the display categories for this entity.""" + attrs = self.entity.attributes + device_class: event.EventDeviceClass | None = attrs.get(ATTR_DEVICE_CLASS) + if device_class == event.EventDeviceClass.DOORBELL: + return [DisplayCategory.DOORBELL] + return None + + def interfaces(self) -> Generator[AlexaCapability, None, None]: + """Yield the supported interfaces.""" + if self.default_display_categories() is not None: + yield AlexaDoorbellEventSource(self.entity) + yield AlexaEndpointHealth(self.hass, self.entity) + yield Alexa(self.entity) + + @ENTITY_ADAPTERS.register(light.DOMAIN) class LightCapabilities(AlexaEntity): """Class to represent Light capabilities.""" diff --git a/homeassistant/components/alexa/state_report.py b/homeassistant/components/alexa/state_report.py index ebab3bcee8c..04bb561560f 100644 --- a/homeassistant/components/alexa/state_report.py +++ b/homeassistant/components/alexa/state_report.py @@ -10,6 +10,7 @@ from typing import TYPE_CHECKING, cast import aiohttp import async_timeout +from homeassistant.components import event from homeassistant.const import MATCH_ALL, STATE_ON from homeassistant.core import HomeAssistant, State, callback from homeassistant.helpers.aiohttp_client import async_get_clientsession @@ -91,8 +92,10 @@ async def async_enable_proactive_mode(hass, smart_home_config): return if should_doorbell: - if new_state.state == STATE_ON and ( - old_state is None or old_state.state != STATE_ON + if ( + new_state.domain == event.DOMAIN + or new_state.state == STATE_ON + and (old_state is None or old_state.state != STATE_ON) ): await async_send_doorbell_event_message( hass, smart_home_config, alexa_changed_entity diff --git a/tests/components/alexa/test_smart_home.py b/tests/components/alexa/test_smart_home.py index a2dcdedd470..477e7884e4f 100644 --- a/tests/components/alexa/test_smart_home.py +++ b/tests/components/alexa/test_smart_home.py @@ -1,4 +1,5 @@ """Test for smart home alexa support.""" +from typing import Any from unittest.mock import patch import pytest @@ -2136,18 +2137,48 @@ async def test_forced_motion_sensor(hass: HomeAssistant) -> None: properties.assert_equal("Alexa.EndpointHealth", "connectivity", {"value": "OK"}) -async def test_doorbell_sensor(hass: HomeAssistant) -> None: - """Test doorbell sensor discovery.""" - device = ( - "binary_sensor.test_doorbell", - "off", - {"friendly_name": "Test Doorbell Sensor", "device_class": "occupancy"}, - ) +@pytest.mark.parametrize( + ("device", "endpoint_id", "friendly_name", "display_category"), + [ + ( + ( + "binary_sensor.test_doorbell", + "off", + {"friendly_name": "Test Doorbell Sensor", "device_class": "occupancy"}, + ), + "binary_sensor#test_doorbell", + "Test Doorbell Sensor", + "DOORBELL", + ), + ( + ( + "event.test_doorbell", + None, + { + "friendly_name": "Test Doorbell Event", + "event_types": ["press"], + "device_class": "doorbell", + }, + ), + "event#test_doorbell", + "Test Doorbell Event", + "DOORBELL", + ), + ], +) +async def test_doorbell_event( + hass: HomeAssistant, + device: tuple[str, str, dict[str, Any]], + endpoint_id: str, + friendly_name: str, + display_category: str, +) -> None: + """Test doorbell event/sensor discovery.""" appliance = await discovery_test(device, hass) - assert appliance["endpointId"] == "binary_sensor#test_doorbell" - assert appliance["displayCategories"][0] == "DOORBELL" - assert appliance["friendlyName"] == "Test Doorbell Sensor" + assert appliance["endpointId"] == endpoint_id + assert appliance["displayCategories"][0] == display_category + assert appliance["friendlyName"] == friendly_name capabilities = assert_endpoint_capabilities( appliance, "Alexa.DoorbellEventSource", "Alexa.EndpointHealth", "Alexa"