CONF_EQ_TREBLE,
CONF_FLOW_MODE,
CONF_HIDE_PLAYER,
+ CONF_ICON,
CONF_LOG_LEVEL,
CONF_OUTPUT_CHANNELS,
CONF_SYNC_ADJUST,
ConfigEntryType.LABEL: str,
ConfigEntryType.DIVIDER: str,
ConfigEntryType.ACTION: str,
+ ConfigEntryType.ALERT: str,
+ ConfigEntryType.ICON: str,
}
-UI_ONLY = (ConfigEntryType.LABEL, ConfigEntryType.DIVIDER, ConfigEntryType.ACTION)
+UI_ONLY = (
+ ConfigEntryType.LABEL,
+ ConfigEntryType.DIVIDER,
+ ConfigEntryType.ACTION,
+ ConfigEntryType.ALERT,
+)
@dataclass
depends_on: str | None = None
# hidden: hide from UI
hidden: bool = False
- # advanced: this is an advanced setting (frontend hides it in some corner)
- advanced: bool = False
+ # category: category to group this setting into in the frontend (e.g. advanced)
+ category: str = "generic"
# action: (configentry)action that is needed to get the value for this entry
action: str | None = None
# action_label: default label for the action when no translation for the action is present
ConfigValueOption("verbose", "VERBOSE"),
),
default_value="GLOBAL",
- advanced=True,
+ category="advanced",
)
DEFAULT_PROVIDER_CONFIG_ENTRIES = (CONF_ENTRY_LOG_LEVEL,)
type=ConfigEntryType.BOOLEAN,
label="Enable queue flow mode",
default_value=False,
- advanced=False,
)
],
default_value="stereo",
label="Output Channel Mode",
- advanced=True,
+ category="audio",
)
CONF_ENTRY_VOLUME_NORMALIZATION = ConfigEntry(
label="Enable volume normalization",
default_value=True,
description="Enable volume normalization (EBU-R128 based)",
+ category="audio",
)
CONF_ENTRY_VOLUME_NORMALIZATION_TARGET = ConfigEntry(
label="Target level for volume normalization",
description="Adjust average (perceived) loudness to this target level",
depends_on=CONF_VOLUME_NORMALIZATION,
- advanced=True,
+ category="audio",
)
CONF_ENTRY_EQ_BASS = ConfigEntry(
default_value=0,
label="Equalizer: bass",
description="Use the builtin basic equalizer to adjust the bass of audio.",
- advanced=True,
+ category="audio",
)
CONF_ENTRY_EQ_MID = ConfigEntry(
default_value=0,
label="Equalizer: midrange",
description="Use the builtin basic equalizer to adjust the midrange of audio.",
- advanced=True,
+ category="audio",
)
CONF_ENTRY_EQ_TREBLE = ConfigEntry(
default_value=0,
label="Equalizer: treble",
description="Use the builtin basic equalizer to adjust the treble of audio.",
- advanced=True,
+ category="audio",
)
label="Enable crossfade",
default_value=False,
description="Enable a crossfade transition between (queue) tracks.",
- advanced=False,
+ category="audio",
)
CONF_ENTRY_CROSSFADE_DURATION = ConfigEntry(
label="Crossfade duration",
description="Duration in seconds of the crossfade between tracks (if enabled)",
depends_on=CONF_CROSSFADE,
- advanced=True,
+ category="audio",
)
CONF_ENTRY_HIDE_PLAYER = ConfigEntry(
type=ConfigEntryType.BOOLEAN,
label="Hide this player in the user interface",
default_value=False,
- advanced=True,
)
CONF_ENTRY_ENFORCE_MP3 = ConfigEntry(
"to all players. Some players can not deal with that and require the stream to be packed "
"into a lossy mp3 codec. \n\n "
"Only enable when needed. Saves some bandwidth at the cost of audio quality.",
- advanced=True,
+ category="audio",
)
CONF_ENTRY_SYNC_ADJUST = ConfigEntry(
description="If this player is playing audio synced with other players "
"and you always hear the audio too early or late on this player, "
"you can shift the audio a bit.",
- advanced=True,
+ category="advanced",
)
type=ConfigEntryType.BOOLEAN,
default_value=True,
label="Pre-announce TTS announcements",
- description="When a TTS (text-to-speech) message is sent to the Announce feature, "
- "prepend the announcement with a short pre-announcement sound (bell whistle).",
+ category="announcements",
)
options=[
ConfigValueOption("Absolute volume", "absolute"),
ConfigValueOption("Relative volume increase", "relative"),
- ConfigValueOption("Percentual volume increase", "percentual"),
+ ConfigValueOption("Volume increase by fixed percentage", "percentual"),
ConfigValueOption("Do not adjust volume", "none"),
],
default_value="percentual",
label="Volume strategy for Announcements",
- description="When an announcement is being broadcast to this player, "
- "how should the volume be adjusted temporary (use in combination with the volume value below).",
+ category="announcements",
)
CONF_ENTRY_ANNOUNCE_VOLUME = ConfigEntry(
type=ConfigEntryType.INTEGER,
default_value=85,
label="Volume for Announcements",
- description="The percentual, relative or absolute volume level, "
- "used with the strategy for announcements.",
+ category="announcements",
)
CONF_ENTRY_ANNOUNCE_VOLUME_MIN = ConfigEntry(
default_value=15,
label="Minimum Volume level for Announcements",
description="The volume (adjustment) of announcements should no go below this level.",
+ category="announcements",
)
CONF_ENTRY_ANNOUNCE_VOLUME_MAX = ConfigEntry(
default_value=75,
label="Maximum Volume level for Announcements",
description="The volume (adjustment) of announcements should no go above this level.",
+ category="announcements",
+)
+
+CONF_ENTRY_PLAYER_ICON = ConfigEntry(
+ key=CONF_ICON,
+ type=ConfigEntryType.ICON,
+ default_value="mdi-speaker",
+ label="Icon",
+ description="Material design icon for this player. "
+ "\n\nSee https://pictogrammers.com/library/mdi/",
+ category="generic",
+)
+
+CONF_ENTRY_PLAYER_ICON_GROUP = ConfigEntry.from_dict(
+ {**CONF_ENTRY_PLAYER_ICON.to_dict(), "default_value": "mdi-speaker-multiple"}
)
LABEL = "label"
DIVIDER = "divider"
ACTION = "action"
+ ICON = "icon"
+ ALERT = "alert"
UNKNOWN = "unknown"
@classmethod
# a hidden player is hidden in the UI only but can still be controlled
hidden: bool = False
+ # icon: material design icon for this player
+ # will be set by the player manager based on config
+ icon: str = "mdi-speaker"
+
# group_volume: if the player is a player group or syncgroup master,
# this will return the average volume of all child players
# if not a group player, this is just the player's volume
CONF_ANNOUNCE_VOLUME: Final[str] = "announce_volume"
CONF_ANNOUNCE_VOLUME_MIN: Final[str] = "announce_volume_min"
CONF_ANNOUNCE_VOLUME_MAX: Final[str] = "announce_volume_max"
-
+CONF_ICON: Final[str] = "icon"
# config default values
DEFAULT_HOST: Final[str] = "0.0.0.0"
CONF_ENTRY_ANNOUNCE_VOLUME_MAX,
CONF_ENTRY_ANNOUNCE_VOLUME_MIN,
CONF_ENTRY_ANNOUNCE_VOLUME_STRATEGY,
+ CONF_ENTRY_PLAYER_ICON,
+ CONF_ENTRY_PLAYER_ICON_GROUP,
CONF_ENTRY_TTS_PRE_ANNOUNCE,
)
from music_assistant.common.models.enums import (
player.hidden = self.mass.config.get_raw_player_config_value(
player.player_id, CONF_HIDE_PLAYER, False
)
+ player.icon = self.mass.config.get_raw_player_config_value(
+ player.player_id,
+ CONF_ENTRY_PLAYER_ICON.key,
+ CONF_ENTRY_PLAYER_ICON_GROUP.default_value
+ if player.type in (PlayerType.GROUP, PlayerType.SYNC_GROUP)
+ else CONF_ENTRY_PLAYER_ICON.default_value,
+ )
# handle syncgroup - get attributes from first player that has this group as source
if player.player_id.startswith(SYNCGROUP_PREFIX):
if player.powered and (sync_leader := self.get_sync_leader(player)):
"on the given IP and TCP port by players on the local network. \n"
"This is an advanced setting that should normally "
"not be adjusted in regular setups.",
- advanced=True,
+ category="advanced",
),
ConfigEntry(
key=CONF_BIND_IP,
"Use 0.0.0.0 to bind to all interfaces, which is the default. \n"
"This is an advanced setting that should normally "
"not be adjusted in regular setups.",
- advanced=True,
+ category="advanced",
),
)
"to enhance security and protect outside access to the webinterface and API. \n\n"
"This is an advanced setting that should normally "
"not be adjusted in regular setups.",
- advanced=True,
+ category="advanced",
),
)
CONF_ENTRY_ANNOUNCE_VOLUME_STRATEGY,
CONF_ENTRY_AUTO_PLAY,
CONF_ENTRY_HIDE_PLAYER,
+ CONF_ENTRY_PLAYER_ICON,
+ CONF_ENTRY_PLAYER_ICON_GROUP,
CONF_ENTRY_TTS_PRE_ANNOUNCE,
CONF_ENTRY_VOLUME_NORMALIZATION,
CONF_ENTRY_VOLUME_NORMALIZATION_TARGET,
async def get_player_config_entries(self, player_id: str) -> tuple[ConfigEntry, ...]:
"""Return all (provider/player specific) Config Entries for the given player (if any)."""
entries = (
+ CONF_ENTRY_PLAYER_ICON,
CONF_ENTRY_VOLUME_NORMALIZATION,
CONF_ENTRY_AUTO_PLAY,
CONF_ENTRY_VOLUME_NORMALIZATION_TARGET,
multi_value=True,
required=True,
),
+ CONF_ENTRY_PLAYER_ICON_GROUP,
)
return entries
label="Enable encryption",
description="Enable encrypted communication with the player, "
"some (3rd party) players require this.",
- advanced=True,
+ category="airplay",
),
ConfigEntry(
key=CONF_ALAC_ENCODE,
label="Enable compression",
description="Save some network bandwidth by sending the audio as "
"(lossless) ALAC at the cost of a bit CPU.",
- advanced=True,
+ category="airplay",
),
CONF_ENTRY_SYNC_ADJUST,
ConfigEntry(
required=False,
label="Device password",
description="Some devices require a password to connect/play.",
- advanced=True,
+ category="airplay",
),
)
BACKOFF_TIME_LOWER_LIMIT = 15 # seconds
base_entries = await super().get_player_config_entries(player_id)
if player_id not in self._players:
# most probably a syncgroup
- return base_entries
+ return (*base_entries, CONF_ENTRY_CROSSFADE, CONF_ENTRY_CROSSFADE_DURATION)
return base_entries + PLAYER_CONFIG_ENTRIES
async def cmd_stop(self, player_id: str) -> None:
description="Enable a crossfade transition between (queue) tracks. \n\n"
"Note that Cast does not natively support crossfading so you need to enable "
"the 'flow mode' workaround to use crossfading with Cast players.",
- advanced=False,
+ category="audio",
depends_on=CONF_FLOW_MODE,
),
CONF_ENTRY_FLOW_MODE,
description="Enable a crossfade transition between (queue) tracks. \n\n"
"Note that DLNA does not natively support crossfading so you need to enable "
"the 'flow mode' workaround to use crossfading with DLNA players.",
- advanced=False,
+ category="audio",
depends_on=CONF_FLOW_MODE,
),
CONF_ENTRY_FLOW_MODE,
description="Music Assistant prefers information stored in ID3 tags and only uses"
" online sources for additional metadata. This means that the ID3 tags need to be "
"accurate, preferably tagged with MusicBrainz Picard.",
- advanced=False,
required=False,
options=(
ConfigValueOption("Skip track and log warning", "skip"),
type=ConfigEntryType.STRING,
label="Mount options",
required=False,
- advanced=True,
+ category="advanced",
default_value="noserverino,file_mode=0775,dir_mode=0775,uid=0,gid=0",
description="[optional] Any additional mount options you "
"want to pass to the mount command if needed for your particular setup.",
default_value="2323",
label="Port to use to connect to the Fully Kiosk API (default is 2323).",
required=True,
- advanced=True,
+ category="advanced",
),
)
description="By default, Music Assistant sends lossless, high quality audio "
"to all players. Some devices can not deal with that and require "
"the stream to be packed into a lossy mp3 codec. Only enable when needed.",
- advanced=True,
+ category="advanced",
),
)
"'authenticate' button to generate a token for you with logging in.",
depends_on=CONF_URL,
value=values.get(CONF_AUTH_TOKEN) if values else None,
- advanced=True,
+ category="advanced",
),
)
description="Enable a crossfade transition between (queue) tracks. \n\n"
"Note that you need to enable the 'flow mode' workaround to use "
"crossfading with Home Assistant players.",
- advanced=False,
+ category="audio",
depends_on=CONF_FLOW_MODE,
),
CONF_ENTRY_FLOW_MODE,
"to all players. Some players can not deal with that and require the stream to be packed "
"into a lossy mp3 codec. \n\n "
"Only enable when needed. Saves some bandwidth at the cost of audio quality.",
- advanced=True,
+ category="audio",
),
)
required=False,
label="Enable display support",
description="Enable/disable native display support on squeezebox or squeezelite32 hardware.",
- advanced=True,
+ category="advanced",
)
CONF_ENTRY_VISUALIZATION = ConfigEntry(
key=CONF_VISUALIZATION,
label="Visualization type",
description="The type of visualization to show on the display "
"during playback if the device supports this.",
- advanced=True,
+ category="advanced",
depends_on=CONF_DISPLAY,
)
"player compatibility, so security risks are minimized to practically zero."
"You may safely disable this option if you have no players that rely on this feature "
"or you dont care about the additional metadata.",
- advanced=True,
+ category="advanced",
),
ConfigEntry(
key=CONF_CLI_JSON_PORT,
"it on a different port. Set to 0 to disable this functionality.\n\n"
"You may safely disable this option if you have no players that rely on this feature "
"or you dont care about the additional metadata.",
- advanced=True,
+ category="advanced",
),
ConfigEntry(
key=CONF_DISCOVERY,
"discover and connect to this server. \n\n"
"You may want to disable this feature if you are running multiple slimproto servers "
"on your network and/or you don't want clients to auto connect to this server.",
- advanced=True,
+ category="advanced",
),
ConfigEntry(
key=CONF_PORT,
"The default is 3483 and using a different port is not supported by "
"hardware squeezebox players. Only adjust this port if you want to "
"use other slimproto based servers side by side with (squeezelite) software players.",
- advanced=True,
+ category="advanced",
),
)
async def get_player_config_entries(self, player_id: str) -> tuple[ConfigEntry]:
"""Return all (provider/player specific) Config Entries for the given player (if any)."""
base_entries = await super().get_player_config_entries(player_id)
+ if not self.slimproto.get_player(player_id):
+ # most probably a syncgroup
+ return (*base_entries, CONF_ENTRY_CROSSFADE, CONF_ENTRY_CROSSFADE_DURATION)
# create preset entries (for players that support it)
preset_entries = ()
label=f"Preset {index}",
description="Assign a playable item to the player's preset. "
"Only supported on real squeezebox hardware or jive(lite) based emulators.",
- advanced=False,
+ category="presets",
required=False,
)
for index in range(1, preset_count + 1)
description="Music Assistant by default already includes a Snapserver. \n\n"
"Checking this option allows you to connect to your own/external existing Snapserver "
"and not use the builtin one provided by Music Assistant.",
- advanced=snapserver_present,
+ category="advanced" if snapserver_present else "generic",
),
ConfigEntry(
key=CONF_SERVER_HOST,
label="Snapcast server ip",
required=False,
depends_on=CONF_USE_EXTERNAL_SERVER,
- advanced=snapserver_present,
+ category="advanced" if snapserver_present else "generic",
),
ConfigEntry(
key=CONF_SERVER_CONTROL_PORT,
label="Snapcast control port",
required=False,
depends_on=CONF_USE_EXTERNAL_SERVER,
- advanced=snapserver_present,
+ category="advanced" if snapserver_present else "generic",
),
)
base_entries = await super().get_player_config_entries(player_id)
if not (sonos_player := self.sonosplayers.get(player_id)):
# most probably a syncgroup
- return base_entries
+ return (*base_entries, CONF_ENTRY_CROSSFADE)
return (
*base_entries,
CONF_ENTRY_CROSSFADE,
value=sonos_player.bass,
range=(-10, 10),
description="Set the Bass level for the Sonos player",
- advanced=True,
+ category="advanced",
),
ConfigEntry(
key="sonos_treble",
value=sonos_player.treble,
range=(-10, 10),
description="Set the Treble level for the Sonos player",
- advanced=True,
+ category="advanced",
),
ConfigEntry(
key="sonos_loudness",
default_value=sonos_player.loudness,
value=sonos_player.loudness,
description="Enable loudness compensation on the Sonos player",
- advanced=True,
+ category="advanced",
),
)
),
ConfigEntry(
key="ugp_note",
- type=ConfigEntryType.LABEL,
+ type=ConfigEntryType.ALERT,
label="Please note that although the universal group "
"allows you to group any player, it will not enable audio sync "
"between players of different ecosystems.",
description="Enable a crossfade transition between (queue) tracks. \n\n"
"Note that DLNA does not natively support crossfading so you need to enable "
"the 'flow mode' workaround to use crossfading with DLNA players.",
- advanced=False,
+ category="audio",
),
CONF_ENTRY_CROSSFADE_DURATION,
)