Fix: fire event if playerfeatures/controls updated
authorMarcel van der Veldt <m.vanderveldt@outlook.com>
Wed, 25 Feb 2026 12:34:40 +0000 (13:34 +0100)
committerMarcel van der Veldt <m.vanderveldt@outlook.com>
Wed, 25 Feb 2026 12:34:40 +0000 (13:34 +0100)
to ensure HA updates its supported features as well

music_assistant/constants.py
music_assistant/controllers/players/controller.py

index dcad1942d97695c72f1e817f6c244eb2c54fb826..351cbd2bc06c3bca1c0f1fef94565a30efcd3051 100644 (file)
@@ -986,6 +986,10 @@ ATTR_AVAILABLE: Final[str] = "available"
 ATTR_MUTE_LOCK: Final[str] = "mute_lock"
 ATTR_ACTIVE_SOURCE: Final[str] = "active_source"
 ATTR_ACTIVE_PLAYLIST: Final[str] = "active_playlist"
+ATTR_SUPPORTED_FEATURES: Final[str] = "supported_features"
+ATTR_MUTE_CONTROL: Final[str] = "mute_control"
+ATTR_VOLUME_CONTROL: Final[str] = "volume_control"
+ATTR_POWER_CONTROL: Final[str] = "power_control"
 
 # Album type detection patterns
 LIVE_INDICATORS = [
index e0e01c8584c10dcfde3a91faa7d7e8ab43e109ce..5c78cb3e51a2b7fdecf7e4815f3d1d028561e357 100644 (file)
@@ -61,8 +61,12 @@ from music_assistant.constants import (
     ATTR_FAKE_VOLUME,
     ATTR_GROUP_MEMBERS,
     ATTR_LAST_POLL,
+    ATTR_MUTE_CONTROL,
     ATTR_MUTE_LOCK,
+    ATTR_POWER_CONTROL,
     ATTR_PREVIOUS_VOLUME,
+    ATTR_SUPPORTED_FEATURES,
+    ATTR_VOLUME_CONTROL,
     CONF_AUTO_PLAY,
     CONF_ENTRY_ANNOUNCE_VOLUME,
     CONF_ENTRY_ANNOUNCE_VOLUME_MAX,
@@ -1442,13 +1446,9 @@ class PlayerController(ProtocolLinkingMixin, CoreController):
         if ATTR_GROUP_MEMBERS in changed_values:
             prev_group_members, new_group_members = changed_values[ATTR_GROUP_MEMBERS]
             self._handle_group_dsp_change(player, prev_group_members or [], new_group_members)
-
-        if ATTR_GROUP_MEMBERS in changed_values:
             # Removed group members also need to be updated since they are no longer part
             # of this group and are available for playback again
-            prev_group_members = changed_values[ATTR_GROUP_MEMBERS][0] or []
-            new_group_members = changed_values[ATTR_GROUP_MEMBERS][1] or []
-            removed_members = set(prev_group_members) - set(new_group_members)
+            removed_members = set(prev_group_members or []) - set(new_group_members or [])
             for _removed_player_id in removed_members:
                 if removed_player := self.get_player(_removed_player_id):
                     removed_player.update_state()
@@ -1466,11 +1466,9 @@ class PlayerController(ProtocolLinkingMixin, CoreController):
                 new_source,
                 task_id=task_id,
             )
-        became_inactive = False
-        if ATTR_AVAILABLE in changed_values:
-            became_inactive = changed_values[ATTR_AVAILABLE][1] is False
-        if not became_inactive and ATTR_ENABLED in changed_values:
-            became_inactive = changed_values[ATTR_ENABLED][1] is False
+        became_inactive = (
+            ATTR_AVAILABLE in changed_values and changed_values[ATTR_AVAILABLE][1] is False
+        ) or (ATTR_ENABLED in changed_values and changed_values[ATTR_ENABLED][1] is False)
         if became_inactive and (player.state.active_group or player.state.synced_to):
             self.mass.create_task(self._cleanup_player_memberships(player.player_id))
 
@@ -1483,6 +1481,20 @@ class PlayerController(ProtocolLinkingMixin, CoreController):
             self.mass.signal_event(
                 EventType.PLAYER_OPTIONS_UPDATED, object_id=player_id, data=options
             )
+        # signal player config update event if playerfeatures changed
+        # this is temporary needed for the Home Assistant integration which only
+        # re-evalues the entity's supported features on a PLAYER_CONFIG_UPDATED event.
+        # TODO: Remove this temporary workaround once the HA integration is updated to
+        # also re-evaluate supported features on PLAYER_UPDATED events.
+        if changed_values.keys() & {
+            ATTR_SUPPORTED_FEATURES,
+            ATTR_MUTE_CONTROL,
+            ATTR_VOLUME_CONTROL,
+            ATTR_POWER_CONTROL,
+        }:
+            self.mass.signal_event(
+                EventType.PLAYER_CONFIG_UPDATED, object_id=player_id, data=player.config
+            )
 
         if skip_forward and not force_update:
             return