native_player.device_info.add_identifier(conn_type, value)
# Update model/manufacturer if universal player has generic values
self._update_universal_device_info(native_player, protocol_player)
- # Update availability from protocol players
- native_player.update_from_protocol_players()
# Persist updated data to config (async via task)
self._save_universal_player_data(native_player)
protocol_player.update_state()
universal_player.device_info.add_identifier(conn_type, value)
# Update model/manufacturer if universal player has generic values
self._update_universal_device_info(universal_player, protocol_player)
- # Update availability from protocol players
- universal_player.update_from_protocol_players()
# Persist all player data (protocol IDs, identifiers, device info) to config
for provider in self.mass.get_providers(ProviderType.PLAYER):
player.update_state()
# Update availability from protocol players
- if isinstance(universal_player, UniversalPlayer):
- universal_player.update_from_protocol_players()
+ universal_player.update_state()
async def _create_or_update_universal_player(self, protocol_players: list[Player]) -> None:
"""
from typing import TYPE_CHECKING
-from music_assistant_models.enums import PlayerFeature
-
from music_assistant.constants import CONF_PREFERRED_OUTPUT_PROTOCOL
from music_assistant.models.player import DeviceInfo, Player
if TYPE_CHECKING:
+ from music_assistant_models.enums import PlayerFeature
+
from .provider import UniversalPlayerProvider
# Set player attributes
self._attr_name = name
self._attr_device_info = device_info
- # Start as unavailable - will be updated when protocol players are linked
- self._attr_available = False
# a universal player does not have any features on its own,
# it delegates to protocol players
self._attr_supported_features = set()
return True
return False
+ @property
+ def available(self) -> bool:
+ """Return if the player is currently available."""
+ # A universal player is available if any of its linked protocol players are available
+ return any(
+ (p := self.mass.players.get_player(pid)) and p.available
+ for pid in self._protocol_player_ids
+ )
+
@property
def expose_to_ha_by_default(self) -> bool:
"""Return if the player should be exposed to Home Assistant by default."""
return None
- def update_from_protocol_players(self) -> None:
- """
- Update state from linked protocol players.
-
- Called to sync state like volume, availability from protocol players.
- """
- # Aggregate availability - available if any protocol is available
- self._attr_available = any(
- (p := self.mass.players.get_player(pid)) and p.available
- for pid in self._protocol_player_ids
- )
- # Get volume from best control target
- if target := self._get_control_target(PlayerFeature.VOLUME_SET):
- if target.volume_level is not None:
- self._attr_volume_level = target.volume_level
- if target := self._get_control_target(PlayerFeature.VOLUME_MUTE):
- if target.volume_muted is not None:
- self._attr_volume_muted = target.volume_muted
-
- self.update_state()
-
def add_protocol_player(self, protocol_player_id: str) -> None:
"""Add a protocol player to this universal player."""
if protocol_player_id not in self._protocol_player_ids: