diff --git a/homeassistant/components/spotify/__init__.py b/homeassistant/components/spotify/__init__.py index d05d376f67f..adefe23e316 100644 --- a/homeassistant/components/spotify/__init__.py +++ b/homeassistant/components/spotify/__init__.py @@ -21,7 +21,7 @@ from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, Upda from .browse_media import async_browse_media from .const import DOMAIN, LOGGER, SPOTIFY_SCOPES -from .coordinator import SpotifyCoordinator +from .coordinator import SpotifyConfigEntry, SpotifyCoordinator from .models import SpotifyData from .util import ( is_spotify_media_type, @@ -40,9 +40,6 @@ __all__ = [ ] -type SpotifyConfigEntry = ConfigEntry[SpotifyData] - - async def async_setup_entry(hass: HomeAssistant, entry: SpotifyConfigEntry) -> bool: """Set up Spotify from a config entry.""" implementation = await async_get_config_entry_implementation(hass, entry) diff --git a/homeassistant/components/spotify/coordinator.py b/homeassistant/components/spotify/coordinator.py index 556ad88127b..4a8c6885f9f 100644 --- a/homeassistant/components/spotify/coordinator.py +++ b/homeassistant/components/spotify/coordinator.py @@ -3,6 +3,7 @@ from dataclasses import dataclass from datetime import datetime, timedelta import logging +from typing import TYPE_CHECKING from spotifyaio import ( ContextType, @@ -15,15 +16,22 @@ from spotifyaio import ( ) from spotifyaio.models import AudioFeatures +from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed import homeassistant.util.dt as dt_util from .const import DOMAIN +if TYPE_CHECKING: + from .models import SpotifyData + _LOGGER = logging.getLogger(__name__) +type SpotifyConfigEntry = ConfigEntry[SpotifyData] + + @dataclass class SpotifyCoordinatorData: """Class to hold Spotify data.""" @@ -45,6 +53,7 @@ class SpotifyCoordinator(DataUpdateCoordinator[SpotifyCoordinatorData]): """Class to manage fetching Spotify data.""" current_user: UserProfile + config_entry: SpotifyConfigEntry def __init__(self, hass: HomeAssistant, client: SpotifyClient) -> None: """Initialize.""" diff --git a/homeassistant/components/spotify/diagnostics.py b/homeassistant/components/spotify/diagnostics.py index 6acce72a951..82ce40eb22a 100644 --- a/homeassistant/components/spotify/diagnostics.py +++ b/homeassistant/components/spotify/diagnostics.py @@ -7,7 +7,7 @@ from typing import Any from homeassistant.core import HomeAssistant -from . import SpotifyConfigEntry +from .coordinator import SpotifyConfigEntry async def async_get_config_entry_diagnostics( diff --git a/homeassistant/components/spotify/entity.py b/homeassistant/components/spotify/entity.py new file mode 100644 index 00000000000..6ab82977089 --- /dev/null +++ b/homeassistant/components/spotify/entity.py @@ -0,0 +1,25 @@ +"""Base entity for Spotify.""" + +from homeassistant.helpers.device_registry import DeviceEntryType, DeviceInfo +from homeassistant.helpers.update_coordinator import CoordinatorEntity + +from .const import DOMAIN +from .coordinator import SpotifyCoordinator + + +class SpotifyEntity(CoordinatorEntity[SpotifyCoordinator]): + """Defines a base Spotify entity.""" + + _attr_has_entity_name = True + + def __init__(self, coordinator: SpotifyCoordinator) -> None: + """Initialize the Spotify entity.""" + super().__init__(coordinator) + self._attr_device_info = DeviceInfo( + identifiers={(DOMAIN, coordinator.current_user.user_id)}, + manufacturer="Spotify AB", + model=f"Spotify {coordinator.current_user.product}", + name=f"Spotify {coordinator.config_entry.title}", + entry_type=DeviceEntryType.SERVICE, + configuration_url="https://open.spotify.com", + ) diff --git a/homeassistant/components/spotify/media_player.py b/homeassistant/components/spotify/media_player.py index 72c6d76eb96..dce200bc598 100644 --- a/homeassistant/components/spotify/media_player.py +++ b/homeassistant/components/spotify/media_player.py @@ -30,17 +30,13 @@ from homeassistant.components.media_player import ( RepeatMode, ) from homeassistant.core import HomeAssistant, callback -from homeassistant.helpers.device_registry import DeviceEntryType, DeviceInfo from homeassistant.helpers.entity_platform import AddEntitiesCallback -from homeassistant.helpers.update_coordinator import ( - CoordinatorEntity, - DataUpdateCoordinator, -) +from homeassistant.helpers.update_coordinator import DataUpdateCoordinator -from . import SpotifyConfigEntry from .browse_media import async_browse_media_internal -from .const import DOMAIN, MEDIA_PLAYER_PREFIX, PLAYABLE_MEDIA_TYPES -from .coordinator import SpotifyCoordinator +from .const import MEDIA_PLAYER_PREFIX, PLAYABLE_MEDIA_TYPES +from .coordinator import SpotifyConfigEntry, SpotifyCoordinator +from .entity import SpotifyEntity _LOGGER = logging.getLogger(__name__) @@ -80,8 +76,6 @@ async def async_setup_entry( spotify = SpotifyMediaPlayer( data.coordinator, data.devices, - entry.unique_id, - entry.title, ) async_add_entities([spotify]) @@ -99,10 +93,9 @@ def ensure_item[_R]( return wrapper -class SpotifyMediaPlayer(CoordinatorEntity[SpotifyCoordinator], MediaPlayerEntity): +class SpotifyMediaPlayer(SpotifyEntity, MediaPlayerEntity): """Representation of a Spotify controller.""" - _attr_has_entity_name = True _attr_media_image_remotely_accessible = False _attr_name = None _attr_translation_key = "spotify" @@ -111,23 +104,11 @@ class SpotifyMediaPlayer(CoordinatorEntity[SpotifyCoordinator], MediaPlayerEntit self, coordinator: SpotifyCoordinator, device_coordinator: DataUpdateCoordinator[list[Device]], - user_id: str, - name: str, ) -> None: """Initialize.""" super().__init__(coordinator) self.devices = device_coordinator - - self._attr_unique_id = user_id - - self._attr_device_info = DeviceInfo( - identifiers={(DOMAIN, user_id)}, - manufacturer="Spotify AB", - model=f"Spotify {coordinator.current_user.product}", - name=f"Spotify {name}", - entry_type=DeviceEntryType.SERVICE, - configuration_url="https://open.spotify.com", - ) + self._attr_unique_id = coordinator.current_user.user_id @property def currently_playing(self) -> PlaybackState | None: diff --git a/homeassistant/components/spotify/sensor.py b/homeassistant/components/spotify/sensor.py index bf3fd8b07d0..96b390ec907 100644 --- a/homeassistant/components/spotify/sensor.py +++ b/homeassistant/components/spotify/sensor.py @@ -7,12 +7,10 @@ from spotifyaio.models import AudioFeatures from homeassistant.components.sensor import SensorEntity, SensorEntityDescription from homeassistant.core import HomeAssistant -from homeassistant.helpers.device_registry import DeviceEntryType, DeviceInfo from homeassistant.helpers.entity_platform import AddEntitiesCallback -from homeassistant.helpers.update_coordinator import CoordinatorEntity -from . import DOMAIN, SpotifyConfigEntry -from .coordinator import SpotifyCoordinator +from .coordinator import SpotifyConfigEntry, SpotifyCoordinator +from .entity import SpotifyEntity @dataclass(frozen=True, kw_only=True) @@ -41,41 +39,28 @@ async def async_setup_entry( """Set up Spotify sensor based on a config entry.""" coordinator = entry.runtime_data.coordinator - user_id = entry.unique_id - - assert user_id is not None - async_add_entities( - SpotifyAudioFeatureSensor(coordinator, description, user_id, entry.title) + SpotifyAudioFeatureSensor(coordinator, description) for description in AUDIO_FEATURE_SENSORS ) -class SpotifyAudioFeatureSensor(CoordinatorEntity[SpotifyCoordinator], SensorEntity): +class SpotifyAudioFeatureSensor(SpotifyEntity, SensorEntity): """Representation of a Spotify sensor.""" - _attr_has_entity_name = True entity_description: SpotifyAudioFeaturesSensorEntityDescription def __init__( self, coordinator: SpotifyCoordinator, entity_description: SpotifyAudioFeaturesSensorEntityDescription, - user_id: str, - name: str, ) -> None: """Initialize.""" super().__init__(coordinator) - self._attr_unique_id = f"{user_id}_{entity_description.key}" - self.entity_description = entity_description - self._attr_device_info = DeviceInfo( - identifiers={(DOMAIN, user_id)}, - manufacturer="Spotify AB", - model=f"Spotify {coordinator.current_user.product}", - name=f"Spotify {name}", - entry_type=DeviceEntryType.SERVICE, - configuration_url="https://open.spotify.com", + self._attr_unique_id = ( + f"{coordinator.current_user.user_id}_{entity_description.key}" ) + self.entity_description = entity_description @property def native_value(self) -> float | None: