"""Support for ESPHome media players.""" from __future__ import annotations from typing import Any from aioesphomeapi import ( MediaPlayerCommand, MediaPlayerEntityState, MediaPlayerInfo, MediaPlayerState, ) from homeassistant.components import media_source from homeassistant.components.media_player import ( MediaPlayerDeviceClass, MediaPlayerEntity, ) from homeassistant.components.media_player.browse_media import ( BrowseMedia, async_process_play_media_url, ) from homeassistant.components.media_player.const import MediaPlayerEntityFeature from homeassistant.config_entries import ConfigEntry from homeassistant.const import STATE_IDLE, STATE_PAUSED, STATE_PLAYING from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback from . import ( EsphomeEntity, EsphomeEnumMapper, esphome_state_property, platform_async_setup_entry, ) async def async_setup_entry( hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback, ) -> None: """Set up esphome media players based on a config entry.""" await platform_async_setup_entry( hass, entry, async_add_entities, component_key="media_player", info_type=MediaPlayerInfo, entity_type=EsphomeMediaPlayer, state_type=MediaPlayerEntityState, ) _STATES: EsphomeEnumMapper[MediaPlayerState, str] = EsphomeEnumMapper( { MediaPlayerState.IDLE: STATE_IDLE, MediaPlayerState.PLAYING: STATE_PLAYING, MediaPlayerState.PAUSED: STATE_PAUSED, } ) class EsphomeMediaPlayer( EsphomeEntity[MediaPlayerInfo, MediaPlayerEntityState], MediaPlayerEntity ): """A media player implementation for esphome.""" _attr_device_class = MediaPlayerDeviceClass.SPEAKER @property # type: ignore[misc] @esphome_state_property def state(self) -> str | None: """Return current state.""" return _STATES.from_esphome(self._state.state) @property # type: ignore[misc] @esphome_state_property def is_volume_muted(self) -> bool: """Return true if volume is muted.""" return self._state.muted @property # type: ignore[misc] @esphome_state_property def volume_level(self) -> float | None: """Volume level of the media player (0..1).""" return self._state.volume @property def supported_features(self) -> int: """Flag supported features.""" flags = ( MediaPlayerEntityFeature.PLAY_MEDIA | MediaPlayerEntityFeature.BROWSE_MEDIA | MediaPlayerEntityFeature.STOP | MediaPlayerEntityFeature.VOLUME_SET | MediaPlayerEntityFeature.VOLUME_MUTE ) if self._static_info.supports_pause: flags |= MediaPlayerEntityFeature.PAUSE | MediaPlayerEntityFeature.PLAY return flags async def async_play_media( self, media_type: str, media_id: str, **kwargs: Any ) -> None: """Send the play command with media url to the media player.""" if media_source.is_media_source_id(media_id): sourced_media = await media_source.async_resolve_media( self.hass, media_id, self.entity_id ) media_id = sourced_media.url media_id = async_process_play_media_url(self.hass, media_id) await self._client.media_player_command( self._static_info.key, media_url=media_id, ) async def async_browse_media( self, media_content_type: str | None = None, media_content_id: str | None = None ) -> BrowseMedia: """Implement the websocket media browsing helper.""" return await media_source.async_browse_media( self.hass, media_content_id, content_filter=lambda item: item.media_content_type.startswith("audio/"), ) async def async_set_volume_level(self, volume: float) -> None: """Set volume level, range 0..1.""" await self._client.media_player_command( self._static_info.key, volume=volume, ) async def async_media_pause(self) -> None: """Send pause command.""" await self._client.media_player_command( self._static_info.key, command=MediaPlayerCommand.PAUSE, ) async def async_media_play(self) -> None: """Send play command.""" await self._client.media_player_command( self._static_info.key, command=MediaPlayerCommand.PLAY, ) async def async_media_stop(self) -> None: """Send stop command.""" await self._client.media_player_command( self._static_info.key, command=MediaPlayerCommand.STOP, ) async def async_mute_volume(self, mute: bool) -> None: """Mute the volume.""" await self._client.media_player_command( self._static_info.key, command=MediaPlayerCommand.MUTE if mute else MediaPlayerCommand.UNMUTE, )