core/homeassistant/components/volumio/media_player.py

283 lines
9.0 KiB
Python
Raw Normal View History

2023-02-03 22:08:48 +00:00
"""Volumio Platform.
Volumio rest API: https://volumio.github.io/docs/API/REST_API.html
"""
2022-09-06 11:59:05 +00:00
from __future__ import annotations
2018-02-02 22:12:54 +00:00
from datetime import timedelta
import json
2022-09-06 11:59:05 +00:00
from typing import Any
from homeassistant.components.media_player import (
BrowseMedia,
MediaPlayerEntity,
MediaPlayerEntityFeature,
MediaPlayerState,
MediaType,
RepeatMode,
2019-07-31 19:25:30 +00:00
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_ID, CONF_NAME
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity import DeviceInfo
from homeassistant.helpers.entity_platform import AddEntitiesCallback
2018-02-02 22:12:54 +00:00
from homeassistant.util import Throttle
from .browse_media import browse_node, browse_top_level
2020-07-27 07:19:19 +00:00
from .const import DATA_INFO, DATA_VOLUMIO, DOMAIN
2018-02-02 22:12:54 +00:00
PLAYLIST_UPDATE_INTERVAL = timedelta(seconds=15)
async def async_setup_entry(
hass: HomeAssistant,
config_entry: ConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> None:
2020-07-27 07:19:19 +00:00
"""Set up the Volumio media player platform."""
2020-07-27 07:19:19 +00:00
data = hass.data[DOMAIN][config_entry.entry_id]
volumio = data[DATA_VOLUMIO]
info = data[DATA_INFO]
uid = config_entry.data[CONF_ID]
name = config_entry.data[CONF_NAME]
2020-07-30 14:51:46 +00:00
entity = Volumio(volumio, uid, name, info)
async_add_entities([entity])
class Volumio(MediaPlayerEntity):
"""Volumio Player Object."""
_attr_media_content_type = MediaType.MUSIC
_attr_supported_features = (
MediaPlayerEntityFeature.PAUSE
| MediaPlayerEntityFeature.VOLUME_SET
| MediaPlayerEntityFeature.VOLUME_MUTE
| MediaPlayerEntityFeature.PREVIOUS_TRACK
| MediaPlayerEntityFeature.NEXT_TRACK
| MediaPlayerEntityFeature.SEEK
| MediaPlayerEntityFeature.STOP
| MediaPlayerEntityFeature.PLAY
| MediaPlayerEntityFeature.PLAY_MEDIA
| MediaPlayerEntityFeature.VOLUME_STEP
| MediaPlayerEntityFeature.SELECT_SOURCE
| MediaPlayerEntityFeature.REPEAT_SET
| MediaPlayerEntityFeature.SHUFFLE_SET
| MediaPlayerEntityFeature.CLEAR_PLAYLIST
| MediaPlayerEntityFeature.BROWSE_MEDIA
)
2020-07-30 14:51:46 +00:00
def __init__(self, volumio, uid, name, info):
"""Initialize the media player."""
2020-07-27 07:19:19 +00:00
self._volumio = volumio
self._uid = uid
self._name = name
2020-07-27 07:19:19 +00:00
self._info = info
self._state = {}
2018-02-02 22:12:54 +00:00
self._playlists = []
self._currentplaylist = None
self.thumbnail_cache = {}
2022-09-06 11:59:05 +00:00
async def async_update(self) -> None:
"""Update state."""
2020-07-27 07:19:19 +00:00
self._state = await self._volumio.get_state()
await self._async_update_playlists()
2020-07-27 07:19:19 +00:00
@property
def unique_id(self):
"""Return the unique id for the entity."""
return self._uid
@property
def name(self):
"""Return the name of the entity."""
return self._name
@property
def device_info(self) -> DeviceInfo:
2020-07-27 07:19:19 +00:00
"""Return device info for this device."""
return DeviceInfo(
identifiers={(DOMAIN, self.unique_id)},
manufacturer="Volumio",
model=self._info["hardware"],
name=self.name,
sw_version=self._info["systemversion"],
)
@property
def state(self) -> MediaPlayerState:
"""Return the state of the device."""
2019-07-31 19:25:30 +00:00
status = self._state.get("status", None)
if status == "pause":
return MediaPlayerState.PAUSED
2019-07-31 19:25:30 +00:00
if status == "play":
return MediaPlayerState.PLAYING
return MediaPlayerState.IDLE
@property
def media_title(self):
"""Title of current playing media."""
2019-07-31 19:25:30 +00:00
return self._state.get("title", None)
@property
def media_artist(self):
"""Artist of current playing media (Music track only)."""
2019-07-31 19:25:30 +00:00
return self._state.get("artist", None)
@property
def media_album_name(self):
"""Artist of current playing media (Music track only)."""
2019-07-31 19:25:30 +00:00
return self._state.get("album", None)
@property
def media_image_url(self):
"""Image url of current playing media."""
2019-07-31 19:25:30 +00:00
url = self._state.get("albumart", None)
2020-07-27 07:19:19 +00:00
return self._volumio.canonic_url(url)
@property
def media_seek_position(self):
"""Time in seconds of current seek position."""
2019-07-31 19:25:30 +00:00
return self._state.get("seek", None)
@property
def media_duration(self):
"""Time in seconds of current song duration."""
2019-07-31 19:25:30 +00:00
return self._state.get("duration", None)
@property
def volume_level(self):
"""Volume level of the media player (0..1)."""
2019-07-31 19:25:30 +00:00
volume = self._state.get("volume", None)
2018-02-02 22:12:54 +00:00
if volume is not None and volume != "":
2019-01-19 06:12:56 +00:00
volume = int(volume) / 100
return volume
@property
def is_volume_muted(self):
"""Boolean if volume is currently muted."""
2019-07-31 19:25:30 +00:00
return self._state.get("mute", None)
@property
def shuffle(self):
"""Boolean if shuffle is enabled."""
return self._state.get("random", False)
2022-01-19 21:07:11 +00:00
@property
def repeat(self) -> RepeatMode:
2022-01-19 21:07:11 +00:00
"""Return current repeat mode."""
if self._state.get("repeat", None):
return RepeatMode.ALL
return RepeatMode.OFF
2022-01-19 21:07:11 +00:00
2018-02-02 22:12:54 +00:00
@property
def source_list(self):
"""Return the list of available input sources."""
return self._playlists
@property
def source(self):
"""Name of the current input source."""
return self._currentplaylist
2022-09-06 11:59:05 +00:00
async def async_media_next_track(self) -> None:
"""Send media_next command to media player."""
2020-07-27 07:19:19 +00:00
await self._volumio.next()
2022-09-06 11:59:05 +00:00
async def async_media_previous_track(self) -> None:
"""Send media_previous command to media player."""
2020-07-27 07:19:19 +00:00
await self._volumio.previous()
2022-09-06 11:59:05 +00:00
async def async_media_play(self) -> None:
"""Send media_play command to media player."""
2020-07-27 07:19:19 +00:00
await self._volumio.play()
2022-09-06 11:59:05 +00:00
async def async_media_pause(self) -> None:
"""Send media_pause command to media player."""
if self._state.get("trackType") == "webradio":
2020-07-27 07:19:19 +00:00
await self._volumio.stop()
else:
2020-07-27 07:19:19 +00:00
await self._volumio.pause()
2022-09-06 11:59:05 +00:00
async def async_media_stop(self) -> None:
"""Send media_stop command to media player."""
2020-07-27 07:19:19 +00:00
await self._volumio.stop()
2022-09-06 11:59:05 +00:00
async def async_set_volume_level(self, volume: float) -> None:
"""Send volume_up command to media player."""
2020-07-27 07:19:19 +00:00
await self._volumio.set_volume_level(int(volume * 100))
2022-09-06 11:59:05 +00:00
async def async_volume_up(self) -> None:
2018-02-02 22:12:54 +00:00
"""Service to send the Volumio the command for volume up."""
2020-07-27 07:19:19 +00:00
await self._volumio.volume_up()
2018-02-02 22:12:54 +00:00
2022-09-06 11:59:05 +00:00
async def async_volume_down(self) -> None:
2018-02-02 22:12:54 +00:00
"""Service to send the Volumio the command for volume down."""
2020-07-27 07:19:19 +00:00
await self._volumio.volume_down()
2018-02-02 22:12:54 +00:00
2022-09-06 11:59:05 +00:00
async def async_mute_volume(self, mute: bool) -> None:
"""Send mute command to media player."""
if mute:
2020-07-27 07:19:19 +00:00
await self._volumio.mute()
else:
await self._volumio.unmute()
2018-02-02 22:12:54 +00:00
2022-09-06 11:59:05 +00:00
async def async_set_shuffle(self, shuffle: bool) -> None:
"""Enable/disable shuffle mode."""
2020-07-27 07:19:19 +00:00
await self._volumio.set_shuffle(shuffle)
async def async_set_repeat(self, repeat: RepeatMode) -> None:
2022-01-19 21:07:11 +00:00
"""Set repeat mode."""
if repeat == RepeatMode.OFF:
2022-01-19 21:07:11 +00:00
await self._volumio.repeatAll("false")
else:
await self._volumio.repeatAll("true")
2022-09-06 11:59:05 +00:00
async def async_select_source(self, source: str) -> None:
2020-07-27 07:19:19 +00:00
"""Choose an available playlist and play it."""
await self._volumio.play_playlist(source)
2018-02-02 22:12:54 +00:00
self._currentplaylist = source
2022-09-06 11:59:05 +00:00
async def async_clear_playlist(self) -> None:
2018-02-02 22:12:54 +00:00
"""Clear players playlist."""
2020-07-27 07:19:19 +00:00
await self._volumio.clear_playlist()
2018-02-02 22:12:54 +00:00
self._currentplaylist = None
@Throttle(PLAYLIST_UPDATE_INTERVAL)
async def _async_update_playlists(self, **kwargs):
2018-02-02 22:12:54 +00:00
"""Update available Volumio playlists."""
2020-07-27 07:19:19 +00:00
self._playlists = await self._volumio.get_playlists()
2022-09-06 11:59:05 +00:00
async def async_play_media(
self, media_type: MediaType | str, media_id: str, **kwargs: Any
2022-09-06 11:59:05 +00:00
) -> None:
"""Send the play_media command to the media player."""
await self._volumio.replace_and_play(json.loads(media_id))
2022-09-06 11:59:05 +00:00
async def async_browse_media(
self,
media_content_type: MediaType | str | None = None,
media_content_id: str | None = None,
2022-09-06 11:59:05 +00:00
) -> BrowseMedia:
"""Implement the websocket media browsing helper."""
self.thumbnail_cache = {}
2021-07-29 23:20:03 +00:00
if media_content_type in (None, "library"):
return await browse_top_level(self._volumio)
return await browse_node(
self, self._volumio, media_content_type, media_content_id
)
async def async_get_browse_image(
2022-09-06 11:59:05 +00:00
self,
media_content_type: MediaType | str,
2022-09-06 11:59:05 +00:00
media_content_id: str,
media_image_id: str | None = None,
) -> tuple[bytes | None, str | None]:
"""Get album art from Volumio."""
cached_url = self.thumbnail_cache.get(media_content_id)
image_url = self._volumio.canonic_url(cached_url)
return await self._async_fetch_image(image_url)