Make Spotify polling interval dynamic (#136461)

This commit is contained in:
Joost Lekkerkerker 2025-01-24 22:03:46 +01:00 committed by GitHub
parent 7363413d3d
commit f5fc46a7be
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 155 additions and 1 deletions

View File

@ -31,6 +31,9 @@ _LOGGER = logging.getLogger(__name__)
type SpotifyConfigEntry = ConfigEntry[SpotifyData]
UPDATE_INTERVAL = timedelta(seconds=30)
@dataclass
class SpotifyCoordinatorData:
"""Class to hold Spotify data."""
@ -59,7 +62,7 @@ class SpotifyCoordinator(DataUpdateCoordinator[SpotifyCoordinatorData]):
hass,
_LOGGER,
name=DOMAIN,
update_interval=timedelta(seconds=30),
update_interval=UPDATE_INTERVAL,
)
self.client = client
self._playlist: Playlist | None = None
@ -73,6 +76,7 @@ class SpotifyCoordinator(DataUpdateCoordinator[SpotifyCoordinatorData]):
raise UpdateFailed("Error communicating with Spotify API") from err
async def _async_update_data(self) -> SpotifyCoordinatorData:
self.update_interval = UPDATE_INTERVAL
try:
current = await self.client.get_playback()
except SpotifyConnectionError as err:
@ -120,6 +124,13 @@ class SpotifyCoordinator(DataUpdateCoordinator[SpotifyCoordinatorData]):
)
self._playlist = None
self._checked_playlist_id = None
if current.is_playing and current.progress_ms is not None:
assert current.item is not None
time_left = timedelta(
milliseconds=current.item.duration_ms - current.progress_ms
)
if time_left < UPDATE_INTERVAL:
self.update_interval = time_left + timedelta(seconds=1)
return SpotifyCoordinatorData(
current_playback=current,
position_updated_at=position_updated_at,

View File

@ -641,3 +641,146 @@ async def test_no_album_images(
state = hass.states.get("media_player.spotify_spotify_1")
assert state
assert ATTR_ENTITY_PICTURE not in state.attributes
@pytest.mark.usefixtures("setup_credentials")
async def test_normal_polling_interval(
hass: HomeAssistant,
mock_spotify: MagicMock,
mock_config_entry: MockConfigEntry,
freezer: FrozenDateTimeFactory,
) -> None:
"""Test the Spotify media player polling interval."""
await setup_integration(hass, mock_config_entry)
assert mock_spotify.return_value.get_playback.return_value.is_playing is True
assert (
mock_spotify.return_value.get_playback.return_value.progress_ms
- mock_spotify.return_value.get_playback.return_value.item.duration_ms
< 30000
)
mock_spotify.return_value.get_playback.assert_called_once()
mock_spotify.return_value.get_playback.reset_mock()
freezer.tick(timedelta(seconds=30))
async_fire_time_changed(hass)
await hass.async_block_till_done()
mock_spotify.return_value.get_playback.assert_called_once()
@pytest.mark.usefixtures("setup_credentials")
async def test_smart_polling_interval(
hass: HomeAssistant,
mock_spotify: MagicMock,
mock_config_entry: MockConfigEntry,
freezer: FrozenDateTimeFactory,
) -> None:
"""Test the Spotify media player polling interval."""
mock_spotify.return_value.get_playback.return_value.progress_ms = 10000
mock_spotify.return_value.get_playback.return_value.item.duration_ms = 30000
await setup_integration(hass, mock_config_entry)
mock_spotify.return_value.get_playback.assert_called_once()
mock_spotify.return_value.get_playback.reset_mock()
freezer.tick(timedelta(seconds=20))
async_fire_time_changed(hass)
await hass.async_block_till_done()
mock_spotify.return_value.get_playback.assert_not_called()
mock_spotify.return_value.get_playback.return_value.progress_ms = 10000
mock_spotify.return_value.get_playback.return_value.item.duration_ms = 50000
freezer.tick(timedelta(seconds=1))
async_fire_time_changed(hass)
await hass.async_block_till_done()
mock_spotify.return_value.get_playback.assert_called_once()
mock_spotify.return_value.get_playback.reset_mock()
freezer.tick(timedelta(seconds=21))
async_fire_time_changed(hass)
await hass.async_block_till_done()
mock_spotify.return_value.get_playback.assert_not_called()
freezer.tick(timedelta(seconds=9))
async_fire_time_changed(hass)
await hass.async_block_till_done()
mock_spotify.return_value.get_playback.assert_called_once()
mock_spotify.return_value.get_playback.reset_mock()
@pytest.mark.usefixtures("setup_credentials")
async def test_smart_polling_interval_handles_errors(
hass: HomeAssistant,
mock_spotify: MagicMock,
mock_config_entry: MockConfigEntry,
freezer: FrozenDateTimeFactory,
) -> None:
"""Test the Spotify media player polling interval."""
mock_spotify.return_value.get_playback.return_value.progress_ms = 10000
mock_spotify.return_value.get_playback.return_value.item.duration_ms = 30000
await setup_integration(hass, mock_config_entry)
mock_spotify.return_value.get_playback.assert_called_once()
mock_spotify.return_value.get_playback.reset_mock()
mock_spotify.return_value.get_playback.side_effect = SpotifyConnectionError
freezer.tick(timedelta(seconds=21))
async_fire_time_changed(hass)
await hass.async_block_till_done()
mock_spotify.return_value.get_playback.assert_called_once()
mock_spotify.return_value.get_playback.reset_mock()
freezer.tick(timedelta(seconds=21))
async_fire_time_changed(hass)
await hass.async_block_till_done()
mock_spotify.return_value.get_playback.assert_not_called()
freezer.tick(timedelta(seconds=9))
async_fire_time_changed(hass)
await hass.async_block_till_done()
mock_spotify.return_value.get_playback.assert_called_once()
mock_spotify.return_value.get_playback.reset_mock()
@pytest.mark.usefixtures("setup_credentials")
async def test_smart_polling_interval_handles_paused(
hass: HomeAssistant,
mock_spotify: MagicMock,
mock_config_entry: MockConfigEntry,
freezer: FrozenDateTimeFactory,
) -> None:
"""Test the Spotify media player polling interval."""
mock_spotify.return_value.get_playback.return_value.progress_ms = 10000
mock_spotify.return_value.get_playback.return_value.item.duration_ms = 30000
mock_spotify.return_value.get_playback.return_value.is_playing = False
await setup_integration(hass, mock_config_entry)
mock_spotify.return_value.get_playback.assert_called_once()
mock_spotify.return_value.get_playback.reset_mock()
freezer.tick(timedelta(seconds=21))
async_fire_time_changed(hass)
await hass.async_block_till_done()
mock_spotify.return_value.get_playback.assert_not_called()
freezer.tick(timedelta(seconds=9))
async_fire_time_changed(hass)
await hass.async_block_till_done()
mock_spotify.return_value.get_playback.assert_called_once()
mock_spotify.return_value.get_playback.reset_mock()