From 945eba9aa384f2348e082ddb981f897a9465040f Mon Sep 17 00:00:00 2001 From: Graham Arthur Blair Date: Tue, 10 May 2022 13:49:38 -0700 Subject: [PATCH] Change Ring Chime play sound Buttons to a Siren (#71449) --- homeassistant/components/ring/__init__.py | 2 +- homeassistant/components/ring/button.py | 65 ----------- homeassistant/components/ring/siren.py | 51 +++++++++ tests/components/ring/test_button.py | 55 --------- tests/components/ring/test_siren.py | 132 ++++++++++++++++++++++ 5 files changed, 184 insertions(+), 121 deletions(-) delete mode 100644 homeassistant/components/ring/button.py create mode 100644 homeassistant/components/ring/siren.py delete mode 100644 tests/components/ring/test_button.py create mode 100644 tests/components/ring/test_siren.py diff --git a/homeassistant/components/ring/__init__.py b/homeassistant/components/ring/__init__.py index 2a922a7bb3e..a1ed1ac017b 100644 --- a/homeassistant/components/ring/__init__.py +++ b/homeassistant/components/ring/__init__.py @@ -32,11 +32,11 @@ DEFAULT_ENTITY_NAMESPACE = "ring" PLATFORMS = [ Platform.BINARY_SENSOR, - Platform.BUTTON, Platform.LIGHT, Platform.SENSOR, Platform.SWITCH, Platform.CAMERA, + Platform.SIREN, ] diff --git a/homeassistant/components/ring/button.py b/homeassistant/components/ring/button.py deleted file mode 100644 index 04f9f7950f8..00000000000 --- a/homeassistant/components/ring/button.py +++ /dev/null @@ -1,65 +0,0 @@ -"""This component provides HA button support for Ring Chimes.""" -import logging - -from homeassistant.components.button import ButtonEntity -from homeassistant.config_entries import ConfigEntry -from homeassistant.core import HomeAssistant -from homeassistant.helpers.entity_platform import AddEntitiesCallback - -from . import DOMAIN -from .entity import RingEntityMixin - -_LOGGER = logging.getLogger(__name__) - -BELL_ICON = "mdi:bell-ring" - - -async def async_setup_entry( - hass: HomeAssistant, - config_entry: ConfigEntry, - async_add_entities: AddEntitiesCallback, -) -> None: - """Create the buttons for the Ring devices.""" - devices = hass.data[DOMAIN][config_entry.entry_id]["devices"] - buttons = [] - - # add one button for each test chime type (ding, motion) - for device in devices["chimes"]: - buttons.append(ChimeButton(config_entry.entry_id, device, "ding")) - buttons.append(ChimeButton(config_entry.entry_id, device, "motion")) - - async_add_entities(buttons) - - -class BaseRingButton(RingEntityMixin, ButtonEntity): - """Represents a Button for controlling an aspect of a ring device.""" - - def __init__(self, config_entry_id, device, button_identifier, button_name): - """Initialize the switch.""" - super().__init__(config_entry_id, device) - self._button_identifier = button_identifier - self._button_name = button_name - self._attr_unique_id = f"{self._device.id}-{self._button_identifier}" - - @property - def name(self): - """Name of the device.""" - return f"{self._device.name} {self._button_name}" - - -class ChimeButton(BaseRingButton): - """Creates a button to play the test chime of a Chime device.""" - - _attr_icon = BELL_ICON - - def __init__(self, config_entry_id, device, kind): - """Initialize the button for a device with a chime.""" - super().__init__( - config_entry_id, device, f"play-chime-{kind}", f"Play chime: {kind}" - ) - self.kind = kind - - def press(self) -> None: - """Send the test chime request.""" - if not self._device.test_sound(kind=self.kind): - _LOGGER.error("Failed to ring chime sound on %s", self.name) diff --git a/homeassistant/components/ring/siren.py b/homeassistant/components/ring/siren.py new file mode 100644 index 00000000000..b83d3e7b2ae --- /dev/null +++ b/homeassistant/components/ring/siren.py @@ -0,0 +1,51 @@ +"""This component provides HA Siren support for Ring Chimes.""" +import logging +from typing import Any + +from ring_doorbell.const import CHIME_TEST_SOUND_KINDS, KIND_DING + +from homeassistant.components.siren import ATTR_TONE, SirenEntity, SirenEntityFeature +from homeassistant.config_entries import ConfigEntry +from homeassistant.core import HomeAssistant +from homeassistant.helpers.entity_platform import AddEntitiesCallback + +from . import DOMAIN +from .entity import RingEntityMixin + +_LOGGER = logging.getLogger(__name__) + + +async def async_setup_entry( + hass: HomeAssistant, + config_entry: ConfigEntry, + async_add_entities: AddEntitiesCallback, +) -> None: + """Create the sirens for the Ring devices.""" + devices = hass.data[DOMAIN][config_entry.entry_id]["devices"] + sirens = [] + + for device in devices["chimes"]: + sirens.append(RingChimeSiren(config_entry, device)) + + async_add_entities(sirens) + + +class RingChimeSiren(RingEntityMixin, SirenEntity): + """Creates a siren to play the test chimes of a Chime device.""" + + def __init__(self, config_entry: ConfigEntry, device) -> None: + """Initialize a Ring Chime siren.""" + super().__init__(config_entry.entry_id, device) + # Entity class attributes + self._attr_name = f"{self._device.name} Siren" + self._attr_unique_id = f"{self._device.id}-siren" + self._attr_available_tones = CHIME_TEST_SOUND_KINDS + self._attr_supported_features = ( + SirenEntityFeature.TURN_ON | SirenEntityFeature.TONES + ) + + def turn_on(self, **kwargs: Any) -> None: + """Play the test sound on a Ring Chime device.""" + tone = kwargs.get(ATTR_TONE) or KIND_DING + + self._device.test_sound(kind=tone) diff --git a/tests/components/ring/test_button.py b/tests/components/ring/test_button.py deleted file mode 100644 index 62b2fcd8a78..00000000000 --- a/tests/components/ring/test_button.py +++ /dev/null @@ -1,55 +0,0 @@ -"""The tests for the Ring button platform.""" - -from homeassistant.const import Platform -from homeassistant.helpers import entity_registry as er - -from .common import setup_platform - - -async def test_entity_registry(hass, requests_mock): - """Tests that the devices are registered in the entity registry.""" - await setup_platform(hass, Platform.BUTTON) - entity_registry = er.async_get(hass) - - entry = entity_registry.async_get("button.downstairs_play_chime_ding") - assert entry.unique_id == "123456-play-chime-ding" - - entry = entity_registry.async_get("button.downstairs_play_chime_motion") - assert entry.unique_id == "123456-play-chime-motion" - - -async def test_play_chime_buttons_report_correctly(hass, requests_mock): - """Tests that the initial state of a device that should be on is correct.""" - await setup_platform(hass, Platform.BUTTON) - - state = hass.states.get("button.downstairs_play_chime_ding") - assert state.attributes.get("friendly_name") == "Downstairs Play chime: ding" - assert state.attributes.get("icon") == "mdi:bell-ring" - - state = hass.states.get("button.downstairs_play_chime_motion") - assert state.attributes.get("friendly_name") == "Downstairs Play chime: motion" - assert state.attributes.get("icon") == "mdi:bell-ring" - - -async def test_chime_can_be_played(hass, requests_mock): - """Tests the play chime request is sent correctly.""" - await setup_platform(hass, Platform.BUTTON) - - # Mocks the response for playing a test sound - requests_mock.post( - "https://api.ring.com/clients_api/chimes/123456/play_sound", - text="SUCCESS", - ) - await hass.services.async_call( - "button", - "press", - {"entity_id": "button.downstairs_play_chime_ding"}, - blocking=True, - ) - - await hass.async_block_till_done() - - assert requests_mock.request_history[-1].url.startswith( - "https://api.ring.com/clients_api/chimes/123456/play_sound?" - ) - assert "kind=ding" in requests_mock.request_history[-1].url diff --git a/tests/components/ring/test_siren.py b/tests/components/ring/test_siren.py new file mode 100644 index 00000000000..bb282769e57 --- /dev/null +++ b/tests/components/ring/test_siren.py @@ -0,0 +1,132 @@ +"""The tests for the Ring button platform.""" + +from homeassistant.const import Platform +from homeassistant.helpers import entity_registry as er + +from .common import setup_platform + + +async def test_entity_registry(hass, requests_mock): + """Tests that the devices are registered in the entity registry.""" + await setup_platform(hass, Platform.SIREN) + entity_registry = er.async_get(hass) + + entry = entity_registry.async_get("siren.downstairs_siren") + assert entry.unique_id == "123456-siren" + + +async def test_sirens_report_correctly(hass, requests_mock): + """Tests that the initial state of a device that should be on is correct.""" + await setup_platform(hass, Platform.SIREN) + + state = hass.states.get("siren.downstairs_siren") + assert state.attributes.get("friendly_name") == "Downstairs Siren" + assert state.state == "unknown" + + +async def test_default_ding_chime_can_be_played(hass, requests_mock): + """Tests the play chime request is sent correctly.""" + await setup_platform(hass, Platform.SIREN) + + # Mocks the response for playing a test sound + requests_mock.post( + "https://api.ring.com/clients_api/chimes/123456/play_sound", + text="SUCCESS", + ) + await hass.services.async_call( + "siren", + "turn_on", + {"entity_id": "siren.downstairs_siren"}, + blocking=True, + ) + + await hass.async_block_till_done() + + assert requests_mock.request_history[-1].url.startswith( + "https://api.ring.com/clients_api/chimes/123456/play_sound?" + ) + assert "kind=ding" in requests_mock.request_history[-1].url + + state = hass.states.get("siren.downstairs_siren") + assert state.state == "unknown" + + +async def test_toggle_plays_default_chime(hass, requests_mock): + """Tests the play chime request is sent correctly when toggled.""" + await setup_platform(hass, Platform.SIREN) + + # Mocks the response for playing a test sound + requests_mock.post( + "https://api.ring.com/clients_api/chimes/123456/play_sound", + text="SUCCESS", + ) + await hass.services.async_call( + "siren", + "toggle", + {"entity_id": "siren.downstairs_siren"}, + blocking=True, + ) + + await hass.async_block_till_done() + + assert requests_mock.request_history[-1].url.startswith( + "https://api.ring.com/clients_api/chimes/123456/play_sound?" + ) + assert "kind=ding" in requests_mock.request_history[-1].url + + state = hass.states.get("siren.downstairs_siren") + assert state.state == "unknown" + + +async def test_explicit_ding_chime_can_be_played(hass, requests_mock): + """Tests the play chime request is sent correctly.""" + await setup_platform(hass, Platform.SIREN) + + # Mocks the response for playing a test sound + requests_mock.post( + "https://api.ring.com/clients_api/chimes/123456/play_sound", + text="SUCCESS", + ) + await hass.services.async_call( + "siren", + "turn_on", + {"entity_id": "siren.downstairs_siren", "tone": "ding"}, + blocking=True, + ) + + await hass.async_block_till_done() + + assert requests_mock.request_history[-1].url.startswith( + "https://api.ring.com/clients_api/chimes/123456/play_sound?" + ) + assert "kind=ding" in requests_mock.request_history[-1].url + + state = hass.states.get("siren.downstairs_siren") + assert state.state == "unknown" + + +async def test_motion_chime_can_be_played(hass, requests_mock): + """Tests the play chime request is sent correctly.""" + await setup_platform(hass, Platform.SIREN) + + # Mocks the response for playing a test sound + requests_mock.post( + "https://api.ring.com/clients_api/chimes/123456/play_sound", + text="SUCCESS", + ) + await hass.services.async_call( + "siren", + "turn_on", + {"entity_id": "siren.downstairs_siren", "tone": "motion"}, + blocking=True, + ) + + await hass.async_block_till_done() + + assert requests_mock.request_history[-1].url.startswith( + "https://api.ring.com/clients_api/chimes/123456/play_sound?" + ) + assert "kind=motion" in requests_mock.request_history[-1].url + + state = hass.states.get("siren.downstairs_siren") + assert state.state == "unknown"