Skip to content

Commit

Permalink
Fix: Sonos airplay mode infinite loop
Browse files Browse the repository at this point in the history
  • Loading branch information
marcelveldt committed Nov 16, 2024
1 parent e07eb33 commit c710d9b
Show file tree
Hide file tree
Showing 2 changed files with 21 additions and 7 deletions.
13 changes: 8 additions & 5 deletions music_assistant/providers/sonos/player.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ def get_linked_airplay_player(
return None
if not airplay_player.available:
return None
if active_only and not airplay_player.powered:
if active_only and not airplay_player.powered and not airplay_player.group_childs:
return None
return airplay_player

Expand Down Expand Up @@ -182,7 +182,8 @@ async def cmd_stop(self) -> None:
) and airplay.state != PlayerState.IDLE:
# linked airplay player is active, redirect the command
self.logger.debug("Redirecting STOP command to linked airplay player.")
await self.mass.players.cmd_stop(airplay.player_id)
if player_provider := self.mass.get_provider(airplay.provider):
await player_provider.cmd_stop(airplay.player_id)
return
try:
await self.client.player.group.stop()
Expand All @@ -200,7 +201,8 @@ async def cmd_play(self) -> None:
) and airplay.state != PlayerState.IDLE:
# linked airplay player is active, redirect the command
self.logger.debug("Redirecting PLAY command to linked airplay player.")
await self.mass.players.cmd_play(airplay.player_id)
if player_provider := self.mass.get_provider(airplay.provider):
await player_provider.cmd_play(airplay.player_id)
return
await self.client.player.group.play()

Expand All @@ -214,7 +216,8 @@ async def cmd_pause(self) -> None:
) and airplay.state != PlayerState.IDLE:
# linked airplay player is active, redirect the command
self.logger.debug("Redirecting PAUSE command to linked airplay player.")
await self.mass.players.cmd_pause(airplay.player_id)
if player_provider := self.mass.get_provider(airplay.provider):
await player_provider.cmd_pause(airplay.player_id)
return
await self.client.player.group.pause()

Expand Down Expand Up @@ -264,7 +267,7 @@ def update_attributes(self) -> None: # noqa: PLR0915
self.mass_player.synced_to = active_group.coordinator_id
self.mass_player.active_source = active_group.coordinator_id

if airplay := self.get_linked_airplay_player(True):
if airplay := self.get_linked_airplay_player(True, True):
# linked airplay player is active, update media from there
self.mass_player.state = airplay.state
self.mass_player.powered = airplay.powered
Expand Down
15 changes: 13 additions & 2 deletions music_assistant/providers/sonos/provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,22 +133,33 @@ async def get_player_config_entries(
return base_entries
return (
*base_entries,
ConfigEntry(
key="airplay_detected",
type=ConfigEntryType.BOOLEAN,
label="airplay_detected",
hidden=True,
required=False,
default_value=sonos_player.get_linked_airplay_player(False) is not None,
),
ConfigEntry(
key=CONF_AIRPLAY_MODE,
type=ConfigEntryType.BOOLEAN,
label="Enable Airplay mode (experimental)",
description="Almost all newer Sonos speakers have Airplay support. "
"If you have the Airplay provider enabled in Music Assistant, "
"your Sonos speakers will also be detected as Airplay speakers, meaning "
"your Sonos speaker will also be detected as a Airplay speaker, meaning "
"you can group them with other Airplay speakers.\n\n"
"By default, Music Assistant uses the Sonos protocol for playback but with this "
"feature enabled, it will use the Airplay protocol instead by redirecting "
"the playback related commands to the linked Airplay player in Music Assistant, "
"allowing you to mix and match Sonos speakers with Airplay speakers. \n\n"
"NOTE: You need to have the Airplay provider enabled. "
"Also make sure that the Airplay version of this player is enabled. \n\n"
"TIP: When this feature is enabled, it make sense to set the underlying airplay "
"players to hide in the UI in the player settings to prevent duplicate players.",
required=False,
default_value=False,
depends_on="airplay_detected",
hidden=SonosCapability.AIRPLAY
not in sonos_player.discovery_info["device"]["capabilities"],
),
Expand Down Expand Up @@ -224,7 +235,7 @@ async def play_media(
raise PlayerCommandFailed(msg)
# for now always reset the active session
sonos_player.client.player.group.active_session_id = None
if airplay := sonos_player.get_linked_airplay_player(True):
if airplay := sonos_player.get_linked_airplay_player(True, True):
# linked airplay player is active, redirect the command
self.logger.debug("Redirecting PLAY_MEDIA command to linked airplay player.")
mass_player.active_source = airplay.active_source
Expand Down

0 comments on commit c710d9b

Please sign in to comment.