/
/
1"""Helpers for building shared configuration entries."""
2
3from __future__ import annotations
4
5from typing import TYPE_CHECKING
6
7from music_assistant_models.config_entries import ConfigEntry, ConfigValueOption
8from music_assistant_models.enums import ConfigEntryType
9
10if TYPE_CHECKING:
11 from music_assistant_models.config_entries import ConfigValueType
12
13 from music_assistant.mass import MusicAssistant
14
15# the Music Assistant players a per-player plugin exposes a device for
16CONF_CONNECTED_PLAYERS = "connected_players"
17# how the advertised device name is composed from the connected player's name
18CONF_PUBLISH_NAME_TEMPLATE = "publish_name_template"
19PUBLISH_NAME_PLAYER_MASS = "player_mass"
20PUBLISH_NAME_PLAYER = "player"
21PUBLISH_NAME_MASS_PLAYER = "mass_player"
22PUBLISH_NAME_TEMPLATES = (
23 PUBLISH_NAME_PLAYER_MASS,
24 PUBLISH_NAME_PLAYER,
25 PUBLISH_NAME_MASS_PLAYER,
26)
27
28
29def create_player_selector(
30 mass: MusicAssistant,
31 key: str,
32 selected_value: ConfigValueType = None,
33) -> ConfigEntry:
34 """
35 Return a required single-player selector populated from the available players.
36
37 :param mass: The Music Assistant instance providing the current players.
38 :param key: The config entry key for the selected player.
39 :param selected_value: Previously selected player id to prefill when still available.
40 """
41 options = _player_options(mass)
42 selected = selected_value if any(option.value == selected_value for option in options) else None
43 return ConfigEntry(
44 key=key,
45 type=ConfigEntryType.STRING,
46 required=True,
47 default_value=selected,
48 value=selected,
49 options=options,
50 )
51
52
53def create_connected_players_entry(
54 mass: MusicAssistant, selected: list[str] | None = None
55) -> ConfigEntry:
56 """
57 Return the multi-select of players a per-player plugin exposes a device for.
58
59 :param mass: The Music Assistant instance providing the current players.
60 :param selected: Currently selected player ids; ids not registered right now stay
61 listed (with the id as title) so saving the form does not silently drop them.
62 """
63 options = _player_options(mass)
64 known_ids = {option.value for option in options}
65 options += [
66 ConfigValueOption(player_id, title=player_id)
67 for player_id in selected or []
68 if player_id not in known_ids
69 ]
70 return ConfigEntry(
71 key=CONF_CONNECTED_PLAYERS,
72 type=ConfigEntryType.STRING,
73 multi_value=True,
74 required=False,
75 default_value=[],
76 value=list(selected or []),
77 options=options,
78 )
79
80
81def create_publish_name_template_entry(current: ConfigValueType = None) -> ConfigEntry:
82 """
83 Return the dropdown selecting how the advertised device name is composed.
84
85 :param current: Previously stored template value to preselect.
86 """
87 selected = current if current in PUBLISH_NAME_TEMPLATES else PUBLISH_NAME_PLAYER_MASS
88 return ConfigEntry(
89 key=CONF_PUBLISH_NAME_TEMPLATE,
90 type=ConfigEntryType.STRING,
91 required=True,
92 default_value=PUBLISH_NAME_PLAYER_MASS,
93 value=selected,
94 options=[ConfigValueOption(template) for template in PUBLISH_NAME_TEMPLATES],
95 )
96
97
98def resolve_publish_name(template: ConfigValueType, player_name: str) -> str:
99 """
100 Render the advertised device name for a player from the selected template.
101
102 :param template: One of the PUBLISH_NAME_TEMPLATES option values.
103 :param player_name: The connected player's display name.
104 """
105 if template == PUBLISH_NAME_PLAYER:
106 return player_name
107 if template == PUBLISH_NAME_MASS_PLAYER:
108 return f"Music Assistant | {player_name}"
109 return f"{player_name} | Music Assistant"
110
111
112def _player_options(mass: MusicAssistant) -> list[ConfigValueOption]:
113 """Return a picker option for every registered player, sorted by display name."""
114 return [
115 ConfigValueOption(player.player_id, title=player.display_name)
116 for player in sorted(
117 mass.players.all_players(False, False),
118 key=lambda player: player.display_name.lower(),
119 )
120 ]
121