From 3c2cb493fda1de72afabc1720d195ba5a5bfe387 Mon Sep 17 00:00:00 2001 From: Marcel van der Veldt Date: Mon, 16 Feb 2026 10:34:42 +0100 Subject: [PATCH] Some small follow-up fixes for protocols linking --- .../controllers/players/protocol_linking.py | 9 +++++--- music_assistant/mass.py | 8 +++++-- .../providers/hass_players/provider.py | 21 ++++++++++++++++++- 3 files changed, 32 insertions(+), 6 deletions(-) diff --git a/music_assistant/controllers/players/protocol_linking.py b/music_assistant/controllers/players/protocol_linking.py index f05fc75f..390af146 100644 --- a/music_assistant/controllers/players/protocol_linking.py +++ b/music_assistant/controllers/players/protocol_linking.py @@ -651,15 +651,18 @@ class ProtocolLinkingMixin: if not protocol_provider: continue + # Extract domain from provider instance_id (e.g., "airplay--uuid" -> "airplay") + protocol_domain = protocol_provider.split("--")[0] + # Get provider name for display provider_name = "Protocol" # Default fallback for provider in self.mass.get_providers(ProviderType.PLAYER): - if provider.domain == protocol_provider: + if provider.domain == protocol_domain: provider_name = provider.name break # Get priority for this protocol - priority = PROTOCOL_PRIORITY.get(protocol_provider, 100) + priority = PROTOCOL_PRIORITY.get(protocol_domain, 100) # Check if protocol player is available (registered) protocol_player = self.get_player(protocol_id) @@ -670,7 +673,7 @@ class ProtocolLinkingMixin: OutputProtocol( output_protocol_id=protocol_id, name=provider_name, - protocol_domain=protocol_provider, + protocol_domain=protocol_domain, priority=priority, is_native=False, available=is_available, diff --git a/music_assistant/mass.py b/music_assistant/mass.py index ab360055..d309342a 100644 --- a/music_assistant/mass.py +++ b/music_assistant/mass.py @@ -282,9 +282,13 @@ class MusicAssistant: return list(self._provider_manifests.values()) @api_command("providers/manifests/get") - def get_provider_manifest(self, domain: str) -> ProviderManifest: + def get_provider_manifest(self, instance_id_or_domain: str) -> ProviderManifest: """Return Provider manifests of single provider(domain).""" - return self._provider_manifests[domain] + if instance_id_or_domain in self._provider_manifests: + return self._provider_manifests[instance_id_or_domain] + if provider := self.get_provider(instance_id_or_domain, return_unavailable=True): + return provider.manifest + raise KeyError(f"Provider manifest not found for {instance_id_or_domain}") @api_command("providers") def get_providers( diff --git a/music_assistant/providers/hass_players/provider.py b/music_assistant/providers/hass_players/provider.py index c7465829..1aa4cd09 100644 --- a/music_assistant/providers/hass_players/provider.py +++ b/music_assistant/providers/hass_players/provider.py @@ -10,6 +10,8 @@ from __future__ import annotations from collections.abc import Callable from typing import TYPE_CHECKING, Any, cast +from music_assistant_models.enums import ProviderFeature + from music_assistant.mass import MusicAssistant from music_assistant.models.player_provider import PlayerProvider @@ -42,7 +44,8 @@ class HomeAssistantPlayerProvider(PlayerProvider): hass_prov: HomeAssistantProvider, ) -> None: """Initialize MusicProvider.""" - super().__init__(mass, manifest, config) + supported_features = {ProviderFeature.REMOVE_PLAYER} + super().__init__(mass, manifest, config, supported_features=supported_features) self.hass_prov = hass_prov async def loaded_in_mass(self) -> None: @@ -63,6 +66,12 @@ class HomeAssistantPlayerProvider(PlayerProvider): self.on_unload_callbacks = [ await self.hass_prov.hass.subscribe_entities(self._on_entity_state_update, player_ids) ] + # cleanup any players that are no longer in the config + for player_conf in await self.mass.config.get_player_configs( + provider=self.instance_id, include_unavailable=True, include_disabled=True + ): + if player_conf.player_id not in player_ids: + await self.mass.players.remove(player_conf.player_id) async def unload(self, is_removed: bool = False) -> None: """ @@ -75,6 +84,16 @@ class HomeAssistantPlayerProvider(PlayerProvider): for callback in self.on_unload_callbacks: callback() + async def remove_player(self, player_id: str) -> None: + """Remove a player.""" + player_ids = cast("list[str]", self.config.get_value(CONF_PLAYERS)) + if player_id in player_ids: + player_ids.remove(player_id) + self.mass.config.set_raw_provider_config_value( + self.instance_id, CONF_PLAYERS, player_ids + ) + await self.mass.players.unregister(player_id, True) + async def _setup_player( self, state: HassState, -- 2.34.1