Added Alexa Music
This commit is contained in:
@@ -0,0 +1,529 @@
|
||||
"""Actions for integration."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry, ConfigEntryState
|
||||
from homeassistant.core import (
|
||||
HomeAssistant,
|
||||
ServiceCall,
|
||||
ServiceResponse,
|
||||
SupportsResponse,
|
||||
callback,
|
||||
)
|
||||
from homeassistant.exceptions import ServiceValidationError
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
from music_assistant_models.errors import (
|
||||
InvalidCommand,
|
||||
MediaNotFoundError,
|
||||
ProviderUnavailableError,
|
||||
)
|
||||
|
||||
from .const import (
|
||||
ATTR_COMMAND,
|
||||
ATTR_DATA,
|
||||
ATTR_DURATION,
|
||||
ATTR_FAVORITE,
|
||||
ATTR_LIMIT,
|
||||
ATTR_LIMIT_AFTER,
|
||||
ATTR_LIMIT_BEFORE,
|
||||
ATTR_LOCAL_IMAGE_ENCODED,
|
||||
ATTR_MEDIA_ALBUM_NAME,
|
||||
ATTR_MEDIA_ARTIST,
|
||||
ATTR_MEDIA_CONTENT_ID,
|
||||
ATTR_MEDIA_IMAGE,
|
||||
ATTR_MEDIA_TITLE,
|
||||
ATTR_OFFSET,
|
||||
ATTR_PLAYER_ENTITY,
|
||||
ATTR_POSITION,
|
||||
ATTR_PROVIDERS,
|
||||
ATTR_QUEUE_ID,
|
||||
ATTR_QUEUE_ITEM_ID,
|
||||
ATTR_RELEASE_DATE,
|
||||
ATTR_VOLUME_LEVEL,
|
||||
CONF_DOWNLOAD_LOCAL,
|
||||
DEFAULT_QUEUE_ITEMS_LIMIT,
|
||||
DEFAULT_QUEUE_ITEMS_OFFSET,
|
||||
DOMAIN,
|
||||
LOGGER,
|
||||
SERVICE_GET_GROUP_VOLUME,
|
||||
SERVICE_GET_QUEUE_ITEMS,
|
||||
SERVICE_GET_RECOMMENDATIONS,
|
||||
SERVICE_MOVE_QUEUE_ITEM_DOWN,
|
||||
SERVICE_MOVE_QUEUE_ITEM_NEXT,
|
||||
SERVICE_MOVE_QUEUE_ITEM_UP,
|
||||
SERVICE_PLAY_QUEUE_ITEM,
|
||||
SERVICE_REMOVE_QUEUE_ITEM,
|
||||
SERVICE_SEND_COMMAND,
|
||||
SERVICE_SET_GROUP_VOLUME,
|
||||
)
|
||||
from .controller import MassQueueController
|
||||
from .schemas import (
|
||||
GET_GROUP_VOLUME_SERVICE_SCHEMA,
|
||||
GET_RECOMMENDATIONS_SERVICE_SCHEMA,
|
||||
MOVE_QUEUE_ITEM_DOWN_SERVICE_SCHEMA,
|
||||
MOVE_QUEUE_ITEM_NEXT_SERVICE_SCHEMA,
|
||||
MOVE_QUEUE_ITEM_UP_SERVICE_SCHEMA,
|
||||
PLAY_QUEUE_ITEM_SERVICE_SCHEMA,
|
||||
QUEUE_ITEM_SCHEMA,
|
||||
QUEUE_ITEMS_SERVICE_SCHEMA,
|
||||
REMOVE_QUEUE_ITEM_SERVICE_SCHEMA,
|
||||
SEND_COMMAND_SERVICE_SCHEMA,
|
||||
SET_GROUP_VOLUME_SERVICE_SCHEMA,
|
||||
TRACK_ITEM_SCHEMA,
|
||||
)
|
||||
from .utils import (
|
||||
find_image,
|
||||
parse_uri,
|
||||
)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from music_assistant_client import MusicAssistantClient
|
||||
|
||||
from . import MassQueueEntryData
|
||||
|
||||
|
||||
class MassQueueActions:
|
||||
"""Class to manage Music Assistant actions without passing `hass` and `mass_client` each time."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
hass: HomeAssistant,
|
||||
mass_client: MusicAssistantClient,
|
||||
config_entry: ConfigEntry,
|
||||
):
|
||||
"""Initialize class."""
|
||||
self._hass: HomeAssistant = hass
|
||||
self._client: MusicAssistantClient = mass_client
|
||||
self._controller = MassQueueController(self._hass, self._client, config_entry)
|
||||
self._config_entry = config_entry
|
||||
self._download_local = config_entry.options.get(CONF_DOWNLOAD_LOCAL)
|
||||
|
||||
def setup_controller(self):
|
||||
"""Setup Music Assistant controller."""
|
||||
self._controller.update_players()
|
||||
self._controller.subscribe_events()
|
||||
self._hass.loop.create_task(self._controller.update_queues())
|
||||
|
||||
@callback
|
||||
def register_actions(self) -> None:
|
||||
"""Register actions with Home Assistant."""
|
||||
self._hass.services.async_register(
|
||||
DOMAIN,
|
||||
SERVICE_GET_QUEUE_ITEMS,
|
||||
self.get_queue_items,
|
||||
schema=QUEUE_ITEMS_SERVICE_SCHEMA,
|
||||
supports_response=SupportsResponse.ONLY,
|
||||
)
|
||||
self._hass.services.async_register(
|
||||
DOMAIN,
|
||||
SERVICE_PLAY_QUEUE_ITEM,
|
||||
self.play_queue_item,
|
||||
schema=PLAY_QUEUE_ITEM_SERVICE_SCHEMA,
|
||||
supports_response=SupportsResponse.NONE,
|
||||
)
|
||||
|
||||
self._hass.services.async_register(
|
||||
DOMAIN,
|
||||
SERVICE_REMOVE_QUEUE_ITEM,
|
||||
self.remove_queue_item,
|
||||
schema=REMOVE_QUEUE_ITEM_SERVICE_SCHEMA,
|
||||
supports_response=SupportsResponse.NONE,
|
||||
)
|
||||
self._hass.services.async_register(
|
||||
DOMAIN,
|
||||
SERVICE_MOVE_QUEUE_ITEM_UP,
|
||||
self.move_queue_item_up,
|
||||
schema=MOVE_QUEUE_ITEM_UP_SERVICE_SCHEMA,
|
||||
supports_response=SupportsResponse.NONE,
|
||||
)
|
||||
self._hass.services.async_register(
|
||||
DOMAIN,
|
||||
SERVICE_MOVE_QUEUE_ITEM_DOWN,
|
||||
self.move_queue_item_down,
|
||||
schema=MOVE_QUEUE_ITEM_DOWN_SERVICE_SCHEMA,
|
||||
supports_response=SupportsResponse.NONE,
|
||||
)
|
||||
self._hass.services.async_register(
|
||||
DOMAIN,
|
||||
SERVICE_MOVE_QUEUE_ITEM_NEXT,
|
||||
self.move_queue_item_next,
|
||||
schema=MOVE_QUEUE_ITEM_NEXT_SERVICE_SCHEMA,
|
||||
supports_response=SupportsResponse.NONE,
|
||||
)
|
||||
self._hass.services.async_register(
|
||||
DOMAIN,
|
||||
SERVICE_SEND_COMMAND,
|
||||
self.send_command,
|
||||
schema=SEND_COMMAND_SERVICE_SCHEMA,
|
||||
supports_response=SupportsResponse.OPTIONAL,
|
||||
)
|
||||
self._hass.services.async_register(
|
||||
DOMAIN,
|
||||
SERVICE_GET_RECOMMENDATIONS,
|
||||
self.get_recommendations,
|
||||
schema=GET_RECOMMENDATIONS_SERVICE_SCHEMA,
|
||||
supports_response=SupportsResponse.ONLY,
|
||||
)
|
||||
self._hass.services.async_register(
|
||||
DOMAIN,
|
||||
SERVICE_GET_GROUP_VOLUME,
|
||||
self.get_group_volume,
|
||||
schema=GET_GROUP_VOLUME_SERVICE_SCHEMA,
|
||||
supports_response=SupportsResponse.ONLY,
|
||||
)
|
||||
self._hass.services.async_register(
|
||||
DOMAIN,
|
||||
SERVICE_SET_GROUP_VOLUME,
|
||||
self.set_group_volume,
|
||||
schema=SET_GROUP_VOLUME_SERVICE_SCHEMA,
|
||||
supports_response=SupportsResponse.NONE,
|
||||
)
|
||||
|
||||
def get_queue_id(self, entity_id: str):
|
||||
"""Get the queue ID for a player."""
|
||||
return self._hass.states.get(entity_id).attributes[ATTR_QUEUE_ID]
|
||||
|
||||
async def get_queue_index(self, entity_id: str):
|
||||
"""Get the current index of the queue."""
|
||||
active_queue = await self.get_active_queue(entity_id)
|
||||
try:
|
||||
return active_queue.current_index or 0
|
||||
except AttributeError:
|
||||
return 0
|
||||
|
||||
async def get_active_queue(self, entity_id: str):
|
||||
"""Get active queue details."""
|
||||
queue_id = self.get_queue_id(entity_id)
|
||||
return await self._client.player_queues.get_active_queue(queue_id)
|
||||
|
||||
async def _format_queue_item(self, queue_item: dict) -> dict:
|
||||
"""Format list of queue items for response."""
|
||||
media = queue_item["media_item"]
|
||||
|
||||
queue_item_id = queue_item["queue_item_id"]
|
||||
media_title = media["name"]
|
||||
media_album = media.get("album")
|
||||
media_album_name = "" if media_album is None else media_album.get("name", "")
|
||||
media_content_id = media["uri"]
|
||||
media_image = find_image(queue_item) or ""
|
||||
local_image_encoded = queue_item.get(ATTR_LOCAL_IMAGE_ENCODED)
|
||||
favorite = media["favorite"]
|
||||
|
||||
artists = media["artists"]
|
||||
artist_names = [artist["name"] for artist in artists]
|
||||
media_artist = ", ".join(artist_names)
|
||||
response: ServiceResponse = QUEUE_ITEM_SCHEMA(
|
||||
{
|
||||
ATTR_QUEUE_ITEM_ID: queue_item_id,
|
||||
ATTR_MEDIA_TITLE: media_title,
|
||||
ATTR_MEDIA_ALBUM_NAME: media_album_name,
|
||||
ATTR_MEDIA_ARTIST: media_artist,
|
||||
ATTR_MEDIA_CONTENT_ID: media_content_id,
|
||||
ATTR_MEDIA_IMAGE: media_image,
|
||||
ATTR_FAVORITE: favorite,
|
||||
},
|
||||
)
|
||||
if local_image_encoded:
|
||||
response[ATTR_LOCAL_IMAGE_ENCODED] = local_image_encoded
|
||||
return response
|
||||
|
||||
async def send_command(self, call: ServiceCall) -> ServiceResponse:
|
||||
"""Sends command to Music Assistant and returns response."""
|
||||
command = call.data[ATTR_COMMAND]
|
||||
data = call.data.get(ATTR_DATA)
|
||||
response = await self._controller.send_command(command, data)
|
||||
return {"response": response}
|
||||
|
||||
async def get_recommendations(self, call: ServiceCall) -> ServiceResponse:
|
||||
"""Pulls all recommendations for the providers given."""
|
||||
providers = call.data.get(ATTR_PROVIDERS)
|
||||
return await self._controller.get_recommendations(providers)
|
||||
|
||||
async def get_group_volume(self, call: ServiceCall) -> ServiceResponse:
|
||||
"""Gets the group volume for a single player."""
|
||||
entity_id = call.data.get(ATTR_PLAYER_ENTITY)
|
||||
queue_id = self.get_queue_id(entity_id)
|
||||
try:
|
||||
volume = await self._controller.get_grouped_volume(queue_id)
|
||||
except: # noqa: E722
|
||||
volume = None
|
||||
return volume
|
||||
|
||||
async def set_group_volume(self, call: ServiceCall) -> ServiceResponse:
|
||||
"""Sets the group volume for a player."""
|
||||
entity_id = call.data.get(ATTR_PLAYER_ENTITY)
|
||||
queue_id = self.get_queue_id(entity_id)
|
||||
volume_level = call.data.get(ATTR_VOLUME_LEVEL)
|
||||
await self._controller.set_grouped_volume(queue_id, volume_level)
|
||||
|
||||
async def get_queue_items(self, call: ServiceCall) -> ServiceResponse:
|
||||
"""Get all items in queue."""
|
||||
entity_id = call.data[ATTR_PLAYER_ENTITY]
|
||||
queue_id = self.get_queue_id(entity_id)
|
||||
if queue_id is None:
|
||||
return {entity_id: []}
|
||||
offset = call.data.get(ATTR_OFFSET)
|
||||
limit = call.data.get(ATTR_LIMIT)
|
||||
limit_before = call.data.get(ATTR_LIMIT_BEFORE)
|
||||
limit_after = call.data.get(ATTR_LIMIT_AFTER)
|
||||
idx = await self.get_queue_index(entity_id)
|
||||
if limit_before:
|
||||
offset = idx - limit_before
|
||||
if limit_after:
|
||||
limit = limit_before + limit_after + 1 if limit_before else limit_after + 1
|
||||
if offset is None:
|
||||
offset = idx + DEFAULT_QUEUE_ITEMS_OFFSET
|
||||
if limit is None:
|
||||
limit = DEFAULT_QUEUE_ITEMS_LIMIT
|
||||
offset = max(offset, 0)
|
||||
queue_items = await self._controller.player_queue(queue_id, limit, offset)
|
||||
response: ServiceResponse = {
|
||||
entity_id: [await self._format_queue_item(item) for item in queue_items],
|
||||
}
|
||||
return response
|
||||
|
||||
async def play_queue_item(self, call: ServiceCall) -> ServiceResponse:
|
||||
"""Play selected item in queue."""
|
||||
entity_id = call.data[ATTR_PLAYER_ENTITY]
|
||||
queue_item_id = call.data[ATTR_QUEUE_ITEM_ID]
|
||||
queue_id = self.get_queue_id(entity_id)
|
||||
await self._client.send_command(
|
||||
"player_queues/play_index",
|
||||
queue_id=queue_id,
|
||||
index=queue_item_id,
|
||||
)
|
||||
|
||||
async def remove_queue_item(self, call: ServiceCall) -> ServiceResponse:
|
||||
"""Remove selected item from queue."""
|
||||
entity_id = call.data[ATTR_PLAYER_ENTITY]
|
||||
queue_item_id = call.data[ATTR_QUEUE_ITEM_ID]
|
||||
queue_id = self.get_queue_id(entity_id)
|
||||
await self._client.player_queues.queue_command_delete(queue_id, queue_item_id)
|
||||
|
||||
async def move_queue_item_up(self, call: ServiceCall) -> ServiceResponse:
|
||||
"""Move selected item up in queue."""
|
||||
entity_id = call.data[ATTR_PLAYER_ENTITY]
|
||||
queue_item_id = call.data[ATTR_QUEUE_ITEM_ID]
|
||||
queue_id = self.get_queue_id(entity_id)
|
||||
await self._client.player_queues.queue_command_move_up(queue_id, queue_item_id)
|
||||
|
||||
async def move_queue_item_down(self, call: ServiceCall) -> ServiceResponse:
|
||||
"""Move selected item down in queue."""
|
||||
entity_id = call.data[ATTR_PLAYER_ENTITY]
|
||||
queue_item_id = call.data[ATTR_QUEUE_ITEM_ID]
|
||||
queue_id = self.get_queue_id(entity_id)
|
||||
await self._client.player_queues.queue_command_move_down(
|
||||
queue_id,
|
||||
queue_item_id,
|
||||
)
|
||||
|
||||
async def move_queue_item_next(self, call: ServiceCall) -> ServiceResponse:
|
||||
"""Move selected item next in queue."""
|
||||
entity_id = call.data[ATTR_PLAYER_ENTITY]
|
||||
queue_item_id = call.data[ATTR_QUEUE_ITEM_ID]
|
||||
queue_id = self.get_queue_id(entity_id)
|
||||
await self._client.player_queues.queue_command_move_next(
|
||||
queue_id,
|
||||
queue_item_id,
|
||||
)
|
||||
|
||||
async def unfavorite_item(self, call: ServiceCall) -> ServiceResponse:
|
||||
"""Unfavorites currently playing item in queue."""
|
||||
entity_id = call.data[ATTR_PLAYER_ENTITY]
|
||||
attrs = self._hass.states.get(entity_id).attributes
|
||||
content_id = attrs.get(ATTR_MEDIA_CONTENT_ID)
|
||||
if not content_id:
|
||||
msg = f"Cannot find media with content id {content_id}"
|
||||
raise MediaNotFoundError(msg)
|
||||
provider = content_id.split("://")[0]
|
||||
if provider != "library":
|
||||
msg = f"Unfavorite can only apply to library media items, not from provider {provider}"
|
||||
raise InvalidCommand(msg)
|
||||
item_id = str(content_id.split("/")[-1])
|
||||
await self._client.send_command(
|
||||
"music/favorites/remove_item",
|
||||
media_type="track",
|
||||
library_item_id=item_id,
|
||||
)
|
||||
|
||||
async def get_artist_details(self, artist_uri):
|
||||
"""Retrieves the details for an artist."""
|
||||
provider, item_id = parse_uri(artist_uri)
|
||||
LOGGER.debug(f"Getting artist details for provider {provider}")
|
||||
return await self._client.music.get_artist(item_id, provider)
|
||||
|
||||
async def get_album_details(self, album_uri):
|
||||
"""Retrieves the details for an album."""
|
||||
provider, item_id = parse_uri(album_uri)
|
||||
LOGGER.debug(f"Getting album details for provider {provider}")
|
||||
return await self._client.music.get_album(item_id, provider)
|
||||
|
||||
async def get_playlist_details(self, playlist_uri):
|
||||
"""Retrieves the details for a playlist."""
|
||||
provider, item_id = parse_uri(playlist_uri)
|
||||
LOGGER.debug(f"Getting album details for provider {provider}")
|
||||
return await self._client.music.get_playlist(item_id, provider)
|
||||
|
||||
async def get_podcast_details(self, podcast_uri):
|
||||
"""Retrieves the details for a podcast."""
|
||||
provider, item_id = parse_uri(podcast_uri)
|
||||
LOGGER.debug(f"Getting podcast details for provider {provider}")
|
||||
return await self._client.music.get_podcast(item_id, provider)
|
||||
|
||||
async def get_artist_tracks(self, artist_uri: str, page: int | None = None):
|
||||
"""Retrieves a limited number of tracks from an artist."""
|
||||
details = await self.get_artist_details(artist_uri)
|
||||
mappings = list(details.provider_mappings)
|
||||
if not len(mappings) > 0:
|
||||
msg = f"URI {artist_uri} returned no results!"
|
||||
raise ProviderUnavailableError(msg)
|
||||
mapping = mappings[0]
|
||||
item_id = mapping.item_id
|
||||
provider = mapping.provider_domain
|
||||
resp = (
|
||||
await self._client.music.get_artist_tracks(item_id, provider)
|
||||
if not page
|
||||
else await self._client.music.get_artist_tracks(item_id, provider, page)
|
||||
)
|
||||
return [self.format_track_item(item.to_dict()) for item in resp]
|
||||
|
||||
async def get_album_tracks(self, album_uri: str, page: int | None = None):
|
||||
"""Retrieves all tracks from an album."""
|
||||
details = await self.get_album_details(album_uri)
|
||||
mappings = list(details.provider_mappings)
|
||||
if not len(mappings) > 0:
|
||||
msg = f"URI {album_uri} returned no results!"
|
||||
raise ProviderUnavailableError(msg)
|
||||
mapping = mappings[0]
|
||||
item_id = mapping.item_id
|
||||
provider = mapping.provider_domain
|
||||
resp = (
|
||||
await self._client.music.get_album_tracks(item_id, provider)
|
||||
if not page
|
||||
else await self._client.music.get_album_tracks(item_id, provider, page)
|
||||
)
|
||||
return [self.format_track_item(item.to_dict()) for item in resp]
|
||||
|
||||
async def get_podcast_episodes(self, podcast_uri):
|
||||
"""Retrieves all episodes for a podcast."""
|
||||
provider, item_id = parse_uri(podcast_uri)
|
||||
LOGGER.debug(
|
||||
f"Getting podcast episodes for provider {provider}, item_id {item_id}",
|
||||
)
|
||||
resp: list = await self._client.music.get_podcast_episodes(item_id, provider)
|
||||
formatted = [self.format_podcast_episode(item.to_dict()) for item in resp]
|
||||
formatted.sort(key=lambda x: x[ATTR_RELEASE_DATE], reverse=True)
|
||||
return formatted
|
||||
|
||||
async def get_playlist_tracks(self, playlist_uri: str, page: int | None = None):
|
||||
"""Retrieves all playlist items."""
|
||||
provider, item_id = parse_uri(playlist_uri)
|
||||
LOGGER.debug(
|
||||
f"Getting playlist items for provider {provider}, item_id {item_id}",
|
||||
)
|
||||
resp = (
|
||||
await self._client.music.get_playlist_tracks(item_id, provider)
|
||||
if not page
|
||||
else await self._client.music.get_playlist_tracks(item_id, provider, page)
|
||||
)
|
||||
return [self.format_playlist_track(item.to_dict()) for item in resp]
|
||||
|
||||
def format_playlist_track(self, playlist_track: dict) -> TRACK_ITEM_SCHEMA:
|
||||
"""Processes individual playlist tracks using format_track_item and adds position."""
|
||||
result = self.format_track_item(playlist_track)
|
||||
result[ATTR_POSITION] = playlist_track["position"]
|
||||
return result
|
||||
|
||||
def format_track_item(self, track_item: dict) -> TRACK_ITEM_SCHEMA:
|
||||
"""Process an individual track item."""
|
||||
result = self.format_item(track_item)
|
||||
media_album = track_item.get("album")
|
||||
media_album_name = "" if media_album is None else media_album.get("name", "")
|
||||
artists = track_item["artists"]
|
||||
artist_names = [artist["name"] for artist in artists]
|
||||
media_artist = ", ".join(artist_names)
|
||||
result[ATTR_MEDIA_ALBUM_NAME] = media_album_name
|
||||
result[ATTR_MEDIA_ARTIST] = media_artist
|
||||
return result
|
||||
|
||||
def format_podcast_episode(self, podcast_episode: dict) -> TRACK_ITEM_SCHEMA:
|
||||
"""Process an individual track item."""
|
||||
result = self.format_item(podcast_episode)
|
||||
result[ATTR_RELEASE_DATE] = podcast_episode.get("metadata", {}).get(
|
||||
"release_date",
|
||||
)
|
||||
return result
|
||||
|
||||
def format_item(self, media_item: dict) -> TRACK_ITEM_SCHEMA:
|
||||
"""Processes the individual items in a playlist."""
|
||||
media_title = media_item.get("name") or "N/A"
|
||||
media_content_id = media_item["uri"]
|
||||
media_image = find_image(media_item) or ""
|
||||
local_image_encoded = media_item.get(ATTR_LOCAL_IMAGE_ENCODED)
|
||||
favorite = media_item["favorite"]
|
||||
duration = media_item["duration"] or 0
|
||||
response: ServiceResponse = TRACK_ITEM_SCHEMA(
|
||||
{
|
||||
ATTR_MEDIA_TITLE: media_title,
|
||||
ATTR_MEDIA_CONTENT_ID: media_content_id,
|
||||
ATTR_DURATION: duration,
|
||||
ATTR_MEDIA_IMAGE: media_image,
|
||||
ATTR_FAVORITE: favorite,
|
||||
},
|
||||
)
|
||||
if local_image_encoded:
|
||||
response[ATTR_LOCAL_IMAGE_ENCODED] = local_image_encoded
|
||||
return response
|
||||
|
||||
async def remove_playlist_tracks(
|
||||
self,
|
||||
playlist_id: str | int,
|
||||
positions_to_remove: list[int],
|
||||
):
|
||||
"""Removes one or more items from a playlist."""
|
||||
await self._client.music.remove_playlist_tracks(
|
||||
playlist_id,
|
||||
positions_to_remove,
|
||||
)
|
||||
|
||||
|
||||
@callback
|
||||
def get_music_assistant_client(
|
||||
hass: HomeAssistant,
|
||||
entity_id: str,
|
||||
) -> MusicAssistantClient:
|
||||
"""Get Music Assistant client from entity_id."""
|
||||
registry = er.async_get(hass)
|
||||
entity = registry.async_get(entity_id)
|
||||
config_entry_id = entity.config_entry_id
|
||||
return _get_music_assistant_client(hass, config_entry_id)
|
||||
|
||||
|
||||
@callback
|
||||
def _get_music_assistant_client(
|
||||
hass: HomeAssistant,
|
||||
config_entry_id: str,
|
||||
) -> MusicAssistantClient:
|
||||
"""Get Music Assistant Client from config_entry_id."""
|
||||
entry: MassQueueEntryData | None
|
||||
if not (entry := hass.config_entries.async_get_entry(config_entry_id)):
|
||||
exc = "Entry not found."
|
||||
raise ServiceValidationError(exc)
|
||||
if entry.state is not ConfigEntryState.LOADED:
|
||||
exc = "Entry not loaded"
|
||||
raise ServiceValidationError(exc)
|
||||
return entry.runtime_data.mass
|
||||
|
||||
|
||||
@callback
|
||||
async def setup_controller_and_actions(
|
||||
hass: HomeAssistant,
|
||||
mass_client: MusicAssistantClient,
|
||||
entry: ConfigEntry,
|
||||
) -> MassQueueActions:
|
||||
"""Initialize client and actions class, add actions to Home Assistant."""
|
||||
actions = MassQueueActions(hass, mass_client, entry)
|
||||
actions.setup_controller()
|
||||
return actions
|
||||
Reference in New Issue
Block a user