1
0
mirror of https://github.com/home-assistant/core.git synced 2025-12-26 14:08:21 +00:00

Register music assistant services in async setup (#155963)

This commit is contained in:
Artur Pragacz
2025-11-18 11:38:58 +01:00
committed by GitHub
parent 97b7e51171
commit c32a471573
5 changed files with 101 additions and 84 deletions

View File

@@ -27,7 +27,8 @@ from homeassistant.helpers.issue_registry import (
)
from .const import ATTR_CONF_EXPOSE_PLAYER_TO_HA, DOMAIN, LOGGER
from .services import get_music_assistant_client, register_actions
from .helpers import get_music_assistant_client
from .services import register_actions
if TYPE_CHECKING:
from music_assistant_models.event import MassEvent

View File

@@ -4,11 +4,18 @@ from __future__ import annotations
from collections.abc import Callable, Coroutine
import functools
from typing import Any
from typing import TYPE_CHECKING, Any
from music_assistant_models.errors import MusicAssistantError
from homeassistant.exceptions import HomeAssistantError
from homeassistant.config_entries import ConfigEntryState
from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import HomeAssistantError, ServiceValidationError
if TYPE_CHECKING:
from music_assistant_client import MusicAssistantClient
from . import MusicAssistantConfigEntry
def catch_musicassistant_error[**_P, _R](
@@ -26,3 +33,16 @@ def catch_musicassistant_error[**_P, _R](
raise HomeAssistantError(error_msg) from err
return wrapper
@callback
def get_music_assistant_client(
hass: HomeAssistant, config_entry_id: str
) -> MusicAssistantClient:
"""Get the Music Assistant client for the given config entry."""
entry: MusicAssistantConfigEntry | None
if not (entry := hass.config_entries.async_get_entry(config_entry_id)):
raise ServiceValidationError("Entry not found")
if entry.state is not ConfigEntryState.LOADED:
raise ServiceValidationError("Entry not loaded")
return entry.runtime_data.mass

View File

@@ -22,11 +22,9 @@ from music_assistant_models.errors import MediaNotFoundError
from music_assistant_models.event import MassEvent
from music_assistant_models.media_items import ItemMapping, MediaItemType, Track
from music_assistant_models.player_queue import PlayerQueue
import voluptuous as vol
from homeassistant.components import media_source
from homeassistant.components.media_player import (
ATTR_MEDIA_ENQUEUE,
ATTR_MEDIA_EXTRA,
BrowseMedia,
MediaPlayerDeviceClass,
@@ -41,38 +39,26 @@ from homeassistant.components.media_player import (
async_process_play_media_url,
)
from homeassistant.const import ATTR_NAME, STATE_OFF, Platform
from homeassistant.core import HomeAssistant, ServiceResponse, SupportsResponse
from homeassistant.core import HomeAssistant, ServiceResponse
from homeassistant.exceptions import HomeAssistantError, ServiceValidationError
from homeassistant.helpers import config_validation as cv, entity_registry as er
from homeassistant.helpers.entity_platform import (
AddConfigEntryEntitiesCallback,
async_get_current_platform,
)
from homeassistant.helpers import entity_registry as er
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from homeassistant.util.dt import utc_from_timestamp
from . import MusicAssistantConfigEntry
from .const import (
ATTR_ACTIVE,
ATTR_ACTIVE_QUEUE,
ATTR_ALBUM,
ATTR_ANNOUNCE_VOLUME,
ATTR_ARTIST,
ATTR_AUTO_PLAY,
ATTR_CURRENT_INDEX,
ATTR_CURRENT_ITEM,
ATTR_ELAPSED_TIME,
ATTR_ITEMS,
ATTR_MASS_PLAYER_TYPE,
ATTR_MEDIA_ID,
ATTR_MEDIA_TYPE,
ATTR_NEXT_ITEM,
ATTR_QUEUE_ID,
ATTR_RADIO_MODE,
ATTR_REPEAT_MODE,
ATTR_SHUFFLE_ENABLED,
ATTR_SOURCE_PLAYER,
ATTR_URL,
ATTR_USE_PRE_ANNOUNCE,
DOMAIN,
)
from .entity import MusicAssistantEntity
@@ -122,11 +108,6 @@ REPEAT_MODE_MAPPING_TO_HA = {
# UNKNOWN is intentionally not mapped - will return None
}
SERVICE_PLAY_MEDIA_ADVANCED = "play_media"
SERVICE_PLAY_ANNOUNCEMENT = "play_announcement"
SERVICE_TRANSFER_QUEUE = "transfer_queue"
SERVICE_GET_QUEUE = "get_queue"
async def async_setup_entry(
hass: HomeAssistant,
@@ -143,44 +124,6 @@ async def async_setup_entry(
# register callback to add players when they are discovered
entry.runtime_data.platform_handlers.setdefault(Platform.MEDIA_PLAYER, add_player)
# 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",
)
platform.async_register_entity_service(
SERVICE_GET_QUEUE,
schema=None,
func="_async_handle_get_queue",
supports_response=SupportsResponse.ONLY,
)
class MusicAssistantPlayer(MusicAssistantEntity, MediaPlayerEntity):
"""Representation of MediaPlayerEntity from Music Assistant Player."""

View File

@@ -4,10 +4,13 @@ from __future__ import annotations
from typing import TYPE_CHECKING
from music_assistant_models.enums import MediaType
from music_assistant_models.enums import MediaType, QueueOption
import voluptuous as vol
from homeassistant.config_entries import ConfigEntryState
from homeassistant.components.media_player import (
ATTR_MEDIA_ENQUEUE,
DOMAIN as MEDIA_PLAYER_DOMAIN,
)
from homeassistant.const import ATTR_CONFIG_ENTRY_ID
from homeassistant.core import (
HomeAssistant,
@@ -17,31 +20,41 @@ from homeassistant.core import (
callback,
)
from homeassistant.exceptions import ServiceValidationError
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers import config_validation as cv, service
from .const import (
ATTR_ALBUM,
ATTR_ALBUM_ARTISTS_ONLY,
ATTR_ALBUM_TYPE,
ATTR_ALBUMS,
ATTR_ANNOUNCE_VOLUME,
ATTR_ARTIST,
ATTR_ARTISTS,
ATTR_AUDIOBOOKS,
ATTR_AUTO_PLAY,
ATTR_FAVORITE,
ATTR_ITEMS,
ATTR_LIBRARY_ONLY,
ATTR_LIMIT,
ATTR_MEDIA_ID,
ATTR_MEDIA_TYPE,
ATTR_OFFSET,
ATTR_ORDER_BY,
ATTR_PLAYLISTS,
ATTR_PODCASTS,
ATTR_RADIO,
ATTR_RADIO_MODE,
ATTR_SEARCH,
ATTR_SEARCH_ALBUM,
ATTR_SEARCH_ARTIST,
ATTR_SEARCH_NAME,
ATTR_SOURCE_PLAYER,
ATTR_TRACKS,
ATTR_URL,
ATTR_USE_PRE_ANNOUNCE,
DOMAIN,
)
from .helpers import get_music_assistant_client
from .schemas import (
LIBRARY_RESULTS_SCHEMA,
SEARCH_RESULT_SCHEMA,
@@ -49,7 +62,6 @@ from .schemas import (
)
if TYPE_CHECKING:
from music_assistant_client import MusicAssistantClient
from music_assistant_models.media_items import (
Album,
Artist,
@@ -60,28 +72,18 @@ if TYPE_CHECKING:
Track,
)
from . import MusicAssistantConfigEntry
SERVICE_SEARCH = "search"
SERVICE_GET_LIBRARY = "get_library"
SERVICE_PLAY_MEDIA_ADVANCED = "play_media"
SERVICE_PLAY_ANNOUNCEMENT = "play_announcement"
SERVICE_TRANSFER_QUEUE = "transfer_queue"
SERVICE_GET_QUEUE = "get_queue"
DEFAULT_OFFSET = 0
DEFAULT_LIMIT = 25
DEFAULT_SORT_ORDER = "name"
@callback
def get_music_assistant_client(
hass: HomeAssistant, config_entry_id: str
) -> MusicAssistantClient:
"""Get the Music Assistant client for the given config entry."""
entry: MusicAssistantConfigEntry | None
if not (entry := hass.config_entries.async_get_entry(config_entry_id)):
raise ServiceValidationError("Entry not found")
if entry.state is not ConfigEntryState.LOADED:
raise ServiceValidationError("Entry not loaded")
return entry.runtime_data.mass
@callback
def register_actions(hass: HomeAssistant) -> None:
"""Register custom actions."""
@@ -124,6 +126,55 @@ def register_actions(hass: HomeAssistant) -> None:
supports_response=SupportsResponse.ONLY,
)
# Platform entity services
service.async_register_platform_entity_service(
hass,
DOMAIN,
SERVICE_PLAY_MEDIA_ADVANCED,
entity_domain=MEDIA_PLAYER_DOMAIN,
schema={
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),
},
func="_async_handle_play_media",
)
service.async_register_platform_entity_service(
hass,
DOMAIN,
SERVICE_PLAY_ANNOUNCEMENT,
entity_domain=MEDIA_PLAYER_DOMAIN,
schema={
vol.Required(ATTR_URL): cv.string,
vol.Optional(ATTR_USE_PRE_ANNOUNCE): vol.Coerce(bool),
vol.Optional(ATTR_ANNOUNCE_VOLUME): vol.Coerce(int),
},
func="_async_handle_play_announcement",
)
service.async_register_platform_entity_service(
hass,
DOMAIN,
SERVICE_TRANSFER_QUEUE,
entity_domain=MEDIA_PLAYER_DOMAIN,
schema={
vol.Optional(ATTR_SOURCE_PLAYER): cv.entity_id,
vol.Optional(ATTR_AUTO_PLAY): vol.Coerce(bool),
},
func="_async_handle_transfer_queue",
)
service.async_register_platform_entity_service(
hass,
DOMAIN,
SERVICE_GET_QUEUE,
entity_domain=MEDIA_PLAYER_DOMAIN,
schema=None,
func="_async_handle_get_queue",
supports_response=SupportsResponse.ONLY,
)
async def handle_search(call: ServiceCall) -> ServiceResponse:
"""Handle queue_command action."""

View File

@@ -30,8 +30,7 @@ from homeassistant.components.media_player import (
SERVICE_UNJOIN,
MediaPlayerEntityFeature,
)
from homeassistant.components.music_assistant.const import DOMAIN
from homeassistant.components.music_assistant.media_player import (
from homeassistant.components.music_assistant.const import (
ATTR_ALBUM,
ATTR_ANNOUNCE_VOLUME,
ATTR_ARTIST,
@@ -42,6 +41,9 @@ from homeassistant.components.music_assistant.media_player import (
ATTR_SOURCE_PLAYER,
ATTR_URL,
ATTR_USE_PRE_ANNOUNCE,
DOMAIN,
)
from homeassistant.components.music_assistant.services import (
SERVICE_GET_QUEUE,
SERVICE_PLAY_ANNOUNCEMENT,
SERVICE_PLAY_MEDIA_ADVANCED,