from music_assistant.helpers.api import api_command
from music_assistant.helpers.json import JSON_DECODE_EXCEPTIONS, async_json_dumps, async_json_loads
from music_assistant.helpers.util import load_provider_module
+from music_assistant.models import ProviderModuleType
if TYPE_CHECKING:
import asyncio
return val
@api_command("config/providers/get_entries")
- async def get_provider_config_entries(
+ async def get_provider_config_entries( # noqa: PLR0915
self,
provider_domain: str,
instance_id: str | None = None,
values: the (intermediate) raw values for config entries sent with the action.
"""
# lookup provider manifest and module
+ prov_mod: ProviderModuleType | None
for manifest in self.mass.get_provider_manifests():
if manifest.domain == provider_domain:
- prov_mod = await load_provider_module(provider_domain, manifest.requirements)
+ try:
+ prov_mod = await load_provider_module(provider_domain, manifest.requirements)
+ except Exception as e:
+ msg = f"Failed to load provider module for {provider_domain}: {e}"
+ LOGGER.exception(msg)
+ return []
break
else:
msg = f"Unknown provider domain: {provider_domain}"
- raise KeyError(msg)
+ LOGGER.exception(msg)
+ return []
+
if values is None:
values = self.get(f"{CONF_PROVIDERS}/{instance_id}/values", {}) if instance_id else {}
]
@api_command("config/players/get")
- async def get_player_config(self, player_id: str) -> PlayerConfig:
+ async def get_player_config(
+ self,
+ player_id: str,
+ action: str | None = None,
+ values: dict[str, ConfigValueType] | None = None,
+ ) -> PlayerConfig:
"""Return (full) configuration for a single player."""
raw_conf: dict[str, Any]
if raw_conf := self.get(f"{CONF_PLAYERS}/{player_id}"):
if player := self.mass.players.get(player_id, False):
raw_conf["default_name"] = player.display_name
raw_conf["provider"] = player.provider.lookup_key
- conf_entries = await player.get_config_entries()
+ # pass action and values to get_config_entries
+ if values is None:
+ values = raw_conf.get("values", {})
+ conf_entries = await player.get_config_entries(action=action, values=values)
else:
# handle unavailable player and/or provider
conf_entries = []
msg = f"No config found for player id {player_id}"
raise KeyError(msg)
+ @api_command("config/players/get_entries")
+ async def get_player_config_entries(
+ self,
+ player_id: str,
+ action: str | None = None,
+ values: dict[str, ConfigValueType] | None = None,
+ ) -> list[ConfigEntry]:
+ """
+ Return Config entries to configure a player.
+
+ player_id: id of an existing player instance.
+ action: [optional] action key called from config entries UI.
+ values: the (intermediate) raw values for config entries sent with the action.
+ """
+ if not (player := self.mass.players.get(player_id, False)):
+ msg = f"Player {player_id} not found"
+ raise KeyError(msg)
+
+ if values is None:
+ values = self.get(f"{CONF_PLAYERS}/{player_id}/values", {})
+
+ return await player.get_config_entries(action=action, values=values)
+
@api_command("config/players/get_value")
async def get_player_config_value(
self,
from typing import TYPE_CHECKING, cast
import shortuuid
-from music_assistant_models.config_entries import ConfigEntry, ConfigValueOption
+from music_assistant_models.config_entries import ConfigEntry, ConfigValueOption, ConfigValueType
from music_assistant_models.constants import PLAYER_CONTROL_NONE
from music_assistant_models.enums import (
ConfigEntryType,
else:
return set()
- async def get_config_entries(self) -> list[ConfigEntry]:
+ async def get_config_entries(
+ self,
+ action: str | None = None,
+ values: dict[str, ConfigValueType] | None = None,
+ ) -> list[ConfigEntry]:
"""Return all (provider/player specific) Config Entries for the given player (if any)."""
entries: list[ConfigEntry] = [
# default entries for player groups
- *await super().get_config_entries(),
+ *await super().get_config_entries(action=action, values=values),
# add syncgroup specific entries
ConfigEntry(
key=CONF_GROUP_MEMBERS,
from copy import deepcopy
from typing import TYPE_CHECKING, Any, cast, final
-from music_assistant_models.config_entries import ConfigEntry, ConfigValueOption, PlayerConfig
+from music_assistant_models.config_entries import (
+ ConfigEntry,
+ ConfigValueOption,
+ ConfigValueType,
+ PlayerConfig,
+)
from music_assistant_models.constants import (
PLAYER_CONTROL_FAKE,
PLAYER_CONTROL_NATIVE,
async def get_config_entries(
self,
+ action: str | None = None,
+ values: dict[str, ConfigValueType] | None = None,
) -> list[ConfigEntry]:
- """Return all (provider/player specific) Config Entries for the player."""
+ """Return all (provider/player specific) Config Entries for the player.
+
+ action: [optional] action key called from config entries UI.
+ values: the (intermediate) raw values for config entries sent with the action.
+ """
# Return all base config entries for a player.
# Feel free to override but ensure to include the base entries by calling super() first.
# To override the default config entries, simply define an entry with the same key
# default implementation: groups can't be synced
return None
- async def get_config_entries(self) -> list[ConfigEntry]:
- """Return all (provider/player specific) Config Entries for the player."""
+ async def get_config_entries(
+ self,
+ action: str | None = None,
+ values: dict[str, ConfigValueType] | None = None,
+ ) -> list[ConfigEntry]:
+ """Return all (provider/player specific) Config Entries for the player.
+
+ action: [optional] action key called from config entries UI.
+ values: the (intermediate) raw values for config entries sent with the action.
+ """
# Return all base config entries for a group player.
# Feel free to override but ensure to include the base entries by calling super() first.
# To override the default config entries, simply define an entry with the same key
from typing import TYPE_CHECKING
-from music_assistant_models.config_entries import ConfigEntry
+from music_assistant_models.config_entries import ConfigEntry, ConfigValueType
from music_assistant_models.enums import ConfigEntryType, PlaybackState, PlayerFeature, PlayerType
from music_assistant_models.player import PlayerSource
),
]
- async def get_config_entries(self) -> list[ConfigEntry]:
+ async def get_config_entries(
+ self,
+ action: str | None = None,
+ values: dict[str, ConfigValueType] | None = None,
+ ) -> list[ConfigEntry]:
"""Return all (provider/player specific) Config Entries for the player."""
# OPTIONAL
# this method is optional and should be implemented if you need player specific
# configuration entries. If you do not need player specific configuration entries,
# you can leave this method out completely to accept the default implementation.
# Please note that you need to call the super() method to get the default entries.
- default_entries = await super().get_config_entries()
+ default_entries = await super().get_config_entries(action=action, values=values)
return [
*default_entries,
# example of a player specific config entry
import time
from typing import TYPE_CHECKING, cast
-from music_assistant_models.config_entries import ConfigEntry
+from music_assistant_models.config_entries import ConfigEntry, ConfigValueType
from music_assistant_models.enums import (
ConfigEntryType,
ContentType,
self._attr_can_group_with = {provider.lookup_key}
self._attr_enabled_by_default = not is_broken_raop_model(manufacturer, model)
- async def get_config_entries(self) -> list[ConfigEntry]:
+ async def get_config_entries(
+ self,
+ action: str | None = None,
+ values: dict[str, ConfigValueType] | None = None,
+ ) -> list[ConfigEntry]:
"""Return all (provider/player specific) Config Entries for the given player (if any)."""
- base_entries = [
- *await super().get_config_entries(),
+ base_entries = await super().get_config_entries(action=action, values=values)
+ base_entries += [
CONF_ENTRY_FLOW_MODE_ENFORCED,
CONF_ENTRY_DEPRECATED_EQ_BASS,
CONF_ENTRY_DEPRECATED_EQ_MID,
self._attr_playback_state = PlaybackState.PLAYING
self.update_state()
- async def get_config_entries(self) -> list[ConfigEntry]:
+ async def get_config_entries(
+ self,
+ action: str | None = None,
+ values: dict[str, ConfigValueType] | None = None,
+ ) -> list[ConfigEntry]:
"""Return all (provider/player specific) Config Entries for the given player (if any)."""
- base_entries = await super().get_config_entries()
+ base_entries = await super().get_config_entries(action=action, values=values)
return [
*base_entries,
CONF_ENTRY_FLOW_MODE_ENFORCED,
import time
from typing import TYPE_CHECKING
-from music_assistant_models.config_entries import ConfigEntry
+from music_assistant_models.config_entries import ConfigEntry, ConfigValueType
from music_assistant_models.enums import PlaybackState, PlayerFeature, PlayerType
from music_assistant_models.errors import PlayerCommandFailed
from pyblu import Player as BluosPlayer
self._attr_supported_features.add(PlayerFeature.VOLUME_SET)
await self.mass.players.register_or_update(self)
- async def get_config_entries(self) -> list[ConfigEntry]:
+ async def get_config_entries(
+ self,
+ action: str | None = None,
+ values: dict[str, ConfigValueType] | None = None,
+ ) -> list[ConfigEntry]:
"""Return all (provider/player specific) Config Entries for the player."""
return [
- *await super().get_config_entries(),
+ *await super().get_config_entries(action=action, values=values),
CONF_ENTRY_HTTP_PROFILE_DEFAULT_3,
create_sample_rates_config_entry(
max_sample_rate=192000,
from aiohttp import web
from music_assistant_models.builtin_player import BuiltinPlayerEvent, BuiltinPlayerState
-from music_assistant_models.config_entries import ConfigEntry
+from music_assistant_models.config_entries import ConfigEntry, ConfigValueType
from music_assistant_models.constants import PLAYER_CONTROL_NATIVE
from music_assistant_models.enums import (
BuiltinPlayerEventType,
if update_state:
self.update_state()
- async def get_config_entries(self) -> list[ConfigEntry]:
+ async def get_config_entries(
+ self,
+ action: str | None = None,
+ values: dict[str, ConfigValueType] | None = None,
+ ) -> list[ConfigEntry]:
"""Return all (provider/player specific) Config Entries for the player."""
- base_entries = await super().get_config_entries()
+ base_entries = await super().get_config_entries(action=action, values=values)
return [
*base_entries,
CONF_ENTRY_FLOW_MODE_ENFORCED,
from typing import TYPE_CHECKING, Any, cast
from uuid import UUID
+from music_assistant_models.config_entries import ConfigEntry
+
+if TYPE_CHECKING:
+ from music_assistant_models.config_entries import ConfigValueType
from music_assistant_models.enums import MediaType, PlaybackState, PlayerFeature, PlayerType
from music_assistant_models.errors import PlayerUnavailableError
from music_assistant_models.player import PlayerSource
self.mz_controller = mz_controller
self.cc.start()
- async def get_config_entries(self) -> list[ConfigEntry]:
+ async def get_config_entries(
+ self,
+ action: str | None = None,
+ values: dict[str, ConfigValueType] | None = None,
+ ) -> list[ConfigEntry]:
"""Return all (provider/player specific) Config Entries for the given player (if any)."""
- base_entries = await super().get_config_entries()
+ base_entries = await super().get_config_entries(action=action, values=values)
if self.type == PlayerType.GROUP:
return [
*base_entries,
from async_upnp_client.client import UpnpService, UpnpStateVariable
from async_upnp_client.exceptions import UpnpError, UpnpResponseError
from async_upnp_client.profiles.dlna import DmrDevice, TransportState
-from music_assistant_models.config_entries import ConfigEntry
+from music_assistant_models.config_entries import ConfigEntry, ConfigValueType
from music_assistant_models.enums import PlaybackState, PlayerFeature
from music_assistant_models.errors import PlayerUnavailableError
from music_assistant_models.player import DeviceInfo, PlayerMedia
async def get_config_entries(
self,
+ action: str | None = None,
+ values: dict[str, ConfigValueType] | None = None,
) -> list[ConfigEntry]:
"""Return all (provider/player specific) Config Entries for the given player (if any)."""
- base_entries = await super().get_config_entries()
+ base_entries = await super().get_config_entries(action=action, values=values)
return base_entries + PLAYER_CONFIG_ENTRIES
# async def on_player_config_change(
if TYPE_CHECKING:
from fullykiosk import FullyKiosk
- from music_assistant_models.config_entries import ConfigEntry
+ from music_assistant_models.config_entries import ConfigEntry, ConfigValueType
from .provider import FullyKioskProvider
self._attr_needs_poll = True
self._attr_poll_interval = 10
- async def get_config_entries(self) -> list[ConfigEntry]:
+ async def get_config_entries(
+ self,
+ action: str | None = None,
+ values: dict[str, ConfigValueType] | None = None,
+ ) -> list[ConfigEntry]:
"""Return all (provider/player specific) Config Entries for the given player (if any)."""
- base_entries = await super().get_config_entries()
+ base_entries = await super().get_config_entries(action=action, values=values)
return [
*base_entries,
CONF_ENTRY_FLOW_MODE_ENFORCED,
from hass_client.models import CompressedState
from hass_client.models import Entity as HassEntity
from hass_client.models import State as HassState
- from music_assistant_models.config_entries import ConfigEntry
+ from music_assistant_models.config_entries import ConfigEntry, ConfigValueType
DEFAULT_PLAYER_CONFIG_ENTRIES = (
self.extra_data["hass_supported_features"] = hass_supported_features
self._update_attributes(hass_state["attributes"])
- async def get_config_entries(self) -> list[ConfigEntry]:
+ async def get_config_entries(
+ self,
+ action: str | None = None,
+ values: dict[str, ConfigValueType] | None = None,
+ ) -> list[ConfigEntry]:
"""Return all (provider/player specific) Config Entries for the player."""
- base_entries = await super().get_config_entries()
+ base_entries = await super().get_config_entries(action=action, values=values)
base_entries = [*base_entries, *DEFAULT_PLAYER_CONFIG_ENTRIES]
if self.extra_data.get("esphome_supported_audio_formats"):
# optimized config for new ESPHome mediaplayer
from aiohttp import ServerDisconnectedError
from aiomusiccast.exceptions import MusicCastGroupException
from aiomusiccast.pyamaha import MusicCastConnectionException
-from music_assistant_models.config_entries import ConfigEntry, ConfigValueOption
+from music_assistant_models.config_entries import ConfigEntry, ConfigValueOption, ConfigValueType
from music_assistant_models.enums import ConfigEntryType, PlaybackState, PlayerFeature
from music_assistant_models.player import DeviceInfo, PlayerMedia, PlayerSource
from propcache import under_cached_property as cached_property
await self._cmd_run(self.zone_device.join_players, child_player_zone_devices)
- async def get_config_entries(self) -> list[ConfigEntry]:
+ async def get_config_entries(
+ self,
+ action: str | None = None,
+ values: dict[str, ConfigValueType] | None = None,
+ ) -> list[ConfigEntry]:
"""Get player config entries."""
- base_entries = await super().get_config_entries()
+ base_entries = await super().get_config_entries(action=action, values=values)
zone_entries: list[ConfigEntry] = []
if len(self.physical_device.zone_devices) > 1:
from aioresonate.models import MediaCommand
from aioresonate.models.types import PlaybackStateType
from aioresonate.models.types import RepeatMode as ResonateRepeatMode
-from aioresonate.server import (
- AudioFormat as ResonateAudioFormat,
-)
+from aioresonate.server import AudioFormat as ResonateAudioFormat
from aioresonate.server import (
ClientEvent,
GroupCommandEvent,
GroupStateChangedEvent,
VolumeChangedEvent,
)
-from aioresonate.server.client import (
- ClientGroupChangedEvent,
- DisconnectBehaviour,
-)
+from aioresonate.server.client import ClientGroupChangedEvent, DisconnectBehaviour
from aioresonate.server.group import (
AudioCodec,
GroupDeletedEvent,
GroupMemberRemovedEvent,
Metadata,
)
-from music_assistant_models.config_entries import ConfigEntry
+from music_assistant_models.config_entries import ConfigEntry, ConfigValueType
from music_assistant_models.constants import PLAYER_CONTROL_NONE
from music_assistant_models.enums import (
ContentType,
# Send metadata to the group
self.api.group.set_metadata(metadata)
- async def get_config_entries(self) -> list[ConfigEntry]:
+ async def get_config_entries(
+ self,
+ action: str | None = None,
+ values: dict[str, ConfigValueType] | None = None,
+ ) -> list[ConfigEntry]:
"""Return all (provider/player specific) Config Entries for the player."""
- default_entries = await super().get_config_entries()
+ default_entries = await super().get_config_entries(action=action, values=values)
return [
*default_entries,
ConfigEntry.from_dict(
from .constants import CONF_ROKU_APP_ID
if TYPE_CHECKING:
- from music_assistant_models.config_entries import ConfigEntry
+ from music_assistant_models.config_entries import ConfigEntry, ConfigValueType
from rokuecp import Roku
from .provider import MediaAssistantprovider
"""Return the interval in seconds to poll the player for state updates."""
return 5 if self.powered else 30
- async def get_config_entries(self) -> list[ConfigEntry]:
+ async def get_config_entries(
+ self,
+ action: str | None = None,
+ values: dict[str, ConfigValueType] | None = None,
+ ) -> list[ConfigEntry]:
"""Return all (provider/player specific) Config Entries for the player."""
- default_entries = await super().get_config_entries()
+ default_entries = await super().get_config_entries(action=action, values=values)
return [
*default_entries,
CONF_ENTRY_HTTP_PROFILE,
from contextlib import suppress
from typing import TYPE_CHECKING, cast
-from music_assistant_models.config_entries import ConfigEntry
+from music_assistant_models.config_entries import ConfigEntry, ConfigValueType
from music_assistant_models.enums import ContentType, MediaType, PlaybackState, PlayerFeature
from music_assistant_models.media_items.audio_format import AudioFormat
from music_assistant_models.player import DeviceInfo, PlayerMedia
assert group is not None # for type checking
await group.set_stream(new_stream_name)
- async def get_config_entries(self) -> list[ConfigEntry]:
+ async def get_config_entries(
+ self,
+ action: str | None = None,
+ values: dict[str, ConfigValueType] | None = None,
+ ) -> list[ConfigEntry]:
"""Player config."""
- base_entries = await super().get_config_entries()
+ base_entries = await super().get_config_entries(action=action, values=values)
return [
*base_entries,
CONF_ENTRY_FLOW_MODE_ENFORCED,
from aiosonos.const import EventType as SonosEventType
from aiosonos.const import SonosEvent
from aiosonos.exceptions import ConnectionFailed, FailedCommand
-from music_assistant_models.config_entries import ConfigEntry
+from music_assistant_models.config_entries import ConfigEntry, ConfigValueType
from music_assistant_models.enums import (
ConfigEntryType,
EventType,
async def get_config_entries(
self,
+ action: str | None = None,
+ values: dict[str, ConfigValueType] | None = None,
) -> list[ConfigEntry]:
"""Return all (provider/player specific) Config Entries for the player."""
base_entries = [
- *await super().get_config_entries(),
+ *await super().get_config_entries(action=action, values=values),
CONF_ENTRY_OUTPUT_CODEC,
CONF_ENTRY_HTTP_PROFILE_DEFAULT_1,
create_sample_rates_config_entry(
from .helpers import SonosUpdateError, soco_error
if TYPE_CHECKING:
- from music_assistant_models.config_entries import ConfigEntry
+ from music_assistant_models.config_entries import ConfigEntry, ConfigValueType
from soco.events_base import Event as SonosEvent
from soco.events_base import SubscriptionBase
self.update_state()
await self.unsubscribe()
- async def get_config_entries(self) -> list[ConfigEntry]:
+ async def get_config_entries(
+ self,
+ action: str | None = None,
+ values: dict[str, ConfigValueType] | None = None,
+ ) -> list[ConfigEntry]:
"""Return all (provider/player specific) Config Entries for the player."""
return [
- *await super().get_config_entries(),
+ *await super().get_config_entries(action=action, values=values),
CONF_ENTRY_FLOW_MODE_HIDDEN_DISABLED,
CONF_ENTRY_HTTP_PROFILE_DEFAULT_1,
CONF_ENTRY_OUTPUT_CODEC,
from aioslimproto.models import Preset as SlimPreset
from aioslimproto.models import SlimEvent
from aioslimproto.models import VisualisationType as SlimVisualisationType
-from music_assistant_models.config_entries import ConfigEntry, ConfigValueOption
+from music_assistant_models.config_entries import ConfigEntry, ConfigValueOption, ConfigValueType
from music_assistant_models.enums import (
ConfigEntryType,
ContentType,
await self.client.volume_set(init_volume)
await self.mass.players.register_or_update(self)
- async def get_config_entries(self) -> list[ConfigEntry]:
+ async def get_config_entries(
+ self,
+ action: str | None = None,
+ values: dict[str, ConfigValueType] | None = None,
+ ) -> list[ConfigEntry]:
"""Return all (provider/player specific) Config Entries for the player."""
- base_entries = await super().get_config_entries()
+ base_entries = await super().get_config_entries(action=action, values=values)
max_sample_rate = int(self.client.max_sample_rate)
# create preset entries (for players that support it)
presets = []
from typing import TYPE_CHECKING, cast
from aiohttp import web
-from music_assistant_models.config_entries import ConfigEntry, ConfigValueOption
+from music_assistant_models.config_entries import ConfigEntry, ConfigValueOption, ConfigValueType
from music_assistant_models.constants import PLAYER_CONTROL_NONE
from music_assistant_models.enums import (
ConfigEntryType,
"""Return if the player is a dynamic group player."""
return bool(self.config.get_value(CONF_DYNAMIC_GROUP_MEMBERS, False))
- async def get_config_entries(self) -> list[ConfigEntry]:
+ async def get_config_entries(
+ self,
+ action: str | None = None,
+ values: dict[str, ConfigValueType] | None = None,
+ ) -> list[ConfigEntry]:
"""Return all (provider/player specific) Config Entries for the given player (if any)."""
return [
# default entries for player groups
- *await super().get_config_entries(),
+ *await super().get_config_entries(action=action, values=values),
# add universal group specific entries
CONFIG_ENTRY_UGP_NOTE,
ConfigEntry(