From: Marvin Schenkel Date: Tue, 27 Jan 2026 10:24:03 +0000 (+0100) Subject: Fix player sources in Sonos S1 (#3030) X-Git-Url: https://git.kitaultman.com/?a=commitdiff_plain;h=5485d902a5102489a403bfb92cea405e475301d0;p=music-assistant-server.git Fix player sources in Sonos S1 (#3030) * Set active source to Line in source when selected. * Add PlayerSources for S1. * Add PlayerSources for S1. * Disable media when linein is active. Switch back to MA on stop. --- diff --git a/music_assistant/providers/sonos_s1/constants.py b/music_assistant/providers/sonos_s1/constants.py index 9ca1c190..44dee4c3 100644 --- a/music_assistant/providers/sonos_s1/constants.py +++ b/music_assistant/providers/sonos_s1/constants.py @@ -11,6 +11,8 @@ from soco.core import ( MUSIC_SRC_TV, ) +from music_assistant.models.player import PlayerSource + # Configuration Keys CONF_NETWORK_SCAN = "network_scan" CONF_HOUSEHOLD_ID = "household_id" @@ -22,6 +24,7 @@ PLAYER_FEATURES = ( PlayerFeature.VOLUME_SET, PlayerFeature.ENQUEUE, PlayerFeature.GAPLESS_PLAYBACK, + PlayerFeature.SELECT_SOURCE, ) # Source Mapping @@ -46,6 +49,42 @@ SOURCE_MAPPING = { } LINEIN_SOURCES = (MUSIC_SRC_TV, MUSIC_SRC_LINE_IN) +LINEIN_SOURCE_IDS = (SOURCE_TV, SOURCE_LINEIN) + +PLAYER_SOURCE_MAP = { + SOURCE_LINEIN: PlayerSource( + id=SOURCE_LINEIN, + name="Line-in", + passive=False, + can_play_pause=False, + can_next_previous=False, + can_seek=False, + ), + SOURCE_TV: PlayerSource( + id=SOURCE_TV, + name="TV", + passive=False, + can_play_pause=False, + can_next_previous=False, + can_seek=False, + ), + SOURCE_AIRPLAY: PlayerSource( + id=SOURCE_AIRPLAY, + name="AirPlay", + passive=True, + can_play_pause=True, + can_next_previous=True, + can_seek=True, + ), + SOURCE_SPOTIFY_CONNECT: PlayerSource( + id=SOURCE_SPOTIFY_CONNECT, + name="Spotify Connect", + passive=True, + can_play_pause=True, + can_next_previous=True, + can_seek=True, + ), +} # Playback State Mapping PLAYBACK_STATE_MAP = { diff --git a/music_assistant/providers/sonos_s1/player.py b/music_assistant/providers/sonos_s1/player.py index 1e4e57e2..821d9c45 100644 --- a/music_assistant/providers/sonos_s1/player.py +++ b/music_assistant/providers/sonos_s1/player.py @@ -26,9 +26,11 @@ from music_assistant.models.player import DeviceInfo, Player, PlayerMedia from .constants import ( DURATION_SECONDS, + LINEIN_SOURCE_IDS, LINEIN_SOURCES, NEVER_TIME, PLAYER_FEATURES, + PLAYER_SOURCE_MAP, POSITION_SECONDS, RESUB_COOLDOWN_SECONDS, SONOS_STATE_TRANSITIONING, @@ -140,7 +142,12 @@ class SonosPlayer(Player): self.player_id, ) return - await asyncio.to_thread(self.soco.stop) + if self._attr_active_source in LINEIN_SOURCE_IDS: + # Play an invalid URI to force stop line-in sources + with contextlib.suppress(SoCoException): + await asyncio.to_thread(self.soco.play_uri, "") + else: + await asyncio.to_thread(self.soco.stop) self.mass.call_later(2, self.poll) self.update_state() @@ -538,9 +545,13 @@ class SonosPlayer(Player): uri = track_info["uri"] audio_source = self.soco.music_source_from_uri(uri) - if SOURCE_MAPPING.get(audio_source) and audio_source in LINEIN_SOURCES: + if (source_id := SOURCE_MAPPING.get(audio_source)) and audio_source in LINEIN_SOURCES: self._attr_elapsed_time = None self._attr_elapsed_time_last_updated = None + self._attr_active_source = source_id + self._attr_current_media = None + if source_id not in [x.id for x in self._attr_source_list]: + self._attr_source_list.append(PLAYER_SOURCE_MAP[source_id]) return current_media = PlayerMedia( @@ -551,6 +562,7 @@ class SonosPlayer(Player): image_url=track_info.get("album_art"), ) self._attr_current_media = current_media + self._attr_active_source = None self._update_media_position(track_info, force_update=update_position) def _update_media_position(