Define media_player entity attributes as class variables (#51192)

This commit is contained in:
Franck Nijhof 2021-05-28 12:32:31 +02:00 committed by GitHub
parent 39e62f9c90
commit 17b2678aee
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 115 additions and 106 deletions

View File

@ -31,7 +31,7 @@ from homeassistant.core import HomeAssistant
import homeassistant.helpers.config_validation as cv import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.entity import DeviceInfo
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType, StateType from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
from .const import ATTR_MANUFACTURER, DEFAULT_NAME, DOMAIN from .const import ATTR_MANUFACTURER, DEFAULT_NAME, DOMAIN
@ -100,7 +100,7 @@ class DuneHDPlayerEntity(MediaPlayerEntity):
return True return True
@property @property
def state(self) -> StateType: def state(self) -> str | None:
"""Return player state.""" """Return player state."""
state = STATE_OFF state = STATE_OFF
if "playback_position" in self._state: if "playback_position" in self._state:

View File

@ -1,7 +1,6 @@
"""Denon HEOS Media Player.""" """Denon HEOS Media Player."""
from __future__ import annotations from __future__ import annotations
from collections.abc import Sequence
from functools import reduce, wraps from functools import reduce, wraps
import logging import logging
from operator import ior from operator import ior
@ -362,7 +361,7 @@ class HeosMediaPlayer(MediaPlayerEntity):
return self._source_manager.get_current_source(self._player.now_playing_media) return self._source_manager.get_current_source(self._player.now_playing_media)
@property @property
def source_list(self) -> Sequence[str]: def source_list(self) -> list[str]:
"""List of available input sources.""" """List of available input sources."""
return self._source_manager.source_list return self._source_manager.source_list

View File

@ -5,7 +5,7 @@ import asyncio
import base64 import base64
import collections import collections
from contextlib import suppress from contextlib import suppress
from datetime import timedelta import datetime as dt
import functools as ft import functools as ft
import hashlib import hashlib
import logging import logging
@ -57,6 +57,7 @@ import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.config_validation import ( # noqa: F401 from homeassistant.helpers.config_validation import ( # noqa: F401
PLATFORM_SCHEMA, PLATFORM_SCHEMA,
PLATFORM_SCHEMA_BASE, PLATFORM_SCHEMA_BASE,
datetime,
) )
from homeassistant.helpers.entity import Entity from homeassistant.helpers.entity import Entity
from homeassistant.helpers.entity_component import EntityComponent from homeassistant.helpers.entity_component import EntityComponent
@ -137,7 +138,7 @@ CACHE_URL = "url"
CACHE_CONTENT = "content" CACHE_CONTENT = "content"
ENTITY_IMAGE_CACHE = {CACHE_IMAGES: collections.OrderedDict(), CACHE_MAXSIZE: 16} ENTITY_IMAGE_CACHE = {CACHE_IMAGES: collections.OrderedDict(), CACHE_MAXSIZE: 16}
SCAN_INTERVAL = timedelta(seconds=10) SCAN_INTERVAL = dt.timedelta(seconds=10)
DEVICE_CLASS_TV = "tv" DEVICE_CLASS_TV = "tv"
DEVICE_CLASS_SPEAKER = "speaker" DEVICE_CLASS_SPEAKER = "speaker"
@ -371,11 +372,43 @@ class MediaPlayerEntity(Entity):
_access_token: str | None = None _access_token: str | None = None
_attr_app_id: str | None = None
_attr_app_name: str | None = None
_attr_group_members: list[str] | None = None
_attr_is_volume_muted: bool | None = None
_attr_media_album_artist: str | None = None
_attr_media_album_name: str | None = None
_attr_media_artist: str | None = None
_attr_media_channel: str | None = None
_attr_media_content_id: str | None = None
_attr_media_content_type: str | None = None
_attr_media_duration: int | None = None
_attr_media_episode: str | None = None
_attr_media_image_hash: str | None
_attr_media_image_remotely_accessible: bool = False
_attr_media_image_url: str | None = None
_attr_media_playlist: str | None = None
_attr_media_position_updated_at: dt.datetime | None = None
_attr_media_position: int | None = None
_attr_media_season: str | None = None
_attr_media_series_title: str | None = None
_attr_media_title: str | None = None
_attr_media_track: int | None = None
_attr_repeat: str | None = None
_attr_shuffle: bool | None = None
_attr_sound_mode_list: list[str] | None = None
_attr_sound_mode: str | None = None
_attr_source_list: list[str] | None = None
_attr_source: str | None = None
_attr_state: str | None = None
_attr_supported_features: int = 0
_attr_volume_level: float | None = None
# Implement these for your media player # Implement these for your media player
@property @property
def state(self): def state(self) -> str | None:
"""State of the player.""" """State of the player."""
return None return self._attr_state
@property @property
def access_token(self) -> str: def access_token(self) -> str:
@ -385,56 +418,59 @@ class MediaPlayerEntity(Entity):
return self._access_token return self._access_token
@property @property
def volume_level(self): def volume_level(self) -> float | None:
"""Volume level of the media player (0..1).""" """Volume level of the media player (0..1)."""
return None return self._attr_volume_level
@property @property
def is_volume_muted(self): def is_volume_muted(self) -> bool | None:
"""Boolean if volume is currently muted.""" """Boolean if volume is currently muted."""
return None return self._attr_is_volume_muted
@property @property
def media_content_id(self): def media_content_id(self) -> str | None:
"""Content ID of current playing media.""" """Content ID of current playing media."""
return None return self._attr_media_content_id
@property @property
def media_content_type(self): def media_content_type(self) -> str | None:
"""Content type of current playing media.""" """Content type of current playing media."""
return None return self._attr_media_content_type
@property @property
def media_duration(self): def media_duration(self) -> int | None:
"""Duration of current playing media in seconds.""" """Duration of current playing media in seconds."""
return None return self._attr_media_duration
@property @property
def media_position(self): def media_position(self) -> int | None:
"""Position of current playing media in seconds.""" """Position of current playing media in seconds."""
return None return self._attr_media_position
@property @property
def media_position_updated_at(self): def media_position_updated_at(self) -> dt.datetime | None:
"""When was the position of the current playing media valid. """When was the position of the current playing media valid.
Returns value from homeassistant.util.dt.utcnow(). Returns value from homeassistant.util.dt.utcnow().
""" """
return None return self._attr_media_position_updated_at
@property @property
def media_image_url(self): def media_image_url(self) -> str | None:
"""Image url of current playing media.""" """Image url of current playing media."""
return None return self._attr_media_image_url
@property @property
def media_image_remotely_accessible(self) -> bool: def media_image_remotely_accessible(self) -> bool:
"""If the image url is remotely accessible.""" """If the image url is remotely accessible."""
return False return self._attr_media_image_remotely_accessible
@property @property
def media_image_hash(self): def media_image_hash(self) -> str | None:
"""Hash value for media image.""" """Hash value for media image."""
if hasattr(self, "_attr_media_image_hash"):
return self._attr_media_image_hash
url = self.media_image_url url = self.media_image_url
if url is not None: if url is not None:
return hashlib.sha256(url.encode("utf-8")).hexdigest()[:16] return hashlib.sha256(url.encode("utf-8")).hexdigest()[:16]
@ -463,104 +499,104 @@ class MediaPlayerEntity(Entity):
return None, None return None, None
@property @property
def media_title(self): def media_title(self) -> str | None:
"""Title of current playing media.""" """Title of current playing media."""
return None return self._attr_media_title
@property @property
def media_artist(self): def media_artist(self) -> str | None:
"""Artist of current playing media, music track only.""" """Artist of current playing media, music track only."""
return None return self._attr_media_artist
@property @property
def media_album_name(self): def media_album_name(self) -> str | None:
"""Album name of current playing media, music track only.""" """Album name of current playing media, music track only."""
return None return self._attr_media_album_name
@property @property
def media_album_artist(self): def media_album_artist(self) -> str | None:
"""Album artist of current playing media, music track only.""" """Album artist of current playing media, music track only."""
return None return self._attr_media_album_artist
@property @property
def media_track(self): def media_track(self) -> int | None:
"""Track number of current playing media, music track only.""" """Track number of current playing media, music track only."""
return None return self._attr_media_track
@property @property
def media_series_title(self): def media_series_title(self) -> str | None:
"""Title of series of current playing media, TV show only.""" """Title of series of current playing media, TV show only."""
return None return self._attr_media_series_title
@property @property
def media_season(self): def media_season(self) -> str | None:
"""Season of current playing media, TV show only.""" """Season of current playing media, TV show only."""
return None return self._attr_media_season
@property @property
def media_episode(self): def media_episode(self) -> str | None:
"""Episode of current playing media, TV show only.""" """Episode of current playing media, TV show only."""
return None return self._attr_media_episode
@property @property
def media_channel(self): def media_channel(self) -> str | None:
"""Channel currently playing.""" """Channel currently playing."""
return None return self._attr_media_channel
@property @property
def media_playlist(self): def media_playlist(self) -> str | None:
"""Title of Playlist currently playing.""" """Title of Playlist currently playing."""
return None return self._attr_media_playlist
@property @property
def app_id(self): def app_id(self) -> str | None:
"""ID of the current running app.""" """ID of the current running app."""
return None return self._attr_app_id
@property @property
def app_name(self): def app_name(self) -> str | None:
"""Name of the current running app.""" """Name of the current running app."""
return None return self._attr_app_name
@property @property
def source(self): def source(self) -> str | None:
"""Name of the current input source.""" """Name of the current input source."""
return None return self._attr_source
@property @property
def source_list(self): def source_list(self) -> list[str] | None:
"""List of available input sources.""" """List of available input sources."""
return None return self._attr_source_list
@property @property
def sound_mode(self): def sound_mode(self) -> str | None:
"""Name of the current sound mode.""" """Name of the current sound mode."""
return None return self._attr_sound_mode
@property @property
def sound_mode_list(self): def sound_mode_list(self) -> list[str] | None:
"""List of available sound modes.""" """List of available sound modes."""
return None return self._attr_sound_mode_list
@property @property
def shuffle(self): def shuffle(self) -> bool | None:
"""Boolean if shuffle is enabled.""" """Boolean if shuffle is enabled."""
return None return self._attr_shuffle
@property @property
def repeat(self): def repeat(self) -> str | None:
"""Return current repeat mode.""" """Return current repeat mode."""
return None return self._attr_repeat
@property @property
def group_members(self): def group_members(self) -> list[str] | None:
"""List of members which are currently grouped together.""" """List of members which are currently grouped together."""
return None return self._attr_group_members
@property @property
def supported_features(self): def supported_features(self) -> int:
"""Flag media player features that are supported.""" """Flag media player features that are supported."""
return 0 return self._attr_supported_features
def turn_on(self): def turn_on(self):
"""Turn the media player on.""" """Turn the media player on."""

