Add vlc telnet error handler decorator (#58468)

* Add vlc telnet error handler decorator

* Delint

* Update stale docstring
This commit is contained in:
Martin Hjelmare 2021-10-26 18:43:33 +02:00 committed by GitHub
parent 6b1b8c9880
commit 777589cdcc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -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"