From ed6c86a28176624fc72d8a4d9b850c4d6755b5a6 Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Mon, 14 Mar 2022 13:55:06 +0100 Subject: [PATCH] Add siren platform to Switch as X (#68118) * Add siren platform to Switch as X * Clarify inline test documentation * Clarify inline test documentation --- .../components/switch_as_x/config_flow.py | 1 + homeassistant/components/switch_as_x/siren.py | 43 +++++++ tests/components/switch_as_x/test_init.py | 26 ++-- tests/components/switch_as_x/test_siren.py | 117 ++++++++++++++++++ 4 files changed, 180 insertions(+), 7 deletions(-) create mode 100644 homeassistant/components/switch_as_x/siren.py create mode 100644 tests/components/switch_as_x/test_siren.py diff --git a/homeassistant/components/switch_as_x/config_flow.py b/homeassistant/components/switch_as_x/config_flow.py index ed11ae25489..ff5c60274ef 100644 --- a/homeassistant/components/switch_as_x/config_flow.py +++ b/homeassistant/components/switch_as_x/config_flow.py @@ -24,6 +24,7 @@ CONFIG_FLOW = { "options": [ {"value": Platform.COVER, "label": "Cover"}, {"value": Platform.LIGHT, "label": "Light"}, + {"value": Platform.SIREN, "label": "Siren"}, ] } } diff --git a/homeassistant/components/switch_as_x/siren.py b/homeassistant/components/switch_as_x/siren.py new file mode 100644 index 00000000000..752f7fd76ad --- /dev/null +++ b/homeassistant/components/switch_as_x/siren.py @@ -0,0 +1,43 @@ +"""Siren support for switch entities.""" +from __future__ import annotations + +from homeassistant.components.siren import SirenEntity +from homeassistant.components.siren.const import SUPPORT_TURN_OFF, SUPPORT_TURN_ON +from homeassistant.config_entries import ConfigEntry +from homeassistant.const import CONF_ENTITY_ID +from homeassistant.core import HomeAssistant +from homeassistant.helpers import entity_registry as er +from homeassistant.helpers.entity_platform import AddEntitiesCallback + +from .entity import BaseToggleEntity + + +async def async_setup_entry( + hass: HomeAssistant, + config_entry: ConfigEntry, + async_add_entities: AddEntitiesCallback, +) -> None: + """Initialize Siren Switch config entry.""" + registry = er.async_get(hass) + entity_id = er.async_validate_entity_id( + registry, config_entry.options[CONF_ENTITY_ID] + ) + wrapped_switch = registry.async_get(entity_id) + device_id = wrapped_switch.device_id if wrapped_switch else None + + async_add_entities( + [ + SirenSwitch( + config_entry.title, + entity_id, + config_entry.entry_id, + device_id, + ) + ] + ) + + +class SirenSwitch(BaseToggleEntity, SirenEntity): + """Represents a Switch as a Siren.""" + + _attr_supported_features = SUPPORT_TURN_ON | SUPPORT_TURN_OFF diff --git a/tests/components/switch_as_x/test_init.py b/tests/components/switch_as_x/test_init.py index ca87673dfba..a8145fe201a 100644 --- a/tests/components/switch_as_x/test_init.py +++ b/tests/components/switch_as_x/test_init.py @@ -11,7 +11,9 @@ from homeassistant.helpers import device_registry as dr, entity_registry as er from tests.common import MockConfigEntry -@pytest.mark.parametrize("target_domain", (Platform.LIGHT,)) +@pytest.mark.parametrize( + "target_domain", (Platform.LIGHT, Platform.COVER, Platform.SIREN) +) async def test_config_entry_unregistered_uuid( hass: HomeAssistant, target_domain: str ) -> None: @@ -36,7 +38,7 @@ async def test_config_entry_unregistered_uuid( assert len(hass.states.async_all()) == 0 -@pytest.mark.parametrize("target_domain", (Platform.LIGHT,)) +@pytest.mark.parametrize("target_domain", (Platform.LIGHT, Platform.SIREN)) async def test_entity_registry_events(hass: HomeAssistant, target_domain: str) -> None: """Test entity registry events are tracked.""" registry = er.async_get(hass) @@ -93,7 +95,9 @@ async def test_entity_registry_events(hass: HomeAssistant, target_domain: str) - assert len(hass.config_entries.async_entries("switch_as_x")) == 0 -@pytest.mark.parametrize("target_domain", (Platform.LIGHT,)) +@pytest.mark.parametrize( + "target_domain", (Platform.LIGHT, Platform.COVER, Platform.SIREN) +) async def test_device_registry_config_entry_1( hass: HomeAssistant, target_domain: str ) -> None: @@ -151,7 +155,9 @@ async def test_device_registry_config_entry_1( assert switch_as_x_config_entry.entry_id not in device_entry.config_entries -@pytest.mark.parametrize("target_domain", (Platform.LIGHT,)) +@pytest.mark.parametrize( + "target_domain", (Platform.LIGHT, Platform.COVER, Platform.SIREN) +) async def test_device_registry_config_entry_2( hass: HomeAssistant, target_domain: str ) -> None: @@ -202,7 +208,9 @@ async def test_device_registry_config_entry_2( assert switch_as_x_config_entry.entry_id not in device_entry.config_entries -@pytest.mark.parametrize("target_domain", (Platform.LIGHT, Platform.COVER)) +@pytest.mark.parametrize( + "target_domain", (Platform.LIGHT, Platform.COVER, Platform.SIREN) +) async def test_config_entry_entity_id( hass: HomeAssistant, target_domain: Platform ) -> None: @@ -237,7 +245,9 @@ async def test_config_entry_entity_id( assert entity_entry.unique_id == config_entry.entry_id -@pytest.mark.parametrize("target_domain", (Platform.LIGHT, Platform.COVER)) +@pytest.mark.parametrize( + "target_domain", (Platform.LIGHT, Platform.COVER, Platform.SIREN) +) async def test_config_entry_uuid(hass: HomeAssistant, target_domain: Platform) -> None: """Test light switch setup from config entry with entity registry id.""" registry = er.async_get(hass) @@ -261,7 +271,9 @@ async def test_config_entry_uuid(hass: HomeAssistant, target_domain: Platform) - assert hass.states.get(f"{target_domain}.abc") -@pytest.mark.parametrize("target_domain", (Platform.LIGHT, Platform.COVER)) +@pytest.mark.parametrize( + "target_domain", (Platform.LIGHT, Platform.COVER, Platform.SIREN) +) async def test_device(hass: HomeAssistant, target_domain: Platform) -> None: """Test the entity is added to the wrapped entity's device.""" device_registry = dr.async_get(hass) diff --git a/tests/components/switch_as_x/test_siren.py b/tests/components/switch_as_x/test_siren.py new file mode 100644 index 00000000000..2b3dedf6fb8 --- /dev/null +++ b/tests/components/switch_as_x/test_siren.py @@ -0,0 +1,117 @@ +"""Tests for the Switch as X Siren platform.""" +from homeassistant.components.siren import DOMAIN as SIREN_DOMAIN +from homeassistant.components.switch import DOMAIN as SWITCH_DOMAIN +from homeassistant.components.switch_as_x.const import CONF_TARGET_DOMAIN, DOMAIN +from homeassistant.const import ( + CONF_ENTITY_ID, + SERVICE_TOGGLE, + SERVICE_TURN_OFF, + SERVICE_TURN_ON, + STATE_OFF, + STATE_ON, + Platform, +) +from homeassistant.core import HomeAssistant +from homeassistant.setup import async_setup_component + +from tests.common import MockConfigEntry + + +async def test_default_state(hass: HomeAssistant) -> None: + """Test siren switch default state.""" + config_entry = MockConfigEntry( + data={}, + domain=DOMAIN, + options={ + CONF_ENTITY_ID: "switch.test", + CONF_TARGET_DOMAIN: Platform.SIREN, + }, + title="Noise Maker", + ) + config_entry.add_to_hass(hass) + assert await hass.config_entries.async_setup(config_entry.entry_id) + await hass.async_block_till_done() + + state = hass.states.get("siren.noise_maker") + assert state is not None + assert state.state == "unavailable" + assert state.attributes["supported_features"] == 3 + + +async def test_service_calls(hass: HomeAssistant) -> None: + """Test service calls affecting the switch as siren entity.""" + await async_setup_component(hass, "switch", {"switch": [{"platform": "demo"}]}) + config_entry = MockConfigEntry( + data={}, + domain=DOMAIN, + options={ + CONF_ENTITY_ID: "switch.decorative_lights", + CONF_TARGET_DOMAIN: Platform.SIREN, + }, + title="noise_maker", + ) + config_entry.add_to_hass(hass) + assert await hass.config_entries.async_setup(config_entry.entry_id) + await hass.async_block_till_done() + + assert hass.states.get("siren.noise_maker").state == STATE_ON + + await hass.services.async_call( + SIREN_DOMAIN, + SERVICE_TOGGLE, + {CONF_ENTITY_ID: "siren.noise_maker"}, + blocking=True, + ) + + assert hass.states.get("switch.decorative_lights").state == STATE_OFF + assert hass.states.get("siren.noise_maker").state == STATE_OFF + + await hass.services.async_call( + SIREN_DOMAIN, + SERVICE_TURN_ON, + {CONF_ENTITY_ID: "siren.noise_maker"}, + blocking=True, + ) + + assert hass.states.get("switch.decorative_lights").state == STATE_ON + assert hass.states.get("siren.noise_maker").state == STATE_ON + + await hass.services.async_call( + SIREN_DOMAIN, + SERVICE_TURN_OFF, + {CONF_ENTITY_ID: "siren.noise_maker"}, + blocking=True, + ) + + assert hass.states.get("switch.decorative_lights").state == STATE_OFF + assert hass.states.get("siren.noise_maker").state == STATE_OFF + + await hass.services.async_call( + SWITCH_DOMAIN, + SERVICE_TURN_ON, + {CONF_ENTITY_ID: "switch.decorative_lights"}, + blocking=True, + ) + + assert hass.states.get("switch.decorative_lights").state == STATE_ON + assert hass.states.get("siren.noise_maker").state == STATE_ON + + await hass.services.async_call( + SWITCH_DOMAIN, + SERVICE_TURN_OFF, + {CONF_ENTITY_ID: "switch.decorative_lights"}, + blocking=True, + ) + + assert hass.states.get("switch.decorative_lights").state == STATE_OFF + assert hass.states.get("siren.noise_maker").state == STATE_OFF + + await hass.services.async_call( + SWITCH_DOMAIN, + SERVICE_TOGGLE, + {CONF_ENTITY_ID: "switch.decorative_lights"}, + blocking=True, + ) + + assert hass.states.get("switch.decorative_lights").state == STATE_ON + assert hass.states.get("siren.noise_maker").state == STATE_ON