From 5a0841155ef6ce076f4f30d93c58cb4a2e3dcf38 Mon Sep 17 00:00:00 2001 From: Joost Lekkerkerker Date: Wed, 26 Jun 2024 09:28:11 +0200 Subject: [PATCH] Add unique_id to MPD (#120495) --- homeassistant/components/mpd/media_player.py | 85 +++++++------------- 1 file changed, 30 insertions(+), 55 deletions(-) diff --git a/homeassistant/components/mpd/media_player.py b/homeassistant/components/mpd/media_player.py index f0df2cdbbe2..204bbc7f499 100644 --- a/homeassistant/components/mpd/media_player.py +++ b/homeassistant/components/mpd/media_player.py @@ -31,6 +31,7 @@ from homeassistant.const import CONF_HOST, CONF_NAME, CONF_PASSWORD, CONF_PORT from homeassistant.core import DOMAIN as HOMEASSISTANT_DOMAIN, HomeAssistant from homeassistant.data_entry_flow import FlowResultType import homeassistant.helpers.config_validation as cv +from homeassistant.helpers.device_registry import DeviceEntryType, DeviceInfo from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.issue_registry import IssueSeverity, async_create_issue from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType @@ -129,7 +130,7 @@ async def async_setup_entry( entry.data[CONF_HOST], entry.data[CONF_PORT], entry.data.get(CONF_PASSWORD), - entry.title, + entry.entry_id, ) ], True, @@ -140,23 +141,26 @@ class MpdDevice(MediaPlayerEntity): """Representation of a MPD server.""" _attr_media_content_type = MediaType.MUSIC + _attr_has_entity_name = True + _attr_name = None - def __init__(self, server, port, password, name): + def __init__( + self, server: str, port: int, password: str | None, unique_id: str + ) -> None: """Initialize the MPD device.""" self.server = server self.port = port - self._name = name + self._attr_unique_id = unique_id + self._attr_device_info = DeviceInfo( + identifiers={(DOMAIN, unique_id)}, + entry_type=DeviceEntryType.SERVICE, + ) self.password = password - self._status = {} + self._status: dict[str, Any] = {} self._currentsong = None - self._playlists = None - self._currentplaylist = None - self._is_available = None - self._muted = False + self._current_playlist: str | None = None self._muted_volume = None - self._media_position_updated_at = None - self._media_position = None self._media_image_hash = None # Track if the song changed so image doesn't have to be loaded every update. self._media_image_file = None @@ -188,7 +192,7 @@ class MpdDevice(MediaPlayerEntity): raise TimeoutError("Connection attempt timed out") from error if self.password is not None: await self._client.password(self.password) - self._is_available = True + self._attr_available = True yield except ( TimeoutError, @@ -199,12 +203,12 @@ class MpdDevice(MediaPlayerEntity): # Log a warning during startup or when previously connected; for # subsequent errors a debug message is sufficient. log_level = logging.DEBUG - if self._is_available is not False: + if self._attr_available is not False: log_level = logging.WARNING LOGGER.log( log_level, "Error connecting to '%s': %s", self.server, error ) - self._is_available = False + self._attr_available = False self._status = {} # Also yield on failure. Handling mpd.ConnectionErrors caused by # attempting to control a disconnected client is the @@ -228,24 +232,14 @@ class MpdDevice(MediaPlayerEntity): if isinstance(position, str) and ":" in position: position = position.split(":")[0] - if position is not None and self._media_position != position: - self._media_position_updated_at = dt_util.utcnow() - self._media_position = int(float(position)) + if position is not None and self._attr_media_position != position: + self._attr_media_position_updated_at = dt_util.utcnow() + self._attr_media_position = int(float(position)) await self._update_playlists() except (mpd.ConnectionError, ValueError) as error: LOGGER.debug("Error updating status: %s", error) - @property - def available(self) -> bool: - """Return true if MPD is available and connected.""" - return self._is_available is True - - @property - def name(self): - """Return the name of the device.""" - return self._name - @property def state(self) -> MediaPlayerState: """Return the media state.""" @@ -260,11 +254,6 @@ class MpdDevice(MediaPlayerEntity): return MediaPlayerState.OFF - @property - def is_volume_muted(self): - """Boolean if volume is currently muted.""" - return self._muted - @property def media_content_id(self): """Return the content ID of current playing media.""" @@ -282,20 +271,6 @@ class MpdDevice(MediaPlayerEntity): return None - @property - def media_position(self): - """Position of current playing media in seconds. - - This is returned as part of the mpd status rather than in the details - of the current song. - """ - return self._media_position - - @property - def media_position_updated_at(self): - """Last valid time of media position.""" - return self._media_position_updated_at - @property def media_title(self): """Return the title of current playing media.""" @@ -436,7 +411,7 @@ class MpdDevice(MediaPlayerEntity): | MediaPlayerEntityFeature.VOLUME_STEP | MediaPlayerEntityFeature.VOLUME_MUTE ) - if self._playlists is not None: + if self._attr_source_list is not None: supported |= MediaPlayerEntityFeature.SELECT_SOURCE return supported @@ -444,7 +419,7 @@ class MpdDevice(MediaPlayerEntity): @property def source(self): """Name of the current input source.""" - return self._currentplaylist + return self._current_playlist @property def source_list(self): @@ -459,12 +434,12 @@ class MpdDevice(MediaPlayerEntity): async def _update_playlists(self, **kwargs: Any) -> None: """Update available MPD playlists.""" try: - self._playlists = [] + self._attr_source_list = [] with suppress(mpd.ConnectionError): for playlist_data in await self._client.listplaylists(): - self._playlists.append(playlist_data["playlist"]) + self._attr_source_list.append(playlist_data["playlist"]) except mpd.CommandError as error: - self._playlists = None + self._attr_source_list = None LOGGER.warning("Playlists could not be updated: %s:", error) async def async_set_volume_level(self, volume: float) -> None: @@ -527,7 +502,7 @@ class MpdDevice(MediaPlayerEntity): await self.async_set_volume_level(0) elif self._muted_volume is not None: await self.async_set_volume_level(self._muted_volume) - self._muted = mute + self._attr_is_volume_muted = mute async def async_play_media( self, media_type: MediaType | str, media_id: str, **kwargs: Any @@ -543,17 +518,17 @@ class MpdDevice(MediaPlayerEntity): if media_type == MediaType.PLAYLIST: LOGGER.debug("Playing playlist: %s", media_id) - if media_id in self._playlists: - self._currentplaylist = media_id + if self._attr_source_list and media_id in self._attr_source_list: + self._current_playlist = media_id else: - self._currentplaylist = None + self._current_playlist = None LOGGER.warning("Unknown playlist name %s", media_id) await self._client.clear() await self._client.load(media_id) await self._client.play() else: await self._client.clear() - self._currentplaylist = None + self._current_playlist = None await self._client.add(media_id) await self._client.play()