From f06c0a8b542b3316ac6e1dab75d5510fca337a34 Mon Sep 17 00:00:00 2001 From: Chris Talkington Date: Thu, 4 Jun 2020 11:59:39 -0500 Subject: [PATCH] Add roku exception handling for service calls (#36328) --- homeassistant/components/roku/__init__.py | 18 +++++++++++++++++- homeassistant/components/roku/media_player.py | 14 +++++++++++++- homeassistant/components/roku/remote.py | 5 ++++- 3 files changed, 34 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/roku/__init__.py b/homeassistant/components/roku/__init__.py index ef233f64a1b54e..a3357ec4cf9f13 100644 --- a/homeassistant/components/roku/__init__.py +++ b/homeassistant/components/roku/__init__.py @@ -4,7 +4,7 @@ import logging from typing import Any, Dict -from rokuecp import Roku, RokuError +from rokuecp import Roku, RokuConnectionError, RokuError from rokuecp.models import Device import voluptuous as vol @@ -92,6 +92,22 @@ async def async_unload_entry(hass: HomeAssistantType, entry: ConfigEntry) -> boo return unload_ok +def roku_exception_handler(func): + """Decorate Roku calls to handle Roku exceptions.""" + + async def handler(self, *args, **kwargs): + try: + await func(self, *args, **kwargs) + except RokuConnectionError as error: + if self.available: + _LOGGER.error("Error communicating with API: %s", error) + except RokuError as error: + if self.available: + _LOGGER.error("Invalid response from API: %s", error) + + return handler + + class RokuDataUpdateCoordinator(DataUpdateCoordinator): """Class to manage fetching Roku data.""" diff --git a/homeassistant/components/roku/media_player.py b/homeassistant/components/roku/media_player.py index 69c9e24ad89998..168d4a4a6fee09 100644 --- a/homeassistant/components/roku/media_player.py +++ b/homeassistant/components/roku/media_player.py @@ -19,7 +19,7 @@ ) from homeassistant.const import STATE_HOME, STATE_IDLE, STATE_PLAYING, STATE_STANDBY -from . import RokuDataUpdateCoordinator, RokuEntity +from . import RokuDataUpdateCoordinator, RokuEntity, roku_exception_handler from .const import DOMAIN _LOGGER = logging.getLogger(__name__) @@ -161,49 +161,60 @@ def source_list(self) -> List: """List of available input sources.""" return ["Home"] + sorted(app.name for app in self.coordinator.data.apps) + @roku_exception_handler async def async_turn_on(self) -> None: """Turn on the Roku.""" await self.coordinator.roku.remote("poweron") + @roku_exception_handler async def async_turn_off(self) -> None: """Turn off the Roku.""" await self.coordinator.roku.remote("poweroff") + @roku_exception_handler async def async_media_pause(self) -> None: """Send pause command.""" if self.state != STATE_STANDBY: await self.coordinator.roku.remote("play") + @roku_exception_handler async def async_media_play(self) -> None: """Send play command.""" if self.state != STATE_STANDBY: await self.coordinator.roku.remote("play") + @roku_exception_handler async def async_media_play_pause(self) -> None: """Send play/pause command.""" if self.state != STATE_STANDBY: await self.coordinator.roku.remote("play") + @roku_exception_handler async def async_media_previous_track(self) -> None: """Send previous track command.""" await self.coordinator.roku.remote("reverse") + @roku_exception_handler async def async_media_next_track(self) -> None: """Send next track command.""" await self.coordinator.roku.remote("forward") + @roku_exception_handler async def async_mute_volume(self, mute) -> None: """Mute the volume.""" await self.coordinator.roku.remote("volume_mute") + @roku_exception_handler async def async_volume_up(self) -> None: """Volume up media player.""" await self.coordinator.roku.remote("volume_up") + @roku_exception_handler async def async_volume_down(self) -> None: """Volume down media player.""" await self.coordinator.roku.remote("volume_down") + @roku_exception_handler async def async_play_media(self, media_type: str, media_id: str, **kwargs) -> None: """Tune to channel.""" if media_type != MEDIA_TYPE_CHANNEL: @@ -216,6 +227,7 @@ async def async_play_media(self, media_type: str, media_id: str, **kwargs) -> No await self.coordinator.roku.tune(media_id) + @roku_exception_handler async def async_select_source(self, source: str) -> None: """Select input source.""" if source == "Home": diff --git a/homeassistant/components/roku/remote.py b/homeassistant/components/roku/remote.py index 99e398fea683cf..5b893b6a0f8e35 100644 --- a/homeassistant/components/roku/remote.py +++ b/homeassistant/components/roku/remote.py @@ -5,7 +5,7 @@ from homeassistant.config_entries import ConfigEntry from homeassistant.helpers.typing import HomeAssistantType -from . import RokuDataUpdateCoordinator, RokuEntity +from . import RokuDataUpdateCoordinator, RokuEntity, roku_exception_handler from .const import DOMAIN @@ -43,14 +43,17 @@ def is_on(self) -> bool: """Return true if device is on.""" return not self.coordinator.data.state.standby + @roku_exception_handler async def async_turn_on(self, **kwargs) -> None: """Turn the device on.""" await self.coordinator.roku.remote("poweron") + @roku_exception_handler async def async_turn_off(self, **kwargs) -> None: """Turn the device off.""" await self.coordinator.roku.remote("poweroff") + @roku_exception_handler async def async_send_command(self, command: List, **kwargs) -> None: """Send a command to one device.""" num_repeats = kwargs[ATTR_NUM_REPEATS]