"""Provide functionality to interact with vlc devices on the network.""" from __future__ import annotations import logging from typing import Any import vlc import voluptuous as vol from homeassistant.components import media_source from homeassistant.components.media_player import ( PLATFORM_SCHEMA, BrowseMedia, MediaPlayerEntity, MediaPlayerEntityFeature, MediaPlayerState, MediaType, async_process_play_media_url, ) from homeassistant.const import CONF_NAME from homeassistant.core import HomeAssistant import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType import homeassistant.util.dt as dt_util _LOGGER = logging.getLogger(__name__) CONF_ARGUMENTS = "arguments" DEFAULT_NAME = "Vlc" PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend( { vol.Optional(CONF_ARGUMENTS, default=""): cv.string, vol.Optional(CONF_NAME): cv.string, } ) def setup_platform( hass: HomeAssistant, config: ConfigType, add_entities: AddEntitiesCallback, discovery_info: DiscoveryInfoType | None = None, ) -> None: """Set up the vlc platform.""" add_entities( [VlcDevice(config.get(CONF_NAME, DEFAULT_NAME), config.get(CONF_ARGUMENTS))] ) class VlcDevice(MediaPlayerEntity): """Representation of a vlc player.""" _attr_media_content_type = MediaType.MUSIC _attr_supported_features = ( MediaPlayerEntityFeature.PAUSE | MediaPlayerEntityFeature.VOLUME_SET | MediaPlayerEntityFeature.VOLUME_MUTE | MediaPlayerEntityFeature.PLAY_MEDIA | MediaPlayerEntityFeature.PLAY | MediaPlayerEntityFeature.STOP | MediaPlayerEntityFeature.BROWSE_MEDIA ) def __init__(self, name, arguments): """Initialize the vlc device.""" self._instance = vlc.Instance(arguments) self._vlc = self._instance.media_player_new() self._attr_name = name def update(self): """Get the latest details from the device.""" status = self._vlc.get_state() if status == vlc.State.Playing: self._attr_state = MediaPlayerState.PLAYING elif status == vlc.State.Paused: self._attr_state = MediaPlayerState.PAUSED else: self._attr_state = MediaPlayerState.IDLE self._attr_media_duration = self._vlc.get_length() / 1000 position = self._vlc.get_position() * self._attr_media_duration if position != self._attr_media_position: self._attr_media_position_updated_at = dt_util.utcnow() self._attr_media_position = position self._attr_volume_level = self._vlc.audio_get_volume() / 100 self._attr_is_volume_muted = self._vlc.audio_get_mute() == 1 return True def media_seek(self, position: float) -> None: """Seek the media to a specific location.""" track_length = self._vlc.get_length() / 1000 self._vlc.set_position(position / track_length) def mute_volume(self, mute: bool) -> None: """Mute the volume.""" self._vlc.audio_set_mute(mute) self._attr_is_volume_muted = mute def set_volume_level(self, volume: float) -> None: """Set volume level, range 0..1.""" self._vlc.audio_set_volume(int(volume * 100)) self._attr_volume_level = volume def media_play(self) -> None: """Send play command.""" self._vlc.play() self._attr_state = MediaPlayerState.PLAYING def media_pause(self) -> None: """Send pause command.""" self._vlc.pause() self._attr_state = MediaPlayerState.PAUSED def media_stop(self) -> None: """Send stop command.""" self._vlc.stop() self._attr_state = MediaPlayerState.IDLE async def async_play_media( self, media_type: MediaType | str, media_id: str, **kwargs: Any ) -> None: """Play media from a URL or file.""" # Handle media_source 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 elif media_type != MediaType.MUSIC: _LOGGER.error( "Invalid media type %s. Only %s is supported", media_type, MediaType.MUSIC, ) return media_id = async_process_play_media_url(self.hass, media_id) def play(): self._vlc.set_media(self._instance.media_new(media_id)) self._vlc.play() await self.hass.async_add_executor_job(play) self._attr_state = MediaPlayerState.PLAYING async def async_browse_media( self, media_content_type: MediaType | 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/"), )