Add actions to Music Assistant integration (#129515)
Co-authored-by: Franck Nijhof <git@frenck.dev> Co-authored-by: Martin Hjelmare <marhje52@gmail.com>pull/131746/head
parent
3eb483c1b0
commit
3485ce9c71
|
@ -28,13 +28,13 @@ from .const import DOMAIN, LOGGER
|
|||
if TYPE_CHECKING:
|
||||
from music_assistant_models.event import MassEvent
|
||||
|
||||
type MusicAssistantConfigEntry = ConfigEntry[MusicAssistantEntryData]
|
||||
|
||||
PLATFORMS = [Platform.MEDIA_PLAYER]
|
||||
|
||||
CONNECT_TIMEOUT = 10
|
||||
LISTEN_READY_TIMEOUT = 30
|
||||
|
||||
type MusicAssistantConfigEntry = ConfigEntry[MusicAssistantEntryData]
|
||||
|
||||
|
||||
@dataclass
|
||||
class MusicAssistantEntryData:
|
||||
|
@ -47,7 +47,7 @@ class MusicAssistantEntryData:
|
|||
async def async_setup_entry(
|
||||
hass: HomeAssistant, entry: MusicAssistantConfigEntry
|
||||
) -> bool:
|
||||
"""Set up from a config entry."""
|
||||
"""Set up Music Assistant from a config entry."""
|
||||
http_session = async_get_clientsession(hass, verify_ssl=False)
|
||||
mass_url = entry.data[CONF_URL]
|
||||
mass = MusicAssistantClient(mass_url, http_session)
|
||||
|
@ -97,6 +97,7 @@ async def async_setup_entry(
|
|||
listen_task.cancel()
|
||||
raise ConfigEntryNotReady("Music Assistant client not ready") from err
|
||||
|
||||
# store the listen task and mass client in the entry data
|
||||
entry.runtime_data = MusicAssistantEntryData(mass, listen_task)
|
||||
|
||||
# If the listen task is already failed, we need to raise ConfigEntryNotReady
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"services": {
|
||||
"play_media": { "service": "mdi:play" },
|
||||
"play_announcement": { "service": "mdi:bullhorn" },
|
||||
"transfer_queue": { "service": "mdi:transfer" }
|
||||
}
|
||||
}
|
|
@ -13,15 +13,18 @@ from music_assistant_models.enums import (
|
|||
EventType,
|
||||
MediaType,
|
||||
PlayerFeature,
|
||||
PlayerState as MassPlayerState,
|
||||
QueueOption,
|
||||
RepeatMode as MassRepeatMode,
|
||||
)
|
||||
from music_assistant_models.errors import MediaNotFoundError, MusicAssistantError
|
||||
from music_assistant_models.event import MassEvent
|
||||
from music_assistant_models.media_items import ItemMapping, MediaItemType, Track
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components import media_source
|
||||
from homeassistant.components.media_player import (
|
||||
ATTR_MEDIA_ENQUEUE,
|
||||
ATTR_MEDIA_EXTRA,
|
||||
BrowseMedia,
|
||||
MediaPlayerDeviceClass,
|
||||
|
@ -37,7 +40,11 @@ from homeassistant.const import STATE_OFF
|
|||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.helpers.entity_platform import (
|
||||
AddEntitiesCallback,
|
||||
async_get_current_platform,
|
||||
)
|
||||
from homeassistant.util.dt import utc_from_timestamp
|
||||
|
||||
from . import MusicAssistantConfigEntry
|
||||
|
@ -79,6 +86,9 @@ QUEUE_OPTION_MAP = {
|
|||
MediaPlayerEnqueue.REPLACE: QueueOption.REPLACE,
|
||||
}
|
||||
|
||||
SERVICE_PLAY_MEDIA_ADVANCED = "play_media"
|
||||
SERVICE_PLAY_ANNOUNCEMENT = "play_announcement"
|
||||
SERVICE_TRANSFER_QUEUE = "transfer_queue"
|
||||
ATTR_RADIO_MODE = "radio_mode"
|
||||
ATTR_MEDIA_ID = "media_id"
|
||||
ATTR_MEDIA_TYPE = "media_type"
|
||||
|
@ -138,6 +148,38 @@ async def async_setup_entry(
|
|||
|
||||
async_add_entities(mass_players)
|
||||
|
||||
# add platform service for play_media with advanced options
|
||||
platform = async_get_current_platform()
|
||||
platform.async_register_entity_service(
|
||||
SERVICE_PLAY_MEDIA_ADVANCED,
|
||||
{
|
||||
vol.Required(ATTR_MEDIA_ID): vol.All(cv.ensure_list, [cv.string]),
|
||||
vol.Optional(ATTR_MEDIA_TYPE): vol.Coerce(MediaType),
|
||||
vol.Optional(ATTR_MEDIA_ENQUEUE): vol.Coerce(QueueOption),
|
||||
vol.Optional(ATTR_ARTIST): cv.string,
|
||||
vol.Optional(ATTR_ALBUM): cv.string,
|
||||
vol.Optional(ATTR_RADIO_MODE): vol.Coerce(bool),
|
||||
},
|
||||
"_async_handle_play_media",
|
||||
)
|
||||
platform.async_register_entity_service(
|
||||
SERVICE_PLAY_ANNOUNCEMENT,
|
||||
{
|
||||
vol.Required(ATTR_URL): cv.string,
|
||||
vol.Optional(ATTR_USE_PRE_ANNOUNCE): vol.Coerce(bool),
|
||||
vol.Optional(ATTR_ANNOUNCE_VOLUME): vol.Coerce(int),
|
||||
},
|
||||
"_async_handle_play_announcement",
|
||||
)
|
||||
platform.async_register_entity_service(
|
||||
SERVICE_TRANSFER_QUEUE,
|
||||
{
|
||||
vol.Optional(ATTR_SOURCE_PLAYER): cv.entity_id,
|
||||
vol.Optional(ATTR_AUTO_PLAY): vol.Coerce(bool),
|
||||
},
|
||||
"_async_handle_transfer_queue",
|
||||
)
|
||||
|
||||
|
||||
class MusicAssistantPlayer(MusicAssistantEntity, MediaPlayerEntity):
|
||||
"""Representation of MediaPlayerEntity from Music Assistant Player."""
|
||||
|
@ -376,6 +418,8 @@ class MusicAssistantPlayer(MusicAssistantEntity, MediaPlayerEntity):
|
|||
async def _async_handle_play_media(
|
||||
self,
|
||||
media_id: list[str],
|
||||
artist: str | None = None,
|
||||
album: str | None = None,
|
||||
enqueue: MediaPlayerEnqueue | QueueOption | None = None,
|
||||
radio_mode: bool | None = None,
|
||||
media_type: str | None = None,
|
||||
|
@ -402,6 +446,14 @@ class MusicAssistantPlayer(MusicAssistantEntity, MediaPlayerEntity):
|
|||
elif await asyncio.to_thread(os.path.isfile, media_id_str):
|
||||
media_uris.append(media_id_str)
|
||||
continue
|
||||
# last resort: search for media item by name/search
|
||||
if item := await self.mass.music.get_item_by_name(
|
||||
name=media_id_str,
|
||||
artist=artist,
|
||||
album=album,
|
||||
media_type=MediaType(media_type) if media_type else None,
|
||||
):
|
||||
media_uris.append(item.uri)
|
||||
|
||||
if not media_uris:
|
||||
raise HomeAssistantError(
|
||||
|
@ -435,6 +487,32 @@ class MusicAssistantPlayer(MusicAssistantEntity, MediaPlayerEntity):
|
|||
self.player_id, url, use_pre_announce, announce_volume
|
||||
)
|
||||
|
||||
@catch_musicassistant_error
|
||||
async def _async_handle_transfer_queue(
|
||||
self, source_player: str | None = None, auto_play: bool | None = None
|
||||
) -> None:
|
||||
"""Transfer the current queue to another player."""
|
||||
if not source_player:
|
||||
# no source player given; try to find a playing player(queue)
|
||||
for queue in self.mass.player_queues:
|
||||
if queue.state == MassPlayerState.PLAYING:
|
||||
source_queue_id = queue.queue_id
|
||||
break
|
||||
else:
|
||||
raise HomeAssistantError(
|
||||
"Source player not specified and no playing player found."
|
||||
)
|
||||
else:
|
||||
# resolve HA entity_id to MA player_id
|
||||
entity_registry = er.async_get(self.hass)
|
||||
if (entity := entity_registry.async_get(source_player)) is None:
|
||||
raise HomeAssistantError("Source player not available.")
|
||||
source_queue_id = entity.unique_id # unique_id is the MA player_id
|
||||
target_queue_id = self.player_id
|
||||
await self.mass.player_queues.transfer_queue(
|
||||
source_queue_id, target_queue_id, auto_play
|
||||
)
|
||||
|
||||
async def async_browse_media(
|
||||
self,
|
||||
media_content_type: MediaType | str | None = None,
|
||||
|
|
|
@ -0,0 +1,90 @@
|
|||
# Descriptions for Music Assistant custom services
|
||||
|
||||
play_media:
|
||||
target:
|
||||
entity:
|
||||
domain: media_player
|
||||
integration: music_assistant
|
||||
supported_features:
|
||||
- media_player.MediaPlayerEntityFeature.PLAY_MEDIA
|
||||
fields:
|
||||
media_id:
|
||||
required: true
|
||||
example: "spotify://playlist/aabbccddeeff"
|
||||
selector:
|
||||
object:
|
||||
media_type:
|
||||
example: "playlist"
|
||||
selector:
|
||||
select:
|
||||
translation_key: media_type
|
||||
options:
|
||||
- artist
|
||||
- album
|
||||
- playlist
|
||||
- track
|
||||
- radio
|
||||
artist:
|
||||
example: "Queen"
|
||||
selector:
|
||||
text:
|
||||
album:
|
||||
example: "News of the world"
|
||||
selector:
|
||||
text:
|
||||
enqueue:
|
||||
selector:
|
||||
select:
|
||||
options:
|
||||
- "play"
|
||||
- "replace"
|
||||
- "next"
|
||||
- "replace_next"
|
||||
- "add"
|
||||
translation_key: enqueue
|
||||
radio_mode:
|
||||
advanced: true
|
||||
selector:
|
||||
boolean:
|
||||
|
||||
play_announcement:
|
||||
target:
|
||||
entity:
|
||||
domain: media_player
|
||||
integration: music_assistant
|
||||
supported_features:
|
||||
- media_player.MediaPlayerEntityFeature.PLAY_MEDIA
|
||||
- media_player.MediaPlayerEntityFeature.MEDIA_ANNOUNCE
|
||||
fields:
|
||||
url:
|
||||
required: true
|
||||
example: "http://someremotesite.com/doorbell.mp3"
|
||||
selector:
|
||||
text:
|
||||
use_pre_announce:
|
||||
example: "true"
|
||||
selector:
|
||||
boolean:
|
||||
announce_volume:
|
||||
example: 75
|
||||
selector:
|
||||
number:
|
||||
min: 1
|
||||
max: 100
|
||||
step: 1
|
||||
|
||||
transfer_queue:
|
||||
target:
|
||||
entity:
|
||||
domain: media_player
|
||||
integration: music_assistant
|
||||
fields:
|
||||
source_player:
|
||||
selector:
|
||||
entity:
|
||||
domain: media_player
|
||||
integration: music_assistant
|
||||
auto_play:
|
||||
example: "true"
|
||||
selector:
|
||||
boolean:
|
|
@ -37,6 +37,70 @@
|
|||
"description": "Check if there are updates available for the Music Assistant Server and/or integration."
|
||||
}
|
||||
},
|
||||
"services": {
|
||||
"play_media": {
|
||||
"name": "Play media",
|
||||
"description": "Play media on a Music Assistant player with more fine-grained control options.",
|
||||
"fields": {
|
||||
"media_id": {
|
||||
"name": "Media ID(s)",
|
||||
"description": "URI or name of the item you want to play. Specify a list if you want to play/enqueue multiple items."
|
||||
},
|
||||
"media_type": {
|
||||
"name": "Media type",
|
||||
"description": "The type of the content to play. Such as artist, album, track or playlist. Will be auto-determined if omitted."
|
||||
},
|
||||
"enqueue": {
|
||||
"name": "Enqueue",
|
||||
"description": "If the content should be played now or added to the queue."
|
||||
},
|
||||
"artist": {
|
||||
"name": "Artist name",
|
||||
"description": "When specifying a track or album by name in the Media ID field, you can optionally restrict results by this artist name."
|
||||
},
|
||||
"album": {
|
||||
"name": "Album name",
|
||||
"description": "When specifying a track by name in the Media ID field, you can optionally restrict results by this album name."
|
||||
},
|
||||
"radio_mode": {
|
||||
"name": "Enable radio mode",
|
||||
"description": "Enable radio mode to auto-generate a playlist based on the selection."
|
||||
}
|
||||
}
|
||||
},
|
||||
"play_announcement": {
|
||||
"name": "Play announcement",
|
||||
"description": "Play announcement on a Music Assistant player with more fine-grained control options.",
|
||||
"fields": {
|
||||
"url": {
|
||||
"name": "URL",
|
||||
"description": "URL to the notification sound."
|
||||
},
|
||||
"use_pre_announce": {
|
||||
"name": "Use pre-announce",
|
||||
"description": "Use pre-announcement sound for the announcement. Omit to use the player default."
|
||||
},
|
||||
"announce_volume": {
|
||||
"name": "Announce volume",
|
||||
"description": "Use a forced volume level for the announcement. Omit to use player default."
|
||||
}
|
||||
}
|
||||
},
|
||||
"transfer_queue": {
|
||||
"name": "Transfer queue",
|
||||
"description": "Transfer the player's queue to another player.",
|
||||
"fields": {
|
||||
"source_player": {
|
||||
"name": "Source media player",
|
||||
"description": "The source media player which has the queue you want to transfer. When omitted, the first playing player will be used."
|
||||
},
|
||||
"auto_play": {
|
||||
"name": "Auto play",
|
||||
"description": "Start playing the queue on the target player. Omit to use the default behavior."
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"selector": {
|
||||
"enqueue": {
|
||||
"options": {
|
||||
|
@ -46,6 +110,15 @@
|
|||
"replace": "Play now and clear queue",
|
||||
"replace_next": "Play next and clear queue"
|
||||
}
|
||||
},
|
||||
"media_type": {
|
||||
"options": {
|
||||
"artist": "Artist",
|
||||
"album": "Album",
|
||||
"track": "Track",
|
||||
"playlist": "Playlist",
|
||||
"radio": "Radio"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,9 +2,13 @@
|
|||
|
||||
from unittest.mock import MagicMock, call
|
||||
|
||||
from music_assistant_models.enums import MediaType, QueueOption
|
||||
from music_assistant_models.media_items import Track
|
||||
import pytest
|
||||
from syrupy import SnapshotAssertion
|
||||
|
||||
from homeassistant.components.media_player import (
|
||||
ATTR_MEDIA_ENQUEUE,
|
||||
ATTR_MEDIA_REPEAT,
|
||||
ATTR_MEDIA_SEEK_POSITION,
|
||||
ATTR_MEDIA_SHUFFLE,
|
||||
|
@ -13,6 +17,23 @@ from homeassistant.components.media_player import (
|
|||
DOMAIN as MEDIA_PLAYER_DOMAIN,
|
||||
SERVICE_CLEAR_PLAYLIST,
|
||||
)
|
||||
from homeassistant.components.music_assistant.const import DOMAIN as MASS_DOMAIN
|
||||
from homeassistant.components.music_assistant.media_player import (
|
||||
ATTR_ALBUM,
|
||||
ATTR_ANNOUNCE_VOLUME,
|
||||
ATTR_ARTIST,
|
||||
ATTR_AUTO_PLAY,
|
||||
ATTR_MEDIA_ID,
|
||||
ATTR_MEDIA_TYPE,
|
||||
ATTR_RADIO_MODE,
|
||||
ATTR_SOURCE_PLAYER,
|
||||
ATTR_URL,
|
||||
ATTR_USE_PRE_ANNOUNCE,
|
||||
SERVICE_PLAY_ANNOUNCEMENT,
|
||||
SERVICE_PLAY_MEDIA_ADVANCED,
|
||||
SERVICE_TRANSFER_QUEUE,
|
||||
)
|
||||
from homeassistant.config_entries import HomeAssistantError
|
||||
from homeassistant.const import (
|
||||
ATTR_ENTITY_ID,
|
||||
SERVICE_MEDIA_NEXT_TRACK,
|
||||
|
@ -35,6 +56,15 @@ from homeassistant.helpers import entity_registry as er
|
|||
|
||||
from .common import setup_integration_from_fixtures, snapshot_music_assistant_entities
|
||||
|
||||
from tests.common import AsyncMock
|
||||
|
||||
MOCK_TRACK = Track(
|
||||
item_id="1",
|
||||
provider="library",
|
||||
name="Test Track",
|
||||
provider_mappings={},
|
||||
)
|
||||
|
||||
|
||||
async def test_media_player(
|
||||
hass: HomeAssistant,
|
||||
|
@ -110,11 +140,11 @@ async def test_media_player_seek_action(
|
|||
)
|
||||
|
||||
|
||||
async def test_media_player_volume_action(
|
||||
async def test_media_player_volume_set_action(
|
||||
hass: HomeAssistant,
|
||||
music_assistant_client: MagicMock,
|
||||
) -> None:
|
||||
"""Test media_player entity volume action."""
|
||||
"""Test media_player entity volume_set action."""
|
||||
await setup_integration_from_fixtures(hass, music_assistant_client)
|
||||
entity_id = "media_player.test_player_1"
|
||||
mass_player_id = "00:00:00:00:00:01"
|
||||
|
@ -261,3 +291,227 @@ async def test_media_player_clear_playlist_action(
|
|||
assert music_assistant_client.send_command.call_args == call(
|
||||
"player_queues/clear", queue_id=mass_player_id
|
||||
)
|
||||
|
||||
|
||||
async def test_media_player_play_media_action(
|
||||
hass: HomeAssistant,
|
||||
music_assistant_client: MagicMock,
|
||||
) -> None:
|
||||
"""Test media_player (advanced) play_media action."""
|
||||
await setup_integration_from_fixtures(hass, music_assistant_client)
|
||||
entity_id = "media_player.test_player_1"
|
||||
mass_player_id = "00:00:00:00:00:01"
|
||||
state = hass.states.get(entity_id)
|
||||
assert state
|
||||
|
||||
# test simple play_media call with URI as media_id and no media type
|
||||
await hass.services.async_call(
|
||||
MASS_DOMAIN,
|
||||
SERVICE_PLAY_MEDIA_ADVANCED,
|
||||
{
|
||||
ATTR_ENTITY_ID: entity_id,
|
||||
ATTR_MEDIA_ID: "spotify://track/1234",
|
||||
},
|
||||
blocking=True,
|
||||
)
|
||||
assert music_assistant_client.send_command.call_count == 1
|
||||
assert music_assistant_client.send_command.call_args == call(
|
||||
"player_queues/play_media",
|
||||
queue_id=mass_player_id,
|
||||
media=["spotify://track/1234"],
|
||||
option=None,
|
||||
radio_mode=False,
|
||||
start_item=None,
|
||||
)
|
||||
|
||||
# test simple play_media call with URI and enqueue specified
|
||||
music_assistant_client.send_command.reset_mock()
|
||||
await hass.services.async_call(
|
||||
MASS_DOMAIN,
|
||||
SERVICE_PLAY_MEDIA_ADVANCED,
|
||||
{
|
||||
ATTR_ENTITY_ID: entity_id,
|
||||
ATTR_MEDIA_ID: "spotify://track/1234",
|
||||
ATTR_MEDIA_ENQUEUE: "add",
|
||||
},
|
||||
blocking=True,
|
||||
)
|
||||
assert music_assistant_client.send_command.call_count == 1
|
||||
assert music_assistant_client.send_command.call_args == call(
|
||||
"player_queues/play_media",
|
||||
queue_id=mass_player_id,
|
||||
media=["spotify://track/1234"],
|
||||
option=QueueOption.ADD,
|
||||
radio_mode=False,
|
||||
start_item=None,
|
||||
)
|
||||
|
||||
# test basic play_media call with URL and radio mode specified
|
||||
music_assistant_client.send_command.reset_mock()
|
||||
await hass.services.async_call(
|
||||
MASS_DOMAIN,
|
||||
SERVICE_PLAY_MEDIA_ADVANCED,
|
||||
{
|
||||
ATTR_ENTITY_ID: entity_id,
|
||||
ATTR_MEDIA_ID: "spotify://track/1234",
|
||||
ATTR_RADIO_MODE: True,
|
||||
},
|
||||
blocking=True,
|
||||
)
|
||||
assert music_assistant_client.send_command.call_count == 1
|
||||
assert music_assistant_client.send_command.call_args == call(
|
||||
"player_queues/play_media",
|
||||
queue_id=mass_player_id,
|
||||
media=["spotify://track/1234"],
|
||||
option=None,
|
||||
radio_mode=True,
|
||||
start_item=None,
|
||||
)
|
||||
|
||||
# test play_media call with media id and media type specified
|
||||
music_assistant_client.send_command.reset_mock()
|
||||
music_assistant_client.music.get_item = AsyncMock(return_value=MOCK_TRACK)
|
||||
await hass.services.async_call(
|
||||
MASS_DOMAIN,
|
||||
SERVICE_PLAY_MEDIA_ADVANCED,
|
||||
{
|
||||
ATTR_ENTITY_ID: entity_id,
|
||||
ATTR_MEDIA_ID: "1",
|
||||
ATTR_MEDIA_TYPE: "track",
|
||||
},
|
||||
blocking=True,
|
||||
)
|
||||
assert music_assistant_client.music.get_item.call_count == 1
|
||||
assert music_assistant_client.music.get_item.call_args == call(
|
||||
MediaType.TRACK, "1", "library"
|
||||
)
|
||||
assert music_assistant_client.send_command.call_count == 1
|
||||
assert music_assistant_client.send_command.call_args == call(
|
||||
"player_queues/play_media",
|
||||
queue_id=mass_player_id,
|
||||
media=[MOCK_TRACK.uri],
|
||||
option=None,
|
||||
radio_mode=False,
|
||||
start_item=None,
|
||||
)
|
||||
|
||||
# test play_media call by name
|
||||
music_assistant_client.send_command.reset_mock()
|
||||
music_assistant_client.music.get_item_by_name = AsyncMock(return_value=MOCK_TRACK)
|
||||
await hass.services.async_call(
|
||||
MASS_DOMAIN,
|
||||
SERVICE_PLAY_MEDIA_ADVANCED,
|
||||
{
|
||||
ATTR_ENTITY_ID: entity_id,
|
||||
ATTR_MEDIA_ID: "test",
|
||||
ATTR_ARTIST: "artist",
|
||||
ATTR_ALBUM: "album",
|
||||
},
|
||||
blocking=True,
|
||||
)
|
||||
assert music_assistant_client.music.get_item_by_name.call_count == 1
|
||||
assert music_assistant_client.music.get_item_by_name.call_args == call(
|
||||
name="test",
|
||||
artist="artist",
|
||||
album="album",
|
||||
media_type=None,
|
||||
)
|
||||
assert music_assistant_client.send_command.call_count == 1
|
||||
assert music_assistant_client.send_command.call_args == call(
|
||||
"player_queues/play_media",
|
||||
queue_id=mass_player_id,
|
||||
media=[MOCK_TRACK.uri],
|
||||
option=None,
|
||||
radio_mode=False,
|
||||
start_item=None,
|
||||
)
|
||||
|
||||
|
||||
async def test_media_player_play_announcement_action(
|
||||
hass: HomeAssistant,
|
||||
music_assistant_client: MagicMock,
|
||||
) -> None:
|
||||
"""Test media_player play_announcement action."""
|
||||
await setup_integration_from_fixtures(hass, music_assistant_client)
|
||||
entity_id = "media_player.test_player_1"
|
||||
mass_player_id = "00:00:00:00:00:01"
|
||||
state = hass.states.get(entity_id)
|
||||
assert state
|
||||
await hass.services.async_call(
|
||||
MASS_DOMAIN,
|
||||
SERVICE_PLAY_ANNOUNCEMENT,
|
||||
{
|
||||
ATTR_ENTITY_ID: entity_id,
|
||||
ATTR_URL: "http://blah.com/announcement.mp3",
|
||||
ATTR_USE_PRE_ANNOUNCE: True,
|
||||
ATTR_ANNOUNCE_VOLUME: 50,
|
||||
},
|
||||
blocking=True,
|
||||
)
|
||||
assert music_assistant_client.send_command.call_count == 1
|
||||
assert music_assistant_client.send_command.call_args == call(
|
||||
"players/cmd/play_announcement",
|
||||
player_id=mass_player_id,
|
||||
url="http://blah.com/announcement.mp3",
|
||||
use_pre_announce=True,
|
||||
volume_level=50,
|
||||
)
|
||||
|
||||
|
||||
async def test_media_player_transfer_queue_action(
|
||||
hass: HomeAssistant,
|
||||
music_assistant_client: MagicMock,
|
||||
) -> None:
|
||||
"""Test media_player transfer_queu action."""
|
||||
await setup_integration_from_fixtures(hass, music_assistant_client)
|
||||
entity_id = "media_player.test_player_1"
|
||||
state = hass.states.get(entity_id)
|
||||
assert state
|
||||
await hass.services.async_call(
|
||||
MASS_DOMAIN,
|
||||
SERVICE_TRANSFER_QUEUE,
|
||||
{
|
||||
ATTR_ENTITY_ID: entity_id,
|
||||
ATTR_SOURCE_PLAYER: "media_player.my_super_test_player_2",
|
||||
ATTR_AUTO_PLAY: True,
|
||||
},
|
||||
blocking=True,
|
||||
)
|
||||
assert music_assistant_client.send_command.call_count == 1
|
||||
assert music_assistant_client.send_command.call_args == call(
|
||||
"player_queues/transfer",
|
||||
source_queue_id="00:00:00:00:00:02",
|
||||
target_queue_id="00:00:00:00:00:01",
|
||||
auto_play=True,
|
||||
require_schema=25,
|
||||
)
|
||||
# test again with invalid source player
|
||||
music_assistant_client.send_command.reset_mock()
|
||||
with pytest.raises(HomeAssistantError, match="Source player not available."):
|
||||
await hass.services.async_call(
|
||||
MASS_DOMAIN,
|
||||
SERVICE_TRANSFER_QUEUE,
|
||||
{
|
||||
ATTR_ENTITY_ID: entity_id,
|
||||
ATTR_SOURCE_PLAYER: "media_player.blah_blah",
|
||||
},
|
||||
blocking=True,
|
||||
)
|
||||
# test again with no source player specified (which picks first playing playerqueue)
|
||||
music_assistant_client.send_command.reset_mock()
|
||||
await hass.services.async_call(
|
||||
MASS_DOMAIN,
|
||||
SERVICE_TRANSFER_QUEUE,
|
||||
{
|
||||
ATTR_ENTITY_ID: entity_id,
|
||||
},
|
||||
blocking=True,
|
||||
)
|
||||
assert music_assistant_client.send_command.call_count == 1
|
||||
assert music_assistant_client.send_command.call_args == call(
|
||||
"player_queues/transfer",
|
||||
source_queue_id="test_group_player_1",
|
||||
target_queue_id="00:00:00:00:00:01",
|
||||
auto_play=None,
|
||||
require_schema=25,
|
||||
)
|
||||
|
|
Loading…
Reference in New Issue