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:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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."""
|
||||
|
||||
@@ -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."""
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user