Home Assistant Unofficial Reference 2024.12.1
media_player.py
Go to the documentation of this file.
1 """Volumio Platform.
2 
3 Volumio rest API: https://volumio.github.io/docs/API/REST_API.html
4 """
5 
6 from __future__ import annotations
7 
8 from datetime import timedelta
9 import json
10 from typing import Any
11 
13  BrowseMedia,
14  MediaPlayerEntity,
15  MediaPlayerEntityFeature,
16  MediaPlayerState,
17  MediaType,
18  RepeatMode,
19 )
20 from homeassistant.config_entries import ConfigEntry
21 from homeassistant.const import CONF_ID, CONF_NAME
22 from homeassistant.core import HomeAssistant
23 from homeassistant.helpers.device_registry import DeviceInfo
24 from homeassistant.helpers.entity_platform import AddEntitiesCallback
25 from homeassistant.util import Throttle
26 
27 from .browse_media import browse_node, browse_top_level
28 from .const import DATA_INFO, DATA_VOLUMIO, DOMAIN
29 
30 PLAYLIST_UPDATE_INTERVAL = timedelta(seconds=15)
31 
32 
34  hass: HomeAssistant,
35  config_entry: ConfigEntry,
36  async_add_entities: AddEntitiesCallback,
37 ) -> None:
38  """Set up the Volumio media player platform."""
39 
40  data = hass.data[DOMAIN][config_entry.entry_id]
41  volumio = data[DATA_VOLUMIO]
42  info = data[DATA_INFO]
43  uid = config_entry.data[CONF_ID]
44  name = config_entry.data[CONF_NAME]
45 
46  entity = Volumio(volumio, uid, name, info)
47  async_add_entities([entity])
48 
49 
51  """Volumio Player Object."""
52 
53  _attr_has_entity_name = True
54  _attr_name = None
55  _attr_media_content_type = MediaType.MUSIC
56  _attr_supported_features = (
57  MediaPlayerEntityFeature.PAUSE
58  | MediaPlayerEntityFeature.VOLUME_SET
59  | MediaPlayerEntityFeature.VOLUME_MUTE
60  | MediaPlayerEntityFeature.PREVIOUS_TRACK
61  | MediaPlayerEntityFeature.NEXT_TRACK
62  | MediaPlayerEntityFeature.SEEK
63  | MediaPlayerEntityFeature.STOP
64  | MediaPlayerEntityFeature.PLAY
65  | MediaPlayerEntityFeature.PLAY_MEDIA
66  | MediaPlayerEntityFeature.VOLUME_STEP
67  | MediaPlayerEntityFeature.SELECT_SOURCE
68  | MediaPlayerEntityFeature.REPEAT_SET
69  | MediaPlayerEntityFeature.SHUFFLE_SET
70  | MediaPlayerEntityFeature.CLEAR_PLAYLIST
71  | MediaPlayerEntityFeature.BROWSE_MEDIA
72  )
73  _attr_source_list = []
74 
75  def __init__(self, volumio, uid, name, info):
76  """Initialize the media player."""
77  self._volumio_volumio = volumio
78  unique_id = uid
79  self._state_state = {}
80  self.thumbnail_cachethumbnail_cache = {}
81  self._attr_unique_id_attr_unique_id = unique_id
82  self._attr_device_info_attr_device_info = DeviceInfo(
83  identifiers={(DOMAIN, unique_id)},
84  manufacturer="Volumio",
85  model=info["hardware"],
86  name=name,
87  sw_version=info["systemversion"],
88  )
89 
90  async def async_update(self) -> None:
91  """Update state."""
92  self._state_state = await self._volumio_volumio.get_state()
93  await self._async_update_playlists_async_update_playlists()
94 
95  @property
96  def state(self) -> MediaPlayerState:
97  """Return the state of the device."""
98  status = self._state_state.get("status", None)
99  if status == "pause":
100  return MediaPlayerState.PAUSED
101  if status == "play":
102  return MediaPlayerState.PLAYING
103 
104  return MediaPlayerState.IDLE
105 
106  @property
107  def media_title(self):
108  """Title of current playing media."""
109  return self._state_state.get("title", None)
110 
111  @property
112  def media_artist(self):
113  """Artist of current playing media (Music track only)."""
114  return self._state_state.get("artist", None)
115 
116  @property
117  def media_album_name(self):
118  """Artist of current playing media (Music track only)."""
119  return self._state_state.get("album", None)
120 
121  @property
122  def media_image_url(self):
123  """Image url of current playing media."""
124  url = self._state_state.get("albumart", None)
125  return self._volumio_volumio.canonic_url(url)
126 
127  @property
129  """Time in seconds of current seek position."""
130  return self._state_state.get("seek", None)
131 
132  @property
133  def media_duration(self):
134  """Time in seconds of current song duration."""
135  return self._state_state.get("duration", None)
136 
137  @property
138  def volume_level(self):
139  """Volume level of the media player (0..1)."""
140  volume = self._state_state.get("volume", None)
141  if volume is not None and volume != "":
142  volume = int(volume) / 100
143  return volume
144 
145  @property
146  def is_volume_muted(self):
147  """Boolean if volume is currently muted."""
148  return self._state_state.get("mute", None)
149 
150  @property
151  def shuffle(self):
152  """Boolean if shuffle is enabled."""
153  return self._state_state.get("random", False)
154 
155  @property
156  def repeat(self) -> RepeatMode:
157  """Return current repeat mode."""
158  if self._state_state.get("repeat", None):
159  return RepeatMode.ALL
160  return RepeatMode.OFF
161 
162  async def async_media_next_track(self) -> None:
163  """Send media_next command to media player."""
164  await self._volumio_volumio.next()
165 
166  async def async_media_previous_track(self) -> None:
167  """Send media_previous command to media player."""
168  await self._volumio_volumio.previous()
169 
170  async def async_media_play(self) -> None:
171  """Send media_play command to media player."""
172  await self._volumio_volumio.play()
173 
174  async def async_media_pause(self) -> None:
175  """Send media_pause command to media player."""
176  if self._state_state.get("trackType") == "webradio":
177  await self._volumio_volumio.stop()
178  else:
179  await self._volumio_volumio.pause()
180 
181  async def async_media_stop(self) -> None:
182  """Send media_stop command to media player."""
183  await self._volumio_volumio.stop()
184 
185  async def async_set_volume_level(self, volume: float) -> None:
186  """Send volume_up command to media player."""
187  await self._volumio_volumio.set_volume_level(int(volume * 100))
188 
189  async def async_volume_up(self) -> None:
190  """Service to send the Volumio the command for volume up."""
191  await self._volumio_volumio.volume_up()
192 
193  async def async_volume_down(self) -> None:
194  """Service to send the Volumio the command for volume down."""
195  await self._volumio_volumio.volume_down()
196 
197  async def async_mute_volume(self, mute: bool) -> None:
198  """Send mute command to media player."""
199  if mute:
200  await self._volumio_volumio.mute()
201  else:
202  await self._volumio_volumio.unmute()
203 
204  async def async_set_shuffle(self, shuffle: bool) -> None:
205  """Enable/disable shuffle mode."""
206  await self._volumio_volumio.set_shuffle(shuffle)
207 
208  async def async_set_repeat(self, repeat: RepeatMode) -> None:
209  """Set repeat mode."""
210  if repeat == RepeatMode.OFF:
211  await self._volumio_volumio.repeatAll("false")
212  else:
213  await self._volumio_volumio.repeatAll("true")
214 
215  async def async_select_source(self, source: str) -> None:
216  """Choose an available playlist and play it."""
217  await self._volumio_volumio.play_playlist(source)
218  self._attr_source_attr_source = source
219 
220  async def async_clear_playlist(self) -> None:
221  """Clear players playlist."""
222  await self._volumio_volumio.clear_playlist()
223  self._attr_source_attr_source = None
224 
225  @Throttle(PLAYLIST_UPDATE_INTERVAL)
226  async def _async_update_playlists(self, **kwargs):
227  """Update available Volumio playlists."""
228  self._attr_source_list_attr_source_list_attr_source_list = await self._volumio_volumio.get_playlists()
229 
230  async def async_play_media(
231  self, media_type: MediaType | str, media_id: str, **kwargs: Any
232  ) -> None:
233  """Send the play_media command to the media player."""
234  await self._volumio_volumio.replace_and_play(json.loads(media_id))
235 
237  self,
238  media_content_type: MediaType | str | None = None,
239  media_content_id: str | None = None,
240  ) -> BrowseMedia:
241  """Implement the websocket media browsing helper."""
242  self.thumbnail_cachethumbnail_cache = {}
243  if media_content_type in (None, "library"):
244  return await browse_top_level(self._volumio_volumio)
245 
246  return await browse_node(
247  self, self._volumio_volumio, media_content_type, media_content_id
248  )
249 
251  self,
252  media_content_type: MediaType | str,
253  media_content_id: str,
254  media_image_id: str | None = None,
255  ) -> tuple[bytes | None, str | None]:
256  """Get album art from Volumio."""
257  cached_url = self.thumbnail_cachethumbnail_cache.get(media_content_id)
258  image_url = self._volumio_volumio.canonic_url(cached_url)
259  return await self._async_fetch_image_async_fetch_image(image_url)
tuple[bytes|None, str|None] _async_fetch_image(self, str url)
Definition: __init__.py:1190
None async_play_media(self, MediaType|str media_type, str media_id, **Any kwargs)
BrowseMedia async_browse_media(self, MediaType|str|None media_content_type=None, str|None media_content_id=None)
def __init__(self, volumio, uid, name, info)
Definition: media_player.py:75
tuple[bytes|None, str|None] async_get_browse_image(self, MediaType|str media_content_type, str media_content_id, str|None media_image_id=None)
web.Response get(self, web.Request request, str config_key)
Definition: view.py:88
str|float get_state(dict[str, float] data, str key)
Definition: sensor.py:26
def browse_node(AFSAPI afsapi, media_content_type, media_content_id)
def browse_top_level(current_mode, AFSAPI afsapi)
Definition: browse_media.py:77
None async_setup_entry(HomeAssistant hass, ConfigEntry config_entry, AddEntitiesCallback async_add_entities)
Definition: media_player.py:37