Use DataUpdateCoordinator for Spotify devices (#66314)

This commit is contained in:
Franck Nijhof 2022-02-11 18:04:49 +01:00 committed by GitHub
parent 57624347e6
commit acf2033734
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 55 additions and 15 deletions

View File

@ -1,9 +1,12 @@
"""The spotify integration.""" """The spotify integration."""
from __future__ import annotations
from dataclasses import dataclass from dataclasses import dataclass
from datetime import timedelta
from typing import Any from typing import Any
import aiohttp import aiohttp
import requests
from spotipy import Spotify, SpotifyException from spotipy import Spotify, SpotifyException
import voluptuous as vol import voluptuous as vol
@ -22,10 +25,11 @@ from homeassistant.helpers.config_entry_oauth2_flow import (
async_get_config_entry_implementation, async_get_config_entry_implementation,
) )
from homeassistant.helpers.typing import ConfigType from homeassistant.helpers.typing import ConfigType
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
from . import config_flow from . import config_flow
from .browse_media import async_browse_media from .browse_media import async_browse_media
from .const import DOMAIN, SPOTIFY_SCOPES from .const import DOMAIN, LOGGER, SPOTIFY_SCOPES
from .util import is_spotify_media_type, resolve_spotify_media_type from .util import is_spotify_media_type, resolve_spotify_media_type
CONFIG_SCHEMA = vol.Schema( CONFIG_SCHEMA = vol.Schema(
@ -57,6 +61,7 @@ class HomeAssistantSpotifyData:
client: Spotify client: Spotify
current_user: dict[str, Any] current_user: dict[str, Any]
devices: DataUpdateCoordinator
session: OAuth2Session session: OAuth2Session
@ -101,10 +106,35 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
if not current_user: if not current_user:
raise ConfigEntryNotReady raise ConfigEntryNotReady
async def _update_devices() -> list[dict[str, Any]]:
try:
devices: dict[str, Any] | None = await hass.async_add_executor_job(
spotify.devices
)
except (requests.RequestException, SpotifyException) as err:
raise UpdateFailed from err
if devices is None:
return []
return devices.get("devices", [])
device_coordinator: DataUpdateCoordinator[
list[dict[str, Any]]
] = DataUpdateCoordinator(
hass,
LOGGER,
name=f"{entry.title} Devices",
update_interval=timedelta(minutes=5),
update_method=_update_devices,
)
await device_coordinator.async_config_entry_first_refresh()
hass.data.setdefault(DOMAIN, {}) hass.data.setdefault(DOMAIN, {})
hass.data[DOMAIN][entry.entry_id] = HomeAssistantSpotifyData( hass.data[DOMAIN][entry.entry_id] = HomeAssistantSpotifyData(
client=spotify, client=spotify,
current_user=current_user, current_user=current_user,
devices=device_coordinator,
session=session, session=session,
) )

View File

@ -1,4 +1,7 @@
"""Define constants for the Spotify integration.""" """Define constants for the Spotify integration."""
import logging
from homeassistant.components.media_player.const import ( from homeassistant.components.media_player.const import (
MEDIA_TYPE_ALBUM, MEDIA_TYPE_ALBUM,
MEDIA_TYPE_ARTIST, MEDIA_TYPE_ARTIST,
@ -9,6 +12,8 @@ from homeassistant.components.media_player.const import (
DOMAIN = "spotify" DOMAIN = "spotify"
LOGGER = logging.getLogger(__package__)
SPOTIFY_SCOPES = [ SPOTIFY_SCOPES = [
# Needed to be able to control playback # Needed to be able to control playback
"user-modify-playback-state", "user-modify-playback-state",

View File

@ -33,7 +33,7 @@ from homeassistant.components.media_player.const import (
) )
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_ID, STATE_IDLE, STATE_PAUSED, STATE_PLAYING from homeassistant.const import CONF_ID, STATE_IDLE, STATE_PAUSED, STATE_PLAYING
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import HomeAssistantError from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.device_registry import DeviceEntryType from homeassistant.helpers.device_registry import DeviceEntryType
from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.entity import DeviceInfo
@ -147,7 +147,6 @@ class SpotifyMediaPlayer(MediaPlayerEntity):
SPOTIFY_SCOPES SPOTIFY_SCOPES
) )
self._currently_playing: dict | None = {} self._currently_playing: dict | None = {}
self._devices: list[dict] | None = []
self._playlist: dict | None = None self._playlist: dict | None = None
@property @property
@ -258,9 +257,7 @@ class SpotifyMediaPlayer(MediaPlayerEntity):
@property @property
def source_list(self) -> list[str] | None: def source_list(self) -> list[str] | None:
"""Return a list of source devices.""" """Return a list of source devices."""
if not self._devices: return [device["name"] for device in self.data.devices.data]
return None
return [device["name"] for device in self._devices]
@property @property
def shuffle(self) -> bool | None: def shuffle(self) -> bool | None:
@ -332,19 +329,16 @@ class SpotifyMediaPlayer(MediaPlayerEntity):
if ( if (
self._currently_playing self._currently_playing
and not self._currently_playing.get("device") and not self._currently_playing.get("device")
and self._devices and self.data.devices.data
): ):
kwargs["device_id"] = self._devices[0].get("id") kwargs["device_id"] = self.data.devices.data[0].get("id")
self.data.client.start_playback(**kwargs) self.data.client.start_playback(**kwargs)
@spotify_exception_handler @spotify_exception_handler
def select_source(self, source: str) -> None: def select_source(self, source: str) -> None:
"""Select playback device.""" """Select playback device."""
if not self._devices: for device in self.data.devices.data:
return
for device in self._devices:
if device["name"] == source: if device["name"] == source:
self.data.client.transfer_playback( self.data.client.transfer_playback(
device["id"], self.state == STATE_PLAYING device["id"], self.state == STATE_PLAYING
@ -386,9 +380,6 @@ class SpotifyMediaPlayer(MediaPlayerEntity):
if context["type"] == MEDIA_TYPE_PLAYLIST: if context["type"] == MEDIA_TYPE_PLAYLIST:
self._playlist = self.data.client.playlist(current["context"]["uri"]) self._playlist = self.data.client.playlist(current["context"]["uri"])
devices = self.data.client.devices() or {}
self._devices = devices.get("devices", [])
async def async_browse_media( async def async_browse_media(
self, media_content_type: str | None = None, media_content_id: str | None = None self, media_content_type: str | None = None, media_content_id: str | None = None
) -> BrowseMedia: ) -> BrowseMedia:
@ -408,3 +399,17 @@ class SpotifyMediaPlayer(MediaPlayerEntity):
media_content_type, media_content_type,
media_content_id, media_content_id,
) )
@callback
def _handle_devices_update(self) -> None:
"""Handle updated data from the coordinator."""
if not self.enabled:
return
self.async_write_ha_state()
async def async_added_to_hass(self) -> None:
"""When entity is added to hass."""
await super().async_added_to_hass()
self.async_on_remove(
self.data.devices.async_add_listener(self._handle_devices_update)
)