Change Ring Chime play sound Buttons to a Siren (#71449)
This commit is contained in:
parent
2205898771
commit
945eba9aa3
5 changed files with 184 additions and 121 deletions
|
@ -32,11 +32,11 @@ DEFAULT_ENTITY_NAMESPACE = "ring"
|
|||
|
||||
PLATFORMS = [
|
||||
Platform.BINARY_SENSOR,
|
||||
Platform.BUTTON,
|
||||
Platform.LIGHT,
|
||||
Platform.SENSOR,
|
||||
Platform.SWITCH,
|
||||
Platform.CAMERA,
|
||||
Platform.SIREN,
|
||||
]
|
||||
|
||||
|
||||
|
|
|
@ -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)
|
51
homeassistant/components/ring/siren.py
Normal file
51
homeassistant/components/ring/siren.py
Normal file
|
@ -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)
|
|
@ -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
|
132
tests/components/ring/test_siren.py
Normal file
132
tests/components/ring/test_siren.py
Normal file
|
@ -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"
|
Loading…
Add table
Reference in a new issue