mirror of
https://github.com/home-assistant/core.git
synced 2025-07-25 14:17:45 +00:00
Define media_player entity attributes as class variables (#51192)
This commit is contained in:
parent
39e62f9c90
commit
17b2678aee
@ -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:
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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."""
|
||||||
|
@ -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
|
||||||
|
Loading…
x
Reference in New Issue
Block a user