Add unique_id to MPD (#120495)

This commit is contained in:
Joost Lekkerkerker
2024-06-26 09:28:11 +02:00
committed by GitHub
parent bff9d12cc0
commit 5a0841155e

View File

@@ -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.core import DOMAIN as HOMEASSISTANT_DOMAIN, HomeAssistant
from homeassistant.data_entry_flow import FlowResultType from homeassistant.data_entry_flow import FlowResultType
import homeassistant.helpers.config_validation as cv 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.entity_platform import AddEntitiesCallback
from homeassistant.helpers.issue_registry import IssueSeverity, async_create_issue from homeassistant.helpers.issue_registry import IssueSeverity, async_create_issue
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
@@ -129,7 +130,7 @@ async def async_setup_entry(
entry.data[CONF_HOST], entry.data[CONF_HOST],
entry.data[CONF_PORT], entry.data[CONF_PORT],
entry.data.get(CONF_PASSWORD), entry.data.get(CONF_PASSWORD),
entry.title, entry.entry_id,
) )
], ],
True, True,
@@ -140,23 +141,26 @@ class MpdDevice(MediaPlayerEntity):
"""Representation of a MPD server.""" """Representation of a MPD server."""
_attr_media_content_type = MediaType.MUSIC _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.""" """Initialize the MPD device."""
self.server = server self.server = server
self.port = port 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.password = password
self._status = {} self._status: dict[str, Any] = {}
self._currentsong = None self._currentsong = None
self._playlists = None self._current_playlist: str | None = None
self._currentplaylist = None
self._is_available = None
self._muted = False
self._muted_volume = None self._muted_volume = None
self._media_position_updated_at = None
self._media_position = None
self._media_image_hash = None self._media_image_hash = None
# Track if the song changed so image doesn't have to be loaded every update. # Track if the song changed so image doesn't have to be loaded every update.
self._media_image_file = None self._media_image_file = None
@@ -188,7 +192,7 @@ class MpdDevice(MediaPlayerEntity):
raise TimeoutError("Connection attempt timed out") from error raise TimeoutError("Connection attempt timed out") from error
if self.password is not None: if self.password is not None:
await self._client.password(self.password) await self._client.password(self.password)
self._is_available = True self._attr_available = True
yield yield
except ( except (
TimeoutError, TimeoutError,
@@ -199,12 +203,12 @@ class MpdDevice(MediaPlayerEntity):
# Log a warning during startup or when previously connected; for # Log a warning during startup or when previously connected; for
# subsequent errors a debug message is sufficient. # subsequent errors a debug message is sufficient.
log_level = logging.DEBUG log_level = logging.DEBUG
if self._is_available is not False: if self._attr_available is not False:
log_level = logging.WARNING log_level = logging.WARNING
LOGGER.log( LOGGER.log(
log_level, "Error connecting to '%s': %s", self.server, error log_level, "Error connecting to '%s': %s", self.server, error
) )
self._is_available = False self._attr_available = False
self._status = {} self._status = {}
# Also yield on failure. Handling mpd.ConnectionErrors caused by # Also yield on failure. Handling mpd.ConnectionErrors caused by
# attempting to control a disconnected client is the # attempting to control a disconnected client is the
@@ -228,24 +232,14 @@ class MpdDevice(MediaPlayerEntity):
if isinstance(position, str) and ":" in position: if isinstance(position, str) and ":" in position:
position = position.split(":")[0] position = position.split(":")[0]
if position is not None and self._media_position != position: if position is not None and self._attr_media_position != position:
self._media_position_updated_at = dt_util.utcnow() self._attr_media_position_updated_at = dt_util.utcnow()
self._media_position = int(float(position)) self._attr_media_position = int(float(position))
await self._update_playlists() await self._update_playlists()
except (mpd.ConnectionError, ValueError) as error: except (mpd.ConnectionError, ValueError) as error:
LOGGER.debug("Error updating status: %s", 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 @property
def state(self) -> MediaPlayerState: def state(self) -> MediaPlayerState:
"""Return the media state.""" """Return the media state."""
@@ -260,11 +254,6 @@ class MpdDevice(MediaPlayerEntity):
return MediaPlayerState.OFF return MediaPlayerState.OFF
@property
def is_volume_muted(self):
"""Boolean if volume is currently muted."""
return self._muted
@property @property
def media_content_id(self): def media_content_id(self):
"""Return the content ID of current playing media.""" """Return the content ID of current playing media."""
@@ -282,20 +271,6 @@ class MpdDevice(MediaPlayerEntity):
return None 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 @property
def media_title(self): def media_title(self):
"""Return the title of current playing media.""" """Return the title of current playing media."""
@@ -436,7 +411,7 @@ class MpdDevice(MediaPlayerEntity):
| MediaPlayerEntityFeature.VOLUME_STEP | MediaPlayerEntityFeature.VOLUME_STEP
| MediaPlayerEntityFeature.VOLUME_MUTE | MediaPlayerEntityFeature.VOLUME_MUTE
) )
if self._playlists is not None: if self._attr_source_list is not None:
supported |= MediaPlayerEntityFeature.SELECT_SOURCE supported |= MediaPlayerEntityFeature.SELECT_SOURCE
return supported return supported
@@ -444,7 +419,7 @@ class MpdDevice(MediaPlayerEntity):
@property @property
def source(self): def source(self):
"""Name of the current input source.""" """Name of the current input source."""
return self._currentplaylist return self._current_playlist
@property @property
def source_list(self): def source_list(self):
@@ -459,12 +434,12 @@ class MpdDevice(MediaPlayerEntity):
async def _update_playlists(self, **kwargs: Any) -> None: async def _update_playlists(self, **kwargs: Any) -> None:
"""Update available MPD playlists.""" """Update available MPD playlists."""
try: try:
self._playlists = [] self._attr_source_list = []
with suppress(mpd.ConnectionError): with suppress(mpd.ConnectionError):
for playlist_data in await self._client.listplaylists(): 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: except mpd.CommandError as error:
self._playlists = None self._attr_source_list = None
LOGGER.warning("Playlists could not be updated: %s:", error) LOGGER.warning("Playlists could not be updated: %s:", error)
async def async_set_volume_level(self, volume: float) -> None: async def async_set_volume_level(self, volume: float) -> None:
@@ -527,7 +502,7 @@ class MpdDevice(MediaPlayerEntity):
await self.async_set_volume_level(0) await self.async_set_volume_level(0)
elif self._muted_volume is not None: elif self._muted_volume is not None:
await self.async_set_volume_level(self._muted_volume) await self.async_set_volume_level(self._muted_volume)
self._muted = mute self._attr_is_volume_muted = mute
async def async_play_media( async def async_play_media(
self, media_type: MediaType | str, media_id: str, **kwargs: Any self, media_type: MediaType | str, media_id: str, **kwargs: Any
@@ -543,17 +518,17 @@ class MpdDevice(MediaPlayerEntity):
if media_type == MediaType.PLAYLIST: if media_type == MediaType.PLAYLIST:
LOGGER.debug("Playing playlist: %s", media_id) LOGGER.debug("Playing playlist: %s", media_id)
if media_id in self._playlists: if self._attr_source_list and media_id in self._attr_source_list:
self._currentplaylist = media_id self._current_playlist = media_id
else: else:
self._currentplaylist = None self._current_playlist = None
LOGGER.warning("Unknown playlist name %s", media_id) LOGGER.warning("Unknown playlist name %s", media_id)
await self._client.clear() await self._client.clear()
await self._client.load(media_id) await self._client.load(media_id)
await self._client.play() await self._client.play()
else: else:
await self._client.clear() await self._client.clear()
self._currentplaylist = None self._current_playlist = None
await self._client.add(media_id) await self._client.add(media_id)
await self._client.play() await self._client.play()