Chore: some fine grained player config entries for hass players
authorMarcel van der Veldt <m.vanderveldt@outlook.com>
Mon, 24 Feb 2025 16:48:26 +0000 (17:48 +0100)
committerMarcel van der Veldt <m.vanderveldt@outlook.com>
Mon, 24 Feb 2025 16:48:26 +0000 (17:48 +0100)
music_assistant/constants.py
music_assistant/providers/dlna/__init__.py
music_assistant/providers/hass_players/__init__.py

index 8271514f33dc5de941986229f4c385c99403d5e1..09367092e823a827c01715d9b79f1705e79ecd94 100644 (file)
@@ -323,6 +323,9 @@ CONF_ENTRY_OUTPUT_CODEC_ENFORCE_MP3 = ConfigEntry.from_dict(
 CONF_ENTRY_OUTPUT_CODEC_HIDDEN = ConfigEntry.from_dict(
     {**CONF_ENTRY_OUTPUT_CODEC.to_dict(), "hidden": True}
 )
+CONF_ENTRY_OUTPUT_CODEC_ENFORCE_FLAC = ConfigEntry.from_dict(
+    {**CONF_ENTRY_OUTPUT_CODEC.to_dict(), "default_value": "flac", "hidden": True}
+)
 
 
 CONF_ENTRY_SYNC_ADJUST = ConfigEntry(
@@ -497,6 +500,10 @@ CONF_ENTRY_ENABLE_ICY_METADATA = ConfigEntry(
     "this correctly. If you experience issues with playback, try to disable this setting.",
 )
 
+CONF_ENTRY_ENABLE_ICY_METADATA_HIDDEN = ConfigEntry.from_dict(
+    {**CONF_ENTRY_ENABLE_ICY_METADATA.to_dict(), "hidden": True}
+)
+
 CONF_ENTRY_WARN_PREVIEW = ConfigEntry(
     key="preview_note",
     type=ConfigEntryType.ALERT,
index b2c9dae076e27b82f1926b0b4a31873a176745f1..eb07e45fde283532433f576ae7bf8eb763585f88 100644 (file)
@@ -591,6 +591,8 @@ class DLNAPlayerProvider(PlayerProvider):
 
     def _set_player_features(self, dlna_player: DLNAPlayer) -> None:
         """Set Player Features based on config values and capabilities."""
+        if not dlna_player.device:
+            return
         supported_features: set[PlayerFeature] = {
             # there is no way to check if a dlna player support enqueuing
             # so we simply assume it does and if it doesn't
index 3d6b5fd729bbf1996efcdfa7eb5d3218801ae54b..ca7a99cd7a0091ba3cc5aca1fe1a095738532aa5 100644 (file)
@@ -23,10 +23,13 @@ from music_assistant.constants import (
     CONF_ENTRY_CROSSFADE,
     CONF_ENTRY_CROSSFADE_DURATION,
     CONF_ENTRY_ENABLE_ICY_METADATA,
+    CONF_ENTRY_ENABLE_ICY_METADATA_HIDDEN,
+    CONF_ENTRY_FLOW_MODE_DEFAULT_ENABLED,
     CONF_ENTRY_FLOW_MODE_ENFORCED,
     CONF_ENTRY_HTTP_PROFILE,
     CONF_ENTRY_HTTP_PROFILE_FORCED_2,
     CONF_ENTRY_OUTPUT_CODEC_DEFAULT_MP3,
+    CONF_ENTRY_OUTPUT_CODEC_ENFORCE_FLAC,
     CONF_ENTRY_OUTPUT_CODEC_ENFORCE_MP3,
     HIDDEN_ANNOUNCE_VOLUME_CONFIG_ENTRIES,
     create_sample_rates_config_entry,
@@ -68,11 +71,23 @@ DEFAULT_PLAYER_CONFIG_ENTRIES = (
     CONF_ENTRY_FLOW_MODE_ENFORCED,
 )
 
+BLOCKLISTED_HASS_INTEGRATIONS = ("alexa_media",)
+WARN_HASS_INTEGRATIONS = ("apple_tv", "cast", "dlna_dmr", "fully_kiosk", "sonos", "snapcast")
+
+CONF_ENTRY_WARN_HASS_INTEGRATION = ConfigEntry(
+    key="warn_hass_integration",
+    type=ConfigEntryType.ALERT,
+    label="Music Assistant has native support for this player type - "
+    "it is strongly recommended to use the native player provider for this player in "
+    "Music Assistant instead of the generic version provided by the Home Assistant provider.",
+)
+
 
 async def _get_hass_media_players(
     hass_prov: HomeAssistantProvider,
 ) -> AsyncGenerator[HassState, None]:
     """Return all HA state objects for (valid) media_player entities."""
+    entity_registry = {x["entity_id"]: x for x in await hass_prov.hass.get_entity_registry()}
     for state in await hass_prov.hass.get_states():
         if not state["entity_id"].startswith("media_player"):
             continue
@@ -85,6 +100,10 @@ async def _get_hass_media_players(
         supported_features = MediaPlayerEntityFeature(state["attributes"]["supported_features"])
         if MediaPlayerEntityFeature.PLAY_MEDIA not in supported_features:
             continue
+        if entity_registry_entry := entity_registry.get(state["entity_id"]):
+            hass_domain = entity_registry_entry["platform"]
+            if hass_domain in BLOCKLISTED_HASS_INTEGRATIONS:
+                continue
         yield state
 
 
@@ -176,6 +195,7 @@ class HomeAssistantPlayers(PlayerProvider):
     ) -> 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)
+        base_entries = (*base_entries, *DEFAULT_PLAYER_CONFIG_ENTRIES)
         player = self.mass.players.get(player_id)
         if player and player.extra_data.get("esphome_supported_audio_formats"):
             # optimized config for new ESPHome mediaplayer
@@ -197,10 +217,10 @@ class HomeAssistantPlayers(PlayerProvider):
             return (
                 *base_entries,
                 # New ESPHome mediaplayer (used in Voice PE) uses FLAC 48khz/16 bits
-                CONF_ENTRY_CROSSFADE,
-                CONF_ENTRY_CROSSFADE_DURATION,
                 CONF_ENTRY_FLOW_MODE_ENFORCED,
                 CONF_ENTRY_HTTP_PROFILE_FORCED_2,
+                CONF_ENTRY_OUTPUT_CODEC_ENFORCE_FLAC,
+                CONF_ENTRY_ENABLE_ICY_METADATA_HIDDEN,
                 create_sample_rates_config_entry(
                     supported_sample_rates=supported_sample_rates,
                     supported_bit_depths=supported_bit_depths,
@@ -211,7 +231,19 @@ class HomeAssistantPlayers(PlayerProvider):
                 *HIDDEN_ANNOUNCE_VOLUME_CONFIG_ENTRIES,
             )
 
-        return base_entries + DEFAULT_PLAYER_CONFIG_ENTRIES
+        # add alert if player is a known player type that has a native provider in MA
+        if player and player.extra_data.get("hass_domain") in WARN_HASS_INTEGRATIONS:
+            base_entries = (CONF_ENTRY_WARN_HASS_INTEGRATION, *base_entries)
+
+        # enable flow mode by default if player does not report enqueue support
+        if (
+            player
+            and MediaPlayerEntityFeature.MEDIA_ENQUEUE
+            not in player.extra_data["hass_supported_features"]
+        ):
+            base_entries = (*base_entries, CONF_ENTRY_FLOW_MODE_DEFAULT_ENABLED)
+
+        return base_entries
 
     async def cmd_stop(self, player_id: str) -> None:
         """Send STOP command to given player.
@@ -473,6 +505,7 @@ class HomeAssistantPlayers(PlayerProvider):
         ):
             player.supported_features.add(PlayerFeature.POWER)
             player.powered = state["state"] not in OFF_STATES
+        player.extra_data["hass_supported_features"] = hass_supported_features
 
         self._update_player_attributes(player, state["attributes"])
         await self.mass.players.register_or_update(player)