Files
ha-core/tests/components/unifiprotect/test_media_player.py
2026-01-28 19:54:15 +01:00

310 lines
9.3 KiB
Python

"""Test the UniFi Protect media_player platform."""
from __future__ import annotations
from unittest.mock import AsyncMock, Mock, patch
import pytest
from uiprotect.data import AiPort, Camera
from uiprotect.exceptions import StreamError
from homeassistant.components.media_player import (
ATTR_MEDIA_CONTENT_TYPE,
ATTR_MEDIA_VOLUME_LEVEL,
)
from homeassistant.components.unifiprotect.const import DEFAULT_ATTRIBUTION
from homeassistant.const import (
ATTR_ATTRIBUTION,
ATTR_ENTITY_ID,
ATTR_SUPPORTED_FEATURES,
STATE_IDLE,
STATE_PLAYING,
Platform,
)
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import entity_registry as er
from . import patch_ufp_method
from .utils import (
MockUFPFixture,
adopt_devices,
assert_entity_counts,
init_entry,
remove_entities,
)
async def test_media_player_camera_remove(
hass: HomeAssistant, ufp: MockUFPFixture, doorbell: Camera
) -> None:
"""Test removing and re-adding a light device."""
await init_entry(hass, ufp, [doorbell])
assert_entity_counts(hass, Platform.MEDIA_PLAYER, 1, 1)
await remove_entities(hass, ufp, [doorbell])
assert_entity_counts(hass, Platform.MEDIA_PLAYER, 0, 0)
await adopt_devices(hass, ufp, [doorbell])
assert_entity_counts(hass, Platform.MEDIA_PLAYER, 1, 1)
async def test_media_player_setup(
hass: HomeAssistant,
entity_registry: er.EntityRegistry,
ufp: MockUFPFixture,
doorbell: Camera,
unadopted_camera: Camera,
) -> None:
"""Test media_player entity setup."""
await init_entry(hass, ufp, [doorbell, unadopted_camera])
assert_entity_counts(hass, Platform.MEDIA_PLAYER, 1, 1)
unique_id = f"{doorbell.mac}_speaker"
entity_id = "media_player.test_camera_speaker"
entity = entity_registry.async_get(entity_id)
assert entity
assert entity.unique_id == unique_id
expected_volume = float(doorbell.speaker_settings.speaker_volume / 100)
state = hass.states.get(entity_id)
assert state
assert state.state == STATE_IDLE
assert state.attributes[ATTR_ATTRIBUTION] == DEFAULT_ATTRIBUTION
assert state.attributes[ATTR_SUPPORTED_FEATURES] == 136708
assert state.attributes[ATTR_MEDIA_CONTENT_TYPE] == "music"
assert state.attributes[ATTR_MEDIA_VOLUME_LEVEL] == expected_volume
async def test_media_player_update(
hass: HomeAssistant,
ufp: MockUFPFixture,
doorbell: Camera,
unadopted_camera: Camera,
) -> None:
"""Test media_player entity update."""
await init_entry(hass, ufp, [doorbell, unadopted_camera])
assert_entity_counts(hass, Platform.MEDIA_PLAYER, 1, 1)
new_camera = doorbell.model_copy()
new_camera.talkback_stream = Mock()
new_camera.talkback_stream.is_running = True
mock_msg = Mock()
mock_msg.changed_data = {}
mock_msg.new_obj = new_camera
ufp.api.bootstrap.cameras = {new_camera.id: new_camera}
ufp.ws_msg(mock_msg)
await hass.async_block_till_done()
state = hass.states.get("media_player.test_camera_speaker")
assert state
assert state.state == STATE_PLAYING
async def test_media_player_set_volume(
hass: HomeAssistant,
ufp: MockUFPFixture,
doorbell: Camera,
unadopted_camera: Camera,
) -> None:
"""Test media_player entity test set_volume_level."""
await init_entry(hass, ufp, [doorbell, unadopted_camera])
assert_entity_counts(hass, Platform.MEDIA_PLAYER, 1, 1)
with patch_ufp_method(
doorbell, "set_speaker_volume", new_callable=AsyncMock
) as mock_method:
await hass.services.async_call(
"media_player",
"volume_set",
{ATTR_ENTITY_ID: "media_player.test_camera_speaker", "volume_level": 0.5},
blocking=True,
)
mock_method.assert_called_once_with(50)
async def test_media_player_stop(
hass: HomeAssistant,
ufp: MockUFPFixture,
doorbell: Camera,
unadopted_camera: Camera,
) -> None:
"""Test media_player entity test media_stop."""
await init_entry(hass, ufp, [doorbell, unadopted_camera])
assert_entity_counts(hass, Platform.MEDIA_PLAYER, 1, 1)
new_camera = doorbell.model_copy()
new_camera.talkback_stream = AsyncMock()
new_camera.talkback_stream.is_running = True
mock_msg = Mock()
mock_msg.changed_data = {}
mock_msg.new_obj = new_camera
ufp.api.bootstrap.cameras = {new_camera.id: new_camera}
ufp.ws_msg(mock_msg)
await hass.async_block_till_done()
await hass.services.async_call(
"media_player",
"media_stop",
{ATTR_ENTITY_ID: "media_player.test_camera_speaker"},
blocking=True,
)
new_camera.talkback_stream.stop.assert_called_once()
async def test_media_player_play(
hass: HomeAssistant,
ufp: MockUFPFixture,
doorbell: Camera,
unadopted_camera: Camera,
) -> None:
"""Test media_player entity test play_media."""
await init_entry(hass, ufp, [doorbell, unadopted_camera])
assert_entity_counts(hass, Platform.MEDIA_PLAYER, 1, 1)
with (
patch_ufp_method(doorbell, "stop_audio", new_callable=AsyncMock),
patch_ufp_method(doorbell, "play_audio", new_callable=AsyncMock) as mock_play,
patch_ufp_method(
doorbell, "wait_until_audio_completes", new_callable=AsyncMock
) as mock_wait,
):
await hass.services.async_call(
"media_player",
"play_media",
{
ATTR_ENTITY_ID: "media_player.test_camera_speaker",
"media_content_id": "http://example.com/test.mp3",
"media_content_type": "music",
},
blocking=True,
)
mock_play.assert_called_once_with("http://example.com/test.mp3", blocking=False)
mock_wait.assert_called_once()
async def test_media_player_play_media_source(
hass: HomeAssistant,
ufp: MockUFPFixture,
doorbell: Camera,
unadopted_camera: Camera,
) -> None:
"""Test media_player entity test play_media."""
await init_entry(hass, ufp, [doorbell, unadopted_camera])
assert_entity_counts(hass, Platform.MEDIA_PLAYER, 1, 1)
with (
patch_ufp_method(doorbell, "stop_audio", new_callable=AsyncMock),
patch_ufp_method(doorbell, "play_audio", new_callable=AsyncMock) as mock_play,
patch_ufp_method(
doorbell, "wait_until_audio_completes", new_callable=AsyncMock
) as mock_wait,
patch(
"homeassistant.components.media_source.async_resolve_media",
return_value=Mock(url="http://example.com/test.mp3"),
),
):
await hass.services.async_call(
"media_player",
"play_media",
{
ATTR_ENTITY_ID: "media_player.test_camera_speaker",
"media_content_id": "media-source://some_source/some_id",
"media_content_type": "audio/mpeg",
},
blocking=True,
)
mock_play.assert_called_once_with("http://example.com/test.mp3", blocking=False)
mock_wait.assert_called_once()
async def test_media_player_play_invalid(
hass: HomeAssistant,
ufp: MockUFPFixture,
doorbell: Camera,
unadopted_camera: Camera,
) -> None:
"""Test media_player entity test play_media, not music."""
await init_entry(hass, ufp, [doorbell, unadopted_camera])
assert_entity_counts(hass, Platform.MEDIA_PLAYER, 1, 1)
with patch_ufp_method(
doorbell, "play_audio", new_callable=AsyncMock
) as mock_method:
with pytest.raises(HomeAssistantError):
await hass.services.async_call(
"media_player",
"play_media",
{
ATTR_ENTITY_ID: "media_player.test_camera_speaker",
"media_content_id": "/test.png",
"media_content_type": "image",
},
blocking=True,
)
assert not mock_method.called
async def test_media_player_play_error(
hass: HomeAssistant,
ufp: MockUFPFixture,
doorbell: Camera,
unadopted_camera: Camera,
) -> None:
"""Test media_player entity test play_media, not music."""
await init_entry(hass, ufp, [doorbell, unadopted_camera])
assert_entity_counts(hass, Platform.MEDIA_PLAYER, 1, 1)
with (
patch_ufp_method(
doorbell, "play_audio", new_callable=AsyncMock, side_effect=StreamError
) as mock_play,
patch_ufp_method(
doorbell, "wait_until_audio_completes", new_callable=AsyncMock
) as mock_wait,
):
with pytest.raises(HomeAssistantError):
await hass.services.async_call(
"media_player",
"play_media",
{
ATTR_ENTITY_ID: "media_player.test_camera_speaker",
"media_content_id": "/test.mp3",
"media_content_type": "music",
},
blocking=True,
)
assert mock_play.called
assert not mock_wait.called
async def test_aiport_no_media_player_entities(
hass: HomeAssistant,
ufp: MockUFPFixture,
aiport: AiPort,
) -> None:
"""Test that AI Port devices do not create camera-specific media player entities."""
await init_entry(hass, ufp, [aiport])
# AI Port should not create any media player entities (speaker)
assert_entity_counts(hass, Platform.MEDIA_PLAYER, 0, 0)