diff --git a/homeassistant/components/spotify/media_player.py b/homeassistant/components/spotify/media_player.py index dce200bc598..7687936fe4c 100644 --- a/homeassistant/components/spotify/media_player.py +++ b/homeassistant/components/spotify/media_player.py @@ -2,10 +2,11 @@ from __future__ import annotations -from collections.abc import Callable +import asyncio +from collections.abc import Awaitable, Callable, Coroutine import datetime as dt import logging -from typing import TYPE_CHECKING, Any +from typing import TYPE_CHECKING, Any, Concatenate from spotifyaio import ( Device, @@ -63,6 +64,7 @@ REPEAT_MODE_MAPPING_TO_HA = { REPEAT_MODE_MAPPING_TO_SPOTIFY = { value: key for key, value in REPEAT_MODE_MAPPING_TO_HA.items() } +AFTER_REQUEST_SLEEP = 1 async def async_setup_entry( @@ -93,6 +95,19 @@ def ensure_item[_R]( return wrapper +def async_refresh_after[_T: SpotifyEntity, **_P]( + func: Callable[Concatenate[_T, _P], Awaitable[None]], +) -> Callable[Concatenate[_T, _P], Coroutine[Any, Any, None]]: + """Define a wrapper to yield and refresh after.""" + + async def _async_wrap(self: _T, *args: _P.args, **kwargs: _P.kwargs) -> None: + await func(self, *args, **kwargs) + await asyncio.sleep(AFTER_REQUEST_SLEEP) + await self.coordinator.async_refresh() + + return _async_wrap + + class SpotifyMediaPlayer(SpotifyEntity, MediaPlayerEntity): """Representation of a Spotify controller.""" @@ -267,30 +282,37 @@ class SpotifyMediaPlayer(SpotifyEntity, MediaPlayerEntity): return None return REPEAT_MODE_MAPPING_TO_HA.get(self.currently_playing.repeat_mode) + @async_refresh_after async def async_set_volume_level(self, volume: float) -> None: """Set the volume level.""" await self.coordinator.client.set_volume(int(volume * 100)) + @async_refresh_after async def async_media_play(self) -> None: """Start or resume playback.""" await self.coordinator.client.start_playback() + @async_refresh_after async def async_media_pause(self) -> None: """Pause playback.""" await self.coordinator.client.pause_playback() + @async_refresh_after async def async_media_previous_track(self) -> None: """Skip to previous track.""" await self.coordinator.client.previous_track() + @async_refresh_after async def async_media_next_track(self) -> None: """Skip to next track.""" await self.coordinator.client.next_track() + @async_refresh_after async def async_media_seek(self, position: float) -> None: """Send seek command.""" await self.coordinator.client.seek_track(int(position * 1000)) + @async_refresh_after async def async_play_media( self, media_type: MediaType | str, media_id: str, **kwargs: Any ) -> None: @@ -334,6 +356,7 @@ class SpotifyMediaPlayer(SpotifyEntity, MediaPlayerEntity): await self.coordinator.client.start_playback(**kwargs) + @async_refresh_after async def async_select_source(self, source: str) -> None: """Select playback device.""" for device in self.devices.data: @@ -341,10 +364,12 @@ class SpotifyMediaPlayer(SpotifyEntity, MediaPlayerEntity): await self.coordinator.client.transfer_playback(device.device_id) return + @async_refresh_after async def async_set_shuffle(self, shuffle: bool) -> None: """Enable/Disable shuffle mode.""" await self.coordinator.client.set_shuffle(state=shuffle) + @async_refresh_after async def async_set_repeat(self, repeat: RepeatMode) -> None: """Set repeat mode.""" if repeat not in REPEAT_MODE_MAPPING_TO_SPOTIFY: diff --git a/tests/components/spotify/conftest.py b/tests/components/spotify/conftest.py index 5d86045e5a8..d3fc418f1cd 100644 --- a/tests/components/spotify/conftest.py +++ b/tests/components/spotify/conftest.py @@ -84,6 +84,13 @@ async def setup_credentials(hass: HomeAssistant) -> None: ) +@pytest.fixture(autouse=True) +async def patch_sleep() -> Generator[AsyncMock]: + """Fixture to setup credentials.""" + with patch("homeassistant.components.spotify.media_player.AFTER_REQUEST_SLEEP", 0): + yield + + @pytest.fixture def mock_spotify() -> Generator[AsyncMock]: """Mock the Spotify API."""