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 = [
|
PLATFORMS = [
|
||||||
Platform.BINARY_SENSOR,
|
Platform.BINARY_SENSOR,
|
||||||
Platform.BUTTON,
|
|
||||||
Platform.LIGHT,
|
Platform.LIGHT,
|
||||||
Platform.SENSOR,
|
Platform.SENSOR,
|
||||||
Platform.SWITCH,
|
Platform.SWITCH,
|
||||||
Platform.CAMERA,
|
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