mirror of
https://github.com/home-assistant/core.git
synced 2026-10-07 06:50:41 -04:00
Match ESPHome media player features to native API commands (#183397)
Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude
parent
ab60cff328
commit
b9588e57de
@@ -19,17 +19,22 @@ from aioesphomeapi import (
|
||||
from homeassistant.components import media_source
|
||||
from homeassistant.components.media_player import (
|
||||
ATTR_MEDIA_ANNOUNCE,
|
||||
ATTR_MEDIA_ENQUEUE,
|
||||
ATTR_MEDIA_EXTRA,
|
||||
BrowseMedia,
|
||||
MediaPlayerDeviceClass,
|
||||
MediaPlayerEnqueue,
|
||||
MediaPlayerEntity,
|
||||
MediaPlayerEntityFeature,
|
||||
MediaPlayerState,
|
||||
MediaType,
|
||||
RepeatMode,
|
||||
async_process_play_media_url,
|
||||
)
|
||||
from homeassistant.core import callback
|
||||
from homeassistant.exceptions import ServiceValidationError
|
||||
|
||||
from .const import DOMAIN
|
||||
from .entity import (
|
||||
EsphomeEntity,
|
||||
convert_api_error_ha_error,
|
||||
@@ -54,28 +59,30 @@ _STATES: EsphomeEnumMapper[EspMediaPlayerState, MediaPlayerState] = EsphomeEnumM
|
||||
}
|
||||
)
|
||||
|
||||
# The native API has no commands for these flags
|
||||
_UNSUPPORTED_FEATURES = (
|
||||
EspMediaPlayerEntityFeature.SEEK
|
||||
| EspMediaPlayerEntityFeature.PREVIOUS_TRACK
|
||||
| EspMediaPlayerEntityFeature.NEXT_TRACK
|
||||
| EspMediaPlayerEntityFeature.SELECT_SOURCE
|
||||
| EspMediaPlayerEntityFeature.SELECT_SOUND_MODE
|
||||
| EspMediaPlayerEntityFeature.SHUFFLE_SET
|
||||
| EspMediaPlayerEntityFeature.GROUPING
|
||||
)
|
||||
|
||||
_FEATURES = {
|
||||
EspMediaPlayerEntityFeature.PAUSE: MediaPlayerEntityFeature.PAUSE,
|
||||
EspMediaPlayerEntityFeature.SEEK: MediaPlayerEntityFeature.SEEK,
|
||||
EspMediaPlayerEntityFeature.VOLUME_SET: MediaPlayerEntityFeature.VOLUME_SET,
|
||||
EspMediaPlayerEntityFeature.VOLUME_MUTE: MediaPlayerEntityFeature.VOLUME_MUTE,
|
||||
EspMediaPlayerEntityFeature.PREVIOUS_TRACK: MediaPlayerEntityFeature.PREVIOUS_TRACK,
|
||||
EspMediaPlayerEntityFeature.NEXT_TRACK: MediaPlayerEntityFeature.NEXT_TRACK,
|
||||
EspMediaPlayerEntityFeature.TURN_ON: MediaPlayerEntityFeature.TURN_ON,
|
||||
EspMediaPlayerEntityFeature.TURN_OFF: MediaPlayerEntityFeature.TURN_OFF,
|
||||
EspMediaPlayerEntityFeature.PLAY_MEDIA: MediaPlayerEntityFeature.PLAY_MEDIA,
|
||||
EspMediaPlayerEntityFeature.VOLUME_STEP: MediaPlayerEntityFeature.VOLUME_STEP,
|
||||
EspMediaPlayerEntityFeature.SELECT_SOURCE: MediaPlayerEntityFeature.SELECT_SOURCE,
|
||||
EspMediaPlayerEntityFeature.STOP: MediaPlayerEntityFeature.STOP,
|
||||
EspMediaPlayerEntityFeature.CLEAR_PLAYLIST: MediaPlayerEntityFeature.CLEAR_PLAYLIST,
|
||||
EspMediaPlayerEntityFeature.PLAY: MediaPlayerEntityFeature.PLAY,
|
||||
EspMediaPlayerEntityFeature.SHUFFLE_SET: MediaPlayerEntityFeature.SHUFFLE_SET,
|
||||
EspMediaPlayerEntityFeature.SELECT_SOUND_MODE: (
|
||||
MediaPlayerEntityFeature.SELECT_SOUND_MODE
|
||||
),
|
||||
EspMediaPlayerEntityFeature.BROWSE_MEDIA: MediaPlayerEntityFeature.BROWSE_MEDIA,
|
||||
EspMediaPlayerEntityFeature.REPEAT_SET: MediaPlayerEntityFeature.REPEAT_SET,
|
||||
EspMediaPlayerEntityFeature.GROUPING: MediaPlayerEntityFeature.GROUPING,
|
||||
EspMediaPlayerEntityFeature.MEDIA_ANNOUNCE: MediaPlayerEntityFeature.MEDIA_ANNOUNCE,
|
||||
EspMediaPlayerEntityFeature.MEDIA_ENQUEUE: MediaPlayerEntityFeature.MEDIA_ENQUEUE,
|
||||
EspMediaPlayerEntityFeature.SEARCH_MEDIA: MediaPlayerEntityFeature.SEARCH_MEDIA,
|
||||
@@ -96,8 +103,11 @@ class EsphomeMediaPlayer(
|
||||
def _on_static_info_update(self, static_info: EntityInfo) -> None:
|
||||
"""Set attrs from static info."""
|
||||
super()._on_static_info_update(static_info)
|
||||
esp_flags = EspMediaPlayerEntityFeature(
|
||||
self._static_info.feature_flags_compat(self._api_version)
|
||||
esp_flags = (
|
||||
EspMediaPlayerEntityFeature(
|
||||
self._static_info.feature_flags_compat(self._api_version)
|
||||
)
|
||||
& ~_UNSUPPORTED_FEATURES
|
||||
)
|
||||
flags = MediaPlayerEntityFeature(0)
|
||||
for espflag in esp_flags:
|
||||
@@ -134,6 +144,14 @@ class EsphomeMediaPlayer(
|
||||
self, media_type: MediaType | str, media_id: str, **kwargs: Any
|
||||
) -> None:
|
||||
"""Send the play command with media url to the media player."""
|
||||
enqueue = kwargs.get(ATTR_MEDIA_ENQUEUE)
|
||||
# The device can only append to its playlist or replace it
|
||||
if enqueue in (MediaPlayerEnqueue.NEXT, MediaPlayerEnqueue.PLAY):
|
||||
raise ServiceValidationError(
|
||||
translation_domain=DOMAIN,
|
||||
translation_key="enqueue_mode_not_supported",
|
||||
translation_placeholders={"enqueue": enqueue},
|
||||
)
|
||||
if media_source.is_media_source_id(media_id):
|
||||
sourced_media = await media_source.async_resolve_media(
|
||||
self.hass, media_id, self.entity_id
|
||||
@@ -160,6 +178,11 @@ class EsphomeMediaPlayer(
|
||||
|
||||
self._client.media_player_command(
|
||||
self._key,
|
||||
command=(
|
||||
MediaPlayerCommand.ENQUEUE
|
||||
if enqueue == MediaPlayerEnqueue.ADD
|
||||
else None
|
||||
),
|
||||
media_url=media_id,
|
||||
announcement=announcement,
|
||||
device_id=self._static_info.device_id,
|
||||
@@ -251,6 +274,26 @@ class EsphomeMediaPlayer(
|
||||
self._key, volume=volume, device_id=self._static_info.device_id
|
||||
)
|
||||
|
||||
@convert_api_error_ha_error
|
||||
@override
|
||||
async def async_volume_up(self) -> None:
|
||||
"""Turn volume up."""
|
||||
self._client.media_player_command(
|
||||
self._key,
|
||||
command=MediaPlayerCommand.VOLUME_UP,
|
||||
device_id=self._static_info.device_id,
|
||||
)
|
||||
|
||||
@convert_api_error_ha_error
|
||||
@override
|
||||
async def async_volume_down(self) -> None:
|
||||
"""Turn volume down."""
|
||||
self._client.media_player_command(
|
||||
self._key,
|
||||
command=MediaPlayerCommand.VOLUME_DOWN,
|
||||
device_id=self._static_info.device_id,
|
||||
)
|
||||
|
||||
@convert_api_error_ha_error
|
||||
@override
|
||||
async def async_media_pause(self) -> None:
|
||||
@@ -281,6 +324,39 @@ class EsphomeMediaPlayer(
|
||||
device_id=self._static_info.device_id,
|
||||
)
|
||||
|
||||
@convert_api_error_ha_error
|
||||
@override
|
||||
async def async_clear_playlist(self) -> None:
|
||||
"""Clear the playlist."""
|
||||
self._client.media_player_command(
|
||||
self._key,
|
||||
command=MediaPlayerCommand.CLEAR_PLAYLIST,
|
||||
device_id=self._static_info.device_id,
|
||||
)
|
||||
|
||||
@convert_api_error_ha_error
|
||||
@override
|
||||
async def async_set_repeat(self, repeat: RepeatMode) -> None:
|
||||
"""Set the repeat mode."""
|
||||
if repeat == RepeatMode.ALL:
|
||||
raise ServiceValidationError(
|
||||
translation_domain=DOMAIN,
|
||||
translation_key="repeat_mode_not_supported",
|
||||
translation_placeholders={"repeat_mode": repeat},
|
||||
)
|
||||
self._client.media_player_command(
|
||||
self._key,
|
||||
command=(
|
||||
MediaPlayerCommand.REPEAT_ONE
|
||||
if repeat == RepeatMode.ONE
|
||||
else MediaPlayerCommand.REPEAT_OFF
|
||||
),
|
||||
device_id=self._static_info.device_id,
|
||||
)
|
||||
# The device does not report its repeat mode
|
||||
self._attr_repeat = repeat
|
||||
self.async_write_ha_state()
|
||||
|
||||
@convert_api_error_ha_error
|
||||
@override
|
||||
async def async_mute_volume(self, mute: bool) -> None:
|
||||
|
||||
@@ -142,6 +142,9 @@
|
||||
"action_call_timeout": {
|
||||
"message": "Timeout waiting for response from action call {call_name} on {device_name}"
|
||||
},
|
||||
"enqueue_mode_not_supported": {
|
||||
"message": "Enqueue mode {enqueue} is not supported."
|
||||
},
|
||||
"error_communicating_with_device": {
|
||||
"message": "Error communicating with the device {device_name}: {error}"
|
||||
},
|
||||
@@ -153,6 +156,9 @@
|
||||
},
|
||||
"ota_in_progress": {
|
||||
"message": "An OTA (Over-The-Air) update is already in progress for {configuration}."
|
||||
},
|
||||
"repeat_mode_not_supported": {
|
||||
"message": "Repeat mode {repeat_mode} is not supported."
|
||||
}
|
||||
},
|
||||
"issues": {
|
||||
|
||||
@@ -5,6 +5,7 @@ from unittest.mock import AsyncMock, Mock, call, patch
|
||||
from aioesphomeapi import (
|
||||
APIClient,
|
||||
MediaPlayerCommand,
|
||||
MediaPlayerEntityFeature as EspMediaPlayerEntityFeature,
|
||||
MediaPlayerEntityState,
|
||||
MediaPlayerFormatPurpose,
|
||||
MediaPlayerInfo,
|
||||
@@ -20,25 +21,35 @@ from homeassistant.components.media_player import (
|
||||
ATTR_MEDIA_ANNOUNCE,
|
||||
ATTR_MEDIA_CONTENT_ID,
|
||||
ATTR_MEDIA_CONTENT_TYPE,
|
||||
ATTR_MEDIA_ENQUEUE,
|
||||
ATTR_MEDIA_EXTRA,
|
||||
ATTR_MEDIA_REPEAT,
|
||||
ATTR_MEDIA_VOLUME_LEVEL,
|
||||
ATTR_MEDIA_VOLUME_MUTED,
|
||||
DOMAIN as MEDIA_PLAYER_DOMAIN,
|
||||
SERVICE_CLEAR_PLAYLIST,
|
||||
SERVICE_MEDIA_PAUSE,
|
||||
SERVICE_MEDIA_PLAY,
|
||||
SERVICE_MEDIA_STOP,
|
||||
SERVICE_PLAY_MEDIA,
|
||||
SERVICE_REPEAT_SET,
|
||||
SERVICE_TURN_OFF,
|
||||
SERVICE_TURN_ON,
|
||||
SERVICE_VOLUME_DOWN,
|
||||
SERVICE_VOLUME_MUTE,
|
||||
SERVICE_VOLUME_SET,
|
||||
SERVICE_VOLUME_UP,
|
||||
STATE_PLAYING,
|
||||
BrowseMedia,
|
||||
MediaClass,
|
||||
MediaPlayerEnqueue,
|
||||
MediaPlayerEntityFeature,
|
||||
MediaType,
|
||||
RepeatMode,
|
||||
)
|
||||
from homeassistant.const import ATTR_ENTITY_ID
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import ServiceValidationError
|
||||
from homeassistant.helpers import device_registry as dr, entity_registry as er
|
||||
from homeassistant.setup import async_setup_component
|
||||
|
||||
@@ -279,6 +290,323 @@ async def test_media_player_entity_with_undefined_flags(
|
||||
)
|
||||
|
||||
|
||||
async def test_media_player_entity_ignores_flags_without_command(
|
||||
hass: HomeAssistant,
|
||||
mock_client: APIClient,
|
||||
mock_generic_device_entry: MockGenericDeviceEntryType,
|
||||
) -> None:
|
||||
"""Test that device flags without a native API command are not exposed."""
|
||||
entity_info = [
|
||||
MediaPlayerInfo(
|
||||
object_id="mymedia_player",
|
||||
key=1,
|
||||
name="my media_player",
|
||||
feature_flags=(
|
||||
EspMediaPlayerEntityFeature.PAUSE
|
||||
| EspMediaPlayerEntityFeature.PLAY
|
||||
| EspMediaPlayerEntityFeature.VOLUME_STEP
|
||||
| EspMediaPlayerEntityFeature.CLEAR_PLAYLIST
|
||||
| EspMediaPlayerEntityFeature.REPEAT_SET
|
||||
| EspMediaPlayerEntityFeature.MEDIA_ENQUEUE
|
||||
| EspMediaPlayerEntityFeature.SEEK
|
||||
| EspMediaPlayerEntityFeature.PREVIOUS_TRACK
|
||||
| EspMediaPlayerEntityFeature.NEXT_TRACK
|
||||
| EspMediaPlayerEntityFeature.SELECT_SOURCE
|
||||
| EspMediaPlayerEntityFeature.SELECT_SOUND_MODE
|
||||
| EspMediaPlayerEntityFeature.SHUFFLE_SET
|
||||
| EspMediaPlayerEntityFeature.GROUPING
|
||||
),
|
||||
)
|
||||
]
|
||||
states = [
|
||||
MediaPlayerEntityState(
|
||||
key=1, volume=50, muted=False, state=MediaPlayerState.PLAYING
|
||||
)
|
||||
]
|
||||
await mock_generic_device_entry(
|
||||
mock_client=mock_client,
|
||||
entity_info=entity_info,
|
||||
states=states,
|
||||
)
|
||||
state = hass.states.get("media_player.test_my_media_player")
|
||||
assert state is not None
|
||||
assert state.attributes["supported_features"] == (
|
||||
MediaPlayerEntityFeature.PAUSE
|
||||
| MediaPlayerEntityFeature.PLAY
|
||||
| MediaPlayerEntityFeature.VOLUME_STEP
|
||||
| MediaPlayerEntityFeature.CLEAR_PLAYLIST
|
||||
| MediaPlayerEntityFeature.REPEAT_SET
|
||||
| MediaPlayerEntityFeature.MEDIA_ENQUEUE
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("service", "service_data", "command"),
|
||||
[
|
||||
pytest.param(
|
||||
SERVICE_CLEAR_PLAYLIST,
|
||||
{},
|
||||
MediaPlayerCommand.CLEAR_PLAYLIST,
|
||||
id="clear_playlist",
|
||||
),
|
||||
pytest.param(
|
||||
SERVICE_REPEAT_SET,
|
||||
{ATTR_MEDIA_REPEAT: RepeatMode.ONE},
|
||||
MediaPlayerCommand.REPEAT_ONE,
|
||||
id="repeat_one",
|
||||
),
|
||||
pytest.param(
|
||||
SERVICE_REPEAT_SET,
|
||||
{ATTR_MEDIA_REPEAT: RepeatMode.OFF},
|
||||
MediaPlayerCommand.REPEAT_OFF,
|
||||
id="repeat_off",
|
||||
),
|
||||
pytest.param(
|
||||
SERVICE_VOLUME_UP,
|
||||
{},
|
||||
MediaPlayerCommand.VOLUME_UP,
|
||||
id="volume_up",
|
||||
),
|
||||
pytest.param(
|
||||
SERVICE_VOLUME_DOWN,
|
||||
{},
|
||||
MediaPlayerCommand.VOLUME_DOWN,
|
||||
id="volume_down",
|
||||
),
|
||||
],
|
||||
)
|
||||
async def test_media_player_playlist_commands(
|
||||
hass: HomeAssistant,
|
||||
mock_client: APIClient,
|
||||
mock_generic_device_entry: MockGenericDeviceEntryType,
|
||||
service: str,
|
||||
service_data: dict[str, str],
|
||||
command: MediaPlayerCommand,
|
||||
) -> None:
|
||||
"""Test the clear playlist, repeat and volume step commands."""
|
||||
entity_info = [
|
||||
MediaPlayerInfo(
|
||||
object_id="mymedia_player",
|
||||
key=1,
|
||||
name="my media_player",
|
||||
feature_flags=(
|
||||
EspMediaPlayerEntityFeature.CLEAR_PLAYLIST
|
||||
| EspMediaPlayerEntityFeature.REPEAT_SET
|
||||
| EspMediaPlayerEntityFeature.VOLUME_STEP
|
||||
),
|
||||
)
|
||||
]
|
||||
states = [
|
||||
MediaPlayerEntityState(
|
||||
key=1, volume=50, muted=False, state=MediaPlayerState.PLAYING
|
||||
)
|
||||
]
|
||||
await mock_generic_device_entry(
|
||||
mock_client=mock_client,
|
||||
entity_info=entity_info,
|
||||
states=states,
|
||||
)
|
||||
|
||||
await hass.services.async_call(
|
||||
MEDIA_PLAYER_DOMAIN,
|
||||
service,
|
||||
{ATTR_ENTITY_ID: "media_player.test_my_media_player", **service_data},
|
||||
blocking=True,
|
||||
)
|
||||
mock_client.media_player_command.assert_called_once_with(
|
||||
1, command=command, device_id=0
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("repeat", [RepeatMode.ONE, RepeatMode.OFF])
|
||||
async def test_media_player_repeat_state(
|
||||
hass: HomeAssistant,
|
||||
mock_client: APIClient,
|
||||
mock_generic_device_entry: MockGenericDeviceEntryType,
|
||||
repeat: RepeatMode,
|
||||
) -> None:
|
||||
"""Test that the repeat mode is kept after it is set."""
|
||||
entity_info = [
|
||||
MediaPlayerInfo(
|
||||
object_id="mymedia_player",
|
||||
key=1,
|
||||
name="my media_player",
|
||||
feature_flags=EspMediaPlayerEntityFeature.REPEAT_SET,
|
||||
)
|
||||
]
|
||||
states = [
|
||||
MediaPlayerEntityState(
|
||||
key=1, volume=50, muted=False, state=MediaPlayerState.PLAYING
|
||||
)
|
||||
]
|
||||
await mock_generic_device_entry(
|
||||
mock_client=mock_client,
|
||||
entity_info=entity_info,
|
||||
states=states,
|
||||
)
|
||||
|
||||
await hass.services.async_call(
|
||||
MEDIA_PLAYER_DOMAIN,
|
||||
SERVICE_REPEAT_SET,
|
||||
{
|
||||
ATTR_ENTITY_ID: "media_player.test_my_media_player",
|
||||
ATTR_MEDIA_REPEAT: repeat,
|
||||
},
|
||||
blocking=True,
|
||||
)
|
||||
state = hass.states.get("media_player.test_my_media_player")
|
||||
assert state is not None
|
||||
assert state.attributes[ATTR_MEDIA_REPEAT] == repeat
|
||||
|
||||
|
||||
async def test_media_player_repeat_all_not_supported(
|
||||
hass: HomeAssistant,
|
||||
mock_client: APIClient,
|
||||
mock_generic_device_entry: MockGenericDeviceEntryType,
|
||||
) -> None:
|
||||
"""Test that repeat mode all raises an error."""
|
||||
entity_info = [
|
||||
MediaPlayerInfo(
|
||||
object_id="mymedia_player",
|
||||
key=1,
|
||||
name="my media_player",
|
||||
feature_flags=EspMediaPlayerEntityFeature.REPEAT_SET,
|
||||
)
|
||||
]
|
||||
states = [
|
||||
MediaPlayerEntityState(
|
||||
key=1, volume=50, muted=False, state=MediaPlayerState.PLAYING
|
||||
)
|
||||
]
|
||||
await mock_generic_device_entry(
|
||||
mock_client=mock_client,
|
||||
entity_info=entity_info,
|
||||
states=states,
|
||||
)
|
||||
|
||||
with pytest.raises(ServiceValidationError) as exc_info:
|
||||
await hass.services.async_call(
|
||||
MEDIA_PLAYER_DOMAIN,
|
||||
SERVICE_REPEAT_SET,
|
||||
{
|
||||
ATTR_ENTITY_ID: "media_player.test_my_media_player",
|
||||
ATTR_MEDIA_REPEAT: RepeatMode.ALL,
|
||||
},
|
||||
blocking=True,
|
||||
)
|
||||
assert exc_info.value.translation_key == "repeat_mode_not_supported"
|
||||
mock_client.media_player_command.assert_not_called()
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("enqueue", "command"),
|
||||
[
|
||||
pytest.param(MediaPlayerEnqueue.ADD, MediaPlayerCommand.ENQUEUE, id="add"),
|
||||
pytest.param(MediaPlayerEnqueue.REPLACE, None, id="replace"),
|
||||
],
|
||||
)
|
||||
async def test_media_player_play_media_enqueue(
|
||||
hass: HomeAssistant,
|
||||
mock_client: APIClient,
|
||||
mock_generic_device_entry: MockGenericDeviceEntryType,
|
||||
enqueue: MediaPlayerEnqueue,
|
||||
command: MediaPlayerCommand | None,
|
||||
) -> None:
|
||||
"""Test that only the add enqueue mode sends the enqueue command."""
|
||||
entity_info = [
|
||||
MediaPlayerInfo(
|
||||
object_id="mymedia_player",
|
||||
key=1,
|
||||
name="my media_player",
|
||||
feature_flags=(
|
||||
EspMediaPlayerEntityFeature.PLAY_MEDIA
|
||||
| EspMediaPlayerEntityFeature.MEDIA_ENQUEUE
|
||||
),
|
||||
)
|
||||
]
|
||||
states = [
|
||||
MediaPlayerEntityState(
|
||||
key=1, volume=50, muted=False, state=MediaPlayerState.PLAYING
|
||||
)
|
||||
]
|
||||
await mock_generic_device_entry(
|
||||
mock_client=mock_client,
|
||||
entity_info=entity_info,
|
||||
states=states,
|
||||
)
|
||||
|
||||
await hass.services.async_call(
|
||||
MEDIA_PLAYER_DOMAIN,
|
||||
SERVICE_PLAY_MEDIA,
|
||||
{
|
||||
ATTR_ENTITY_ID: "media_player.test_my_media_player",
|
||||
ATTR_MEDIA_CONTENT_TYPE: MediaType.MUSIC,
|
||||
ATTR_MEDIA_CONTENT_ID: "http://www.example.com/xy.mp3",
|
||||
ATTR_MEDIA_ENQUEUE: enqueue,
|
||||
},
|
||||
blocking=True,
|
||||
)
|
||||
mock_client.media_player_command.assert_called_once_with(
|
||||
1,
|
||||
command=command,
|
||||
media_url="http://www.example.com/xy.mp3",
|
||||
announcement=None,
|
||||
device_id=0,
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"enqueue",
|
||||
[
|
||||
pytest.param(MediaPlayerEnqueue.NEXT, id="next"),
|
||||
pytest.param(MediaPlayerEnqueue.PLAY, id="play"),
|
||||
],
|
||||
)
|
||||
async def test_media_player_play_media_enqueue_not_supported(
|
||||
hass: HomeAssistant,
|
||||
mock_client: APIClient,
|
||||
mock_generic_device_entry: MockGenericDeviceEntryType,
|
||||
enqueue: MediaPlayerEnqueue,
|
||||
) -> None:
|
||||
"""Test that enqueue modes the device cannot follow raise an error."""
|
||||
entity_info = [
|
||||
MediaPlayerInfo(
|
||||
object_id="mymedia_player",
|
||||
key=1,
|
||||
name="my media_player",
|
||||
feature_flags=(
|
||||
EspMediaPlayerEntityFeature.PLAY_MEDIA
|
||||
| EspMediaPlayerEntityFeature.MEDIA_ENQUEUE
|
||||
),
|
||||
)
|
||||
]
|
||||
states = [
|
||||
MediaPlayerEntityState(
|
||||
key=1, volume=50, muted=False, state=MediaPlayerState.PLAYING
|
||||
)
|
||||
]
|
||||
await mock_generic_device_entry(
|
||||
mock_client=mock_client,
|
||||
entity_info=entity_info,
|
||||
states=states,
|
||||
)
|
||||
|
||||
with pytest.raises(ServiceValidationError) as exc_info:
|
||||
await hass.services.async_call(
|
||||
MEDIA_PLAYER_DOMAIN,
|
||||
SERVICE_PLAY_MEDIA,
|
||||
{
|
||||
ATTR_ENTITY_ID: "media_player.test_my_media_player",
|
||||
ATTR_MEDIA_CONTENT_TYPE: MediaType.MUSIC,
|
||||
ATTR_MEDIA_CONTENT_ID: "http://www.example.com/xy.mp3",
|
||||
ATTR_MEDIA_ENQUEUE: enqueue,
|
||||
},
|
||||
blocking=True,
|
||||
)
|
||||
assert exc_info.value.translation_key == "enqueue_mode_not_supported"
|
||||
mock_client.media_player_command.assert_not_called()
|
||||
|
||||
|
||||
async def test_media_player_entity_with_source(
|
||||
hass: HomeAssistant,
|
||||
mock_client: APIClient,
|
||||
@@ -384,6 +712,7 @@ async def test_media_player_entity_with_source(
|
||||
[
|
||||
call(
|
||||
1,
|
||||
command=None,
|
||||
media_url="http://www.example.com/xy.mp3",
|
||||
announcement=None,
|
||||
device_id=0,
|
||||
@@ -418,6 +747,7 @@ async def test_media_player_entity_with_source(
|
||||
[
|
||||
call(
|
||||
1,
|
||||
command=None,
|
||||
media_url="media-source://tts?message=hello",
|
||||
announcement=True,
|
||||
device_id=0,
|
||||
|
||||
Reference in New Issue
Block a user