self._stream_task.cancel()
with suppress(asyncio.CancelledError):
await self._stream_task
+ self._stream_task = None
async def volume_mute(self, muted: bool) -> None:
"""Send MUTE command to given player."""
assert group is not None # for type checking
# handle client additions
for player_id in player_ids_to_add or []:
- if player_id not in group.clients:
- snapcast_id = self.provider._get_snapclient_id(player_id)
+ snapcast_id = self.provider._get_snapclient_id(player_id)
+ if snapcast_id not in group.clients:
await group.add_client(snapcast_id)
- self._attr_group_members.append(player_id)
+ if player_id not in self._attr_group_members:
+ self._attr_group_members.append(player_id)
# handle client removals
for player_id in player_ids_to_remove or []:
- if player_id in group.clients:
- snapcast_id = self.provider._get_snapclient_id(player_id)
+ snapcast_id = self.provider._get_snapclient_id(player_id)
+ if snapcast_id in group.clients:
await group.remove_client(snapcast_id)
- self._attr_group_members.remove(player_id)
+ if player_id in self._attr_group_members:
+ self._attr_group_members.remove(player_id)
+ # Set default stream and stop ungrouped players
+ removed_snapclient = self.provider._snapserver.client(snapcast_id)
+ await removed_snapclient.group.set_stream("default")
+ if removed_player := self.mass.players.get(player_id):
+ await removed_player.stop()
self.update_state()
- async def ungroup(self) -> None:
- """Ungroup."""
- if self.synced_to is None:
- for mass_child_id in list(self.group_members):
- if mass_child_id != self.player_id:
- if child_player := self.mass.players.get(mass_child_id):
- await child_player.ungroup()
- return
- mass_sync_master_player = self.mass.players.get(self.synced_to)
- assert mass_sync_master_player is not None # for type checking
- mass_sync_master_player._attr_group_members.remove(self.player_id)
- group = self._get_snapgroup()
- assert group is not None # for type checking
- await group.remove_client(self.snap_client_id)
- # assign default/empty stream to the player
- await group.set_stream("default")
- await self.stop()
- # make sure that the player manager gets an update
- self.update_state()
- mass_sync_master_player.update_state()
-
async def play_media(self, media: PlayerMedia) -> None:
"""Handle PLAY MEDIA on given player."""
# ruff: noqa: PLR0915
self._stream_task.cancel()
with suppress(asyncio.CancelledError):
await self._stream_task
+ self._stream_task = None
# get stream or create new one
stream_name = self._get_stream_name(SnapCastStreamType.MUSIC)
2. Easily identify which stream belongs to which player, for instance to be able to
delete a music stream even when it is not active due to an announcement.
"""
- safe_name = create_safe_string(self.display_name, replace_space=True)
+ safe_name = create_safe_string(self.player_id, replace_space=True)
stream_name = f"{MASS_STREAM_PREFIX}{safe_name}"
if stream_type == SnapCastStreamType.ANNOUNCEMENT:
stream_name += MASS_ANNOUNCEMENT_POSTFIX
str(self.config.get_value(CONF_SERVER_CONTROL_PORT))
)
self._snapcast_stream_idle_threshold = self.config.get_value(CONF_STREAM_IDLE_THRESHOLD)
- self._stream_tasks = {}
self._ids_map = bidict({})
if self._use_builtin_server:
for snap_client in self._snapserver.clients:
if ma_player := self.mass.players.get(self._get_ma_id(snap_client.identifier)):
assert isinstance(ma_player, SnapCastPlayer) # for type checking
- snap_client.set_callback(ma_player._handle_player_update)
+ ma_player._handle_player_update(snap_client)
def _handle_disconnect(self, exc: Exception) -> None:
"""Handle disconnect callback from snapserver."""