From 777589cdccf359e99abe40aeb710adc64e3bee47 Mon Sep 17 00:00:00 2001 From: Martin Hjelmare Date: Tue, 26 Oct 2021 18:43:33 +0200 Subject: [PATCH] Add vlc telnet error handler decorator (#58468) * Add vlc telnet error handler decorator * Delint * Update stale docstring --- .../components/vlc_telnet/media_player.py | 98 ++++++++++++------- 1 file changed, 62 insertions(+), 36 deletions(-) diff --git a/homeassistant/components/vlc_telnet/media_player.py b/homeassistant/components/vlc_telnet/media_player.py index ad88cfc2627..624234ce712 100644 --- a/homeassistant/components/vlc_telnet/media_player.py +++ b/homeassistant/components/vlc_telnet/media_player.py @@ -2,7 +2,8 @@ from __future__ import annotations from datetime import datetime -from typing import Any +from functools import wraps +from typing import Any, Callable, TypeVar, cast from aiovlc.client import Client from aiovlc.exceptions import AuthError, CommandError, ConnectError @@ -65,6 +66,8 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend( } ) +Func = TypeVar("Func", bound=Callable[..., Any]) + async def async_setup_platform( hass: HomeAssistant, @@ -96,6 +99,25 @@ async def async_setup_entry( async_add_entities([VlcDevice(entry, vlc, name, available)], True) +def catch_vlc_errors(func: Func) -> Func: + """Catch VLC errors.""" + + @wraps(func) + async def wrapper(self, *args: Any, **kwargs: Any) -> Any: + """Catch VLC errors and modify availability.""" + try: + await func(self, *args, **kwargs) + except CommandError as err: + LOGGER.error("Command error: %s", err) + except ConnectError as err: + # pylint: disable=protected-access + if self._available: + LOGGER.error("Connection error: %s", err) + self._available = False + + return cast(Func, wrapper) + + class VlcDevice(MediaPlayerEntity): """Representation of a vlc player.""" @@ -125,6 +147,7 @@ class VlcDevice(MediaPlayerEntity): "entry_type": "service", } + @catch_vlc_errors async def async_update(self) -> None: """Get the latest details from the device.""" if not self._available: @@ -147,47 +170,39 @@ class VlcDevice(MediaPlayerEntity): self._available = True LOGGER.info("Connected to vlc host: %s", self._vlc.host) - try: - status = await self._vlc.status() - LOGGER.debug("Status: %s", status) + status = await self._vlc.status() + LOGGER.debug("Status: %s", status) - self._volume = status.audio_volume / MAX_VOLUME - state = status.state - if state == "playing": - self._state = STATE_PLAYING - elif state == "paused": - self._state = STATE_PAUSED - else: - self._state = STATE_IDLE + self._volume = status.audio_volume / MAX_VOLUME + state = status.state + if state == "playing": + self._state = STATE_PLAYING + elif state == "paused": + self._state = STATE_PAUSED + else: + self._state = STATE_IDLE - if self._state != STATE_IDLE: - self._media_duration = (await self._vlc.get_length()).length - time_output = await self._vlc.get_time() - vlc_position = time_output.time + if self._state != STATE_IDLE: + self._media_duration = (await self._vlc.get_length()).length + time_output = await self._vlc.get_time() + vlc_position = time_output.time - # Check if current position is stale. - if vlc_position != self._media_position: - self._media_position_updated_at = dt_util.utcnow() - self._media_position = vlc_position + # Check if current position is stale. + if vlc_position != self._media_position: + self._media_position_updated_at = dt_util.utcnow() + self._media_position = vlc_position - info = await self._vlc.info() - data = info.data - LOGGER.debug("Info data: %s", data) + info = await self._vlc.info() + data = info.data + LOGGER.debug("Info data: %s", data) - self._media_artist = data.get(0, {}).get("artist") - self._media_title = data.get(0, {}).get("title") + self._media_artist = data.get(0, {}).get("artist") + self._media_title = data.get(0, {}).get("title") - if not self._media_title: - # Fall back to filename. - if data_info := data.get("data"): - self._media_title = data_info["filename"] - - except CommandError as err: - LOGGER.error("Command error: %s", err) - except ConnectError as err: - if self._available: - LOGGER.error("Connection error: %s", err) - self._available = False + if not self._media_title: + # Fall back to filename. + if data_info := data.get("data"): + self._media_title = data_info["filename"] @property def name(self): @@ -249,10 +264,12 @@ class VlcDevice(MediaPlayerEntity): """Artist of current playing media, music track only.""" return self._media_artist + @catch_vlc_errors async def async_media_seek(self, position: float) -> None: """Seek the media to a specific location.""" await self._vlc.seek(round(position)) + @catch_vlc_errors async def async_mute_volume(self, mute: bool) -> None: """Mute the volume.""" assert self._volume is not None @@ -264,6 +281,7 @@ class VlcDevice(MediaPlayerEntity): self._muted = mute + @catch_vlc_errors async def async_set_volume_level(self, volume: float) -> None: """Set volume level, range 0..1.""" await self._vlc.set_volume(round(volume * MAX_VOLUME)) @@ -273,11 +291,13 @@ class VlcDevice(MediaPlayerEntity): # This can happen if we were muted and then see a volume_up. self._muted = False + @catch_vlc_errors async def async_media_play(self) -> None: """Send play command.""" await self._vlc.play() self._state = STATE_PLAYING + @catch_vlc_errors async def async_media_pause(self) -> None: """Send pause command.""" status = await self._vlc.status() @@ -288,11 +308,13 @@ class VlcDevice(MediaPlayerEntity): self._state = STATE_PAUSED + @catch_vlc_errors async def async_media_stop(self) -> None: """Send stop command.""" await self._vlc.stop() self._state = STATE_IDLE + @catch_vlc_errors async def async_play_media( self, media_type: str, media_id: str, **kwargs: Any ) -> None: @@ -308,18 +330,22 @@ class VlcDevice(MediaPlayerEntity): await self._vlc.add(media_id) self._state = STATE_PLAYING + @catch_vlc_errors async def async_media_previous_track(self) -> None: """Send previous track command.""" await self._vlc.prev() + @catch_vlc_errors async def async_media_next_track(self) -> None: """Send next track command.""" await self._vlc.next() + @catch_vlc_errors async def async_clear_playlist(self) -> None: """Clear players playlist.""" await self._vlc.clear() + @catch_vlc_errors async def async_set_shuffle(self, shuffle: bool) -> None: """Enable/disable shuffle mode.""" shuffle_command = "on" if shuffle else "off"