1
0
mirror of https://github.com/home-assistant/core.git synced 2026-08-06 13:26:29 +01:00
Files
core/tests/components/sonos/test_button.py
T

146 lines
4.5 KiB
Python

"""Tests for the Sonos button platform."""
from typing import Any
from unittest.mock import AsyncMock
import pytest
from sonos_websocket import CLIP_ID_KEY
from sonos_websocket.exception import SonosWebsocketError
from homeassistant.components.button import DOMAIN as BUTTON_DOMAIN, SERVICE_PRESS
from homeassistant.components.media_player import (
ATTR_MEDIA_ANNOUNCE,
ATTR_MEDIA_CONTENT_ID,
ATTR_MEDIA_CONTENT_TYPE,
DOMAIN as MP_DOMAIN,
SERVICE_PLAY_MEDIA,
)
from homeassistant.const import ATTR_ENTITY_ID
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError, ServiceValidationError
CANCEL_ANNOUNCEMENT_BUTTON = "button.zone_a_cancel_announcement"
pytestmark = pytest.mark.usefixtures("entity_registry_enabled_by_default")
async def _announce_clip(hass: HomeAssistant, content_id: str) -> None:
"""Play an announcement clip to set the active clip id."""
await hass.services.async_call(
MP_DOMAIN,
SERVICE_PLAY_MEDIA,
{
ATTR_ENTITY_ID: "media_player.zone_a",
ATTR_MEDIA_CONTENT_TYPE: "music",
ATTR_MEDIA_CONTENT_ID: content_id,
ATTR_MEDIA_ANNOUNCE: True,
},
blocking=True,
)
async def test_cancel_announcement_no_prior(
hass: HomeAssistant,
async_autosetup_sonos,
) -> None:
"""Test cancelling when no announcement has been played."""
with pytest.raises(
ServiceValidationError, match="No active announcement to cancel"
):
await hass.services.async_call(
BUTTON_DOMAIN,
SERVICE_PRESS,
{ATTR_ENTITY_ID: CANCEL_ANNOUNCEMENT_BUTTON},
blocking=True,
)
async def test_cancel_announcement(
hass: HomeAssistant,
async_autosetup_sonos,
sonos_websocket,
) -> None:
"""Test cancelling a currently playing announcement."""
content_id = "http://10.0.0.1:8123/local/sounds/doorbell.mp3"
sonos_websocket.play_clip.return_value = [
{"success": 1},
{CLIP_ID_KEY: "clip-123"},
]
await _announce_clip(hass, content_id)
sonos_websocket.cancel_clip = AsyncMock(return_value=[{"success": 1}, {}])
await hass.services.async_call(
BUTTON_DOMAIN,
SERVICE_PRESS,
{ATTR_ENTITY_ID: CANCEL_ANNOUNCEMENT_BUTTON},
blocking=True,
)
sonos_websocket.cancel_clip.assert_called_once_with("clip-123")
async def test_cancel_announcement_no_clip_id_from_announce_response(
hass: HomeAssistant,
async_autosetup_sonos,
sonos_websocket,
) -> None:
"""Test cancelling fails when the announce response has no clip ID."""
content_id = "http://10.0.0.1:8123/local/sounds/doorbell.mp3"
sonos_websocket.play_clip.return_value = [{"success": 1}, None]
await _announce_clip(hass, content_id)
with pytest.raises(
ServiceValidationError, match="No active announcement to cancel"
):
await hass.services.async_call(
BUTTON_DOMAIN,
SERVICE_PRESS,
{ATTR_ENTITY_ID: CANCEL_ANNOUNCEMENT_BUTTON},
blocking=True,
)
@pytest.mark.parametrize(
("cancel_clip_side_effect", "cancel_clip_return", "error_match"),
[
pytest.param(
SonosWebsocketError("Connection lost"),
None,
"Failed to reach Sonos speaker for announcement: Connection lost",
id="websocket_error",
),
pytest.param(
None,
[{"success": 0}, {}],
"Cancelling announcement failed",
id="non_success_response",
),
],
)
async def test_cancel_announcement_errors(
hass: HomeAssistant,
async_autosetup_sonos,
sonos_websocket,
cancel_clip_side_effect: SonosWebsocketError | None,
cancel_clip_return: list[dict[str, Any]] | None,
error_match: str,
) -> None:
"""Test error handling when cancelling an announcement."""
content_id = "http://10.0.0.1:8123/local/sounds/doorbell.mp3"
sonos_websocket.play_clip.return_value = [
{"success": 1},
{CLIP_ID_KEY: "clip-123"},
]
await _announce_clip(hass, content_id)
sonos_websocket.cancel_clip = AsyncMock(
side_effect=cancel_clip_side_effect,
return_value=cancel_clip_return,
)
with pytest.raises(HomeAssistantError, match=error_match):
await hass.services.async_call(
BUTTON_DOMAIN,
SERVICE_PRESS,
{ATTR_ENTITY_ID: CANCEL_ANNOUNCEMENT_BUTTON},
blocking=True,
)