View File

@ -67,8 +67,6 @@ from .const import (
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
ICON = "mdi:spotify"
SCAN_INTERVAL = timedelta(seconds=30) SCAN_INTERVAL = timedelta(seconds=30)
SUPPORT_SPOTIFY = ( SUPPORT_SPOTIFY = (
@ -211,12 +209,12 @@ def spotify_exception_handler(func):
def wrapper(self, *args, **kwargs): def wrapper(self, *args, **kwargs):
try: try:
result = func(self, *args, **kwargs) result = func(self, *args, **kwargs)
self.player_available = True self._attr_available = True
return result return result
except requests.RequestException: except requests.RequestException:
self.player_available = False self._attr_available = False
except SpotifyException as exc: except SpotifyException as exc:
self.player_available = False self._attr_available = False
if exc.reason == "NO_ACTIVE_DEVICE": if exc.reason == "NO_ACTIVE_DEVICE":
raise HomeAssistantError("No active playback device found") from None raise HomeAssistantError("No active playback device found") from None
@ -226,6 +224,10 @@ def spotify_exception_handler(func):
class SpotifyMediaPlayer(MediaPlayerEntity): class SpotifyMediaPlayer(MediaPlayerEntity):
"""Representation of a Spotify controller.""" """Representation of a Spotify controller."""
_attr_icon = "mdi:spotify"
_attr_media_content_type = MEDIA_TYPE_MUSIC
_attr_media_image_remotely_accessible = False
def __init__( def __init__(
self, self,
session: OAuth2Session, session: OAuth2Session,
@ -247,40 +249,22 @@ class SpotifyMediaPlayer(MediaPlayerEntity):
self._currently_playing: dict | None = {} self._currently_playing: dict | None = {}
self._devices: list[dict] | None = [] self._devices: list[dict] | None = []
self._playlist: dict | None = None self._playlist: dict | None = None
self._spotify: Spotify = None
self.player_available = False self._attr_name = self._name
self._attr_unique_id = user_id
@property
def name(self) -> str:
"""Return the name."""
return self._name
@property
def icon(self) -> str:
"""Return the icon."""
return ICON
@property
def available(self) -> bool:
"""Return True if entity is available."""
return self.player_available
@property
def unique_id(self) -> str:
"""Return the unique ID."""
return self._id
@property @property
def device_info(self) -> DeviceInfo: def device_info(self) -> DeviceInfo:
"""Return device information about this entity.""" """Return device information about this entity."""
model = "Spotify Free"
if self._me is not None: if self._me is not None:
model = self._me["product"] product = self._me["product"]
model = f"Spotify {product}"
return { return {
"identifiers": {(DOMAIN, self._id)}, "identifiers": {(DOMAIN, self._id)},
"manufacturer": "Spotify AB", "manufacturer": "Spotify AB",
"model": f"Spotify {model}".rstrip(), "model": model,
"name": self._name, "name": self._name,
} }
@ -304,11 +288,6 @@ class SpotifyMediaPlayer(MediaPlayerEntity):
item = self._currently_playing.get("item") or {} item = self._currently_playing.get("item") or {}
return item.get("uri") return item.get("uri")
@property
def media_content_type(self) -> str | None:
"""Return the media type."""
return MEDIA_TYPE_MUSIC
@property @property
def media_duration(self) -> int | None: def media_duration(self) -> int | None:
"""Duration of current playing media in seconds.""" """Duration of current playing media in seconds."""
@ -340,11 +319,6 @@ class SpotifyMediaPlayer(MediaPlayerEntity):
return None return None
return fetch_image_url(self._currently_playing["item"]["album"]) return fetch_image_url(self._currently_playing["item"]["album"])
@property
def media_image_remotely_accessible(self) -> bool:
"""If the image url is remotely accessible."""
return False
@property @property
def media_title(self) -> str | None: def media_title(self) -> str | None:
"""Return the media title.""" """Return the media title."""
@ -357,7 +331,7 @@ class SpotifyMediaPlayer(MediaPlayerEntity):
if self._currently_playing.get("item") is None: if self._currently_playing.get("item") is None:
return None return None
return ", ".join( return ", ".join(
[artist["name"] for artist in self._currently_playing["item"]["artists"]] artist["name"] for artist in self._currently_playing["item"]["artists"]
) )
@property @property