From 15e5f516d2229ce011e78fc64a53e62bcc23ede8 Mon Sep 17 00:00:00 2001 From: Chris Talkington Date: Sat, 5 Feb 2022 22:17:31 -0600 Subject: [PATCH] Update rokuecp to 0.13.1 (#65814) --- .strict-typing | 1 + homeassistant/components/roku/__init__.py | 5 ++-- homeassistant/components/roku/browse_media.py | 8 +++--- homeassistant/components/roku/config_flow.py | 13 ++++++--- homeassistant/components/roku/entity.py | 7 ++--- homeassistant/components/roku/manifest.json | 2 +- homeassistant/components/roku/media_player.py | 27 +++++++++++-------- homeassistant/components/roku/remote.py | 8 +++--- mypy.ini | 11 ++++++++ requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 11 files changed, 56 insertions(+), 30 deletions(-) diff --git a/.strict-typing b/.strict-typing index f2c0c29ef27..efdccdb1a43 100644 --- a/.strict-typing +++ b/.strict-typing @@ -152,6 +152,7 @@ homeassistant.components.remote.* homeassistant.components.renault.* homeassistant.components.ridwell.* homeassistant.components.rituals_perfume_genie.* +homeassistant.components.roku.* homeassistant.components.rpi_power.* homeassistant.components.rtsp_to_webrtc.* homeassistant.components.samsungtv.* diff --git a/homeassistant/components/roku/__init__.py b/homeassistant/components/roku/__init__.py index 4d97e8c5fac..ac7a9e38565 100644 --- a/homeassistant/components/roku/__init__.py +++ b/homeassistant/components/roku/__init__.py @@ -1,6 +1,7 @@ """Support for Roku.""" from __future__ import annotations +from collections.abc import Callable import logging from rokuecp import RokuConnectionError, RokuError @@ -46,10 +47,10 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: return unload_ok -def roku_exception_handler(func): +def roku_exception_handler(func: Callable) -> Callable: """Decorate Roku calls to handle Roku exceptions.""" - async def handler(self, *args, **kwargs): + async def handler(self, *args, **kwargs) -> None: # type: ignore try: await func(self, *args, **kwargs) except RokuConnectionError as error: diff --git a/homeassistant/components/roku/browse_media.py b/homeassistant/components/roku/browse_media.py index 49af4740123..7aed3849ce8 100644 --- a/homeassistant/components/roku/browse_media.py +++ b/homeassistant/components/roku/browse_media.py @@ -69,12 +69,12 @@ def get_thumbnail_url_full( async def async_browse_media( - hass, + hass: HomeAssistant, coordinator: RokuDataUpdateCoordinator, get_browse_image_url: GetBrowseImageUrlType, media_content_id: str | None, media_content_type: str | None, -): +) -> BrowseMedia: """Browse media.""" if media_content_id is None: return await root_payload( @@ -113,7 +113,7 @@ async def root_payload( hass: HomeAssistant, coordinator: RokuDataUpdateCoordinator, get_browse_image_url: GetBrowseImageUrlType, -): +) -> BrowseMedia: """Return root payload for Roku.""" device = coordinator.data @@ -223,7 +223,7 @@ def item_payload( item: dict, coordinator: RokuDataUpdateCoordinator, get_browse_image_url: GetBrowseImageUrlType, -): +) -> BrowseMedia: """ Create response payload for a single media item. diff --git a/homeassistant/components/roku/config_flow.py b/homeassistant/components/roku/config_flow.py index dff0f7d53c6..e3b8b97aa8f 100644 --- a/homeassistant/components/roku/config_flow.py +++ b/homeassistant/components/roku/config_flow.py @@ -2,6 +2,7 @@ from __future__ import annotations import logging +from typing import Any from urllib.parse import urlparse from rokuecp import Roku, RokuError @@ -24,7 +25,7 @@ ERROR_UNKNOWN = "unknown" _LOGGER = logging.getLogger(__name__) -async def validate_input(hass: HomeAssistant, data: dict) -> dict: +async def validate_input(hass: HomeAssistant, data: dict[str, Any]) -> dict[str, Any]: """Validate the user input allows us to connect. Data has the keys from DATA_SCHEMA with values provided by the user. @@ -44,12 +45,14 @@ class RokuConfigFlow(ConfigFlow, domain=DOMAIN): VERSION = 1 - def __init__(self): + discovery_info: dict[str, Any] + + def __init__(self) -> None: """Set up the instance.""" self.discovery_info = {} @callback - def _show_form(self, errors: dict | None = None) -> FlowResult: + def _show_form(self, errors: dict[str, Any] | None = None) -> FlowResult: """Show the form to the user.""" return self.async_show_form( step_id="user", @@ -57,7 +60,9 @@ class RokuConfigFlow(ConfigFlow, domain=DOMAIN): errors=errors or {}, ) - async def async_step_user(self, user_input: dict | None = None) -> FlowResult: + async def async_step_user( + self, user_input: dict[str, Any] | None = None + ) -> FlowResult: """Handle a flow initialized by the user.""" if not user_input: return self._show_form() diff --git a/homeassistant/components/roku/entity.py b/homeassistant/components/roku/entity.py index 261d0c95445..ef969b846aa 100644 --- a/homeassistant/components/roku/entity.py +++ b/homeassistant/components/roku/entity.py @@ -17,7 +17,7 @@ class RokuEntity(CoordinatorEntity): def __init__( self, *, - device_id: str, + device_id: str | None, coordinator: RokuDataUpdateCoordinator, description: EntityDescription | None = None, ) -> None: @@ -28,10 +28,11 @@ class RokuEntity(CoordinatorEntity): if description is not None: self.entity_description = description self._attr_name = f"{coordinator.data.info.name} {description.name}" - self._attr_unique_id = f"{device_id}_{description.key}" + if device_id is not None: + self._attr_unique_id = f"{device_id}_{description.key}" @property - def device_info(self) -> DeviceInfo: + def device_info(self) -> DeviceInfo | None: """Return device information about this Roku device.""" if self._device_id is None: return None diff --git a/homeassistant/components/roku/manifest.json b/homeassistant/components/roku/manifest.json index 6d7989e93cb..619672e8f1f 100644 --- a/homeassistant/components/roku/manifest.json +++ b/homeassistant/components/roku/manifest.json @@ -2,7 +2,7 @@ "domain": "roku", "name": "Roku", "documentation": "https://www.home-assistant.io/integrations/roku", - "requirements": ["rokuecp==0.12.0"], + "requirements": ["rokuecp==0.13.1"], "homekit": { "models": ["3810X", "4660X", "7820X", "C105X", "C135X"] }, diff --git a/homeassistant/components/roku/media_player.py b/homeassistant/components/roku/media_player.py index 67abae262d5..d6ad5c9cd13 100644 --- a/homeassistant/components/roku/media_player.py +++ b/homeassistant/components/roku/media_player.py @@ -117,7 +117,9 @@ async def async_setup_entry( class RokuMediaPlayer(RokuEntity, MediaPlayerEntity): """Representation of a Roku media player on the network.""" - def __init__(self, unique_id: str, coordinator: RokuDataUpdateCoordinator) -> None: + def __init__( + self, unique_id: str | None, coordinator: RokuDataUpdateCoordinator + ) -> None: """Initialize the Roku device.""" super().__init__( coordinator=coordinator, @@ -231,7 +233,7 @@ class RokuMediaPlayer(RokuEntity, MediaPlayerEntity): @property def media_duration(self) -> int | None: """Duration of current playing media in seconds.""" - if self._media_playback_trackable(): + if self.coordinator.data.media is not None and self._media_playback_trackable(): return self.coordinator.data.media.duration return None @@ -239,7 +241,7 @@ class RokuMediaPlayer(RokuEntity, MediaPlayerEntity): @property def media_position(self) -> int | None: """Position of current playing media in seconds.""" - if self._media_playback_trackable(): + if self.coordinator.data.media is not None and self._media_playback_trackable(): return self.coordinator.data.media.position return None @@ -247,7 +249,7 @@ class RokuMediaPlayer(RokuEntity, MediaPlayerEntity): @property def media_position_updated_at(self) -> dt.datetime | None: """When was the position of the current playing media valid.""" - if self._media_playback_trackable(): + if self.coordinator.data.media is not None and self._media_playback_trackable(): return self.coordinator.data.media.at return None @@ -263,10 +265,12 @@ class RokuMediaPlayer(RokuEntity, MediaPlayerEntity): @property def source_list(self) -> list: """List of available input sources.""" - return ["Home"] + sorted(app.name for app in self.coordinator.data.apps) + return ["Home"] + sorted( + app.name for app in self.coordinator.data.apps if app.name is not None + ) @roku_exception_handler - async def search(self, keyword): + async def search(self, keyword: str) -> None: """Emulate opening the search screen and entering the search keyword.""" await self.coordinator.roku.search(keyword) @@ -343,7 +347,7 @@ class RokuMediaPlayer(RokuEntity, MediaPlayerEntity): await self.coordinator.async_request_refresh() @roku_exception_handler - async def async_mute_volume(self, mute) -> None: + async def async_mute_volume(self, mute: bool) -> None: """Mute the volume.""" await self.coordinator.roku.remote("volume_mute") await self.coordinator.async_request_refresh() @@ -359,7 +363,9 @@ class RokuMediaPlayer(RokuEntity, MediaPlayerEntity): await self.coordinator.roku.remote("volume_down") @roku_exception_handler - async def async_play_media(self, media_type: str, media_id: str, **kwargs) -> None: + async def async_play_media( + self, media_type: str, media_id: str, **kwargs: Any + ) -> None: """Play media from a URL or file, launch an application, or tune to a channel.""" extra: dict[str, Any] = kwargs.get(ATTR_MEDIA_EXTRA) or {} @@ -433,7 +439,6 @@ class RokuMediaPlayer(RokuEntity, MediaPlayerEntity): None, ) - if appl is not None: + if appl is not None and appl.app_id is not None: await self.coordinator.roku.launch(appl.app_id) - - await self.coordinator.async_request_refresh() + await self.coordinator.async_request_refresh() diff --git a/homeassistant/components/roku/remote.py b/homeassistant/components/roku/remote.py index 8f0d39ed1d9..96c04597d89 100644 --- a/homeassistant/components/roku/remote.py +++ b/homeassistant/components/roku/remote.py @@ -1,6 +1,8 @@ """Support for the Roku remote.""" from __future__ import annotations +from typing import Any + from homeassistant.components.remote import ATTR_NUM_REPEATS, RemoteEntity from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant @@ -42,19 +44,19 @@ class RokuRemote(RokuEntity, RemoteEntity): return not self.coordinator.data.state.standby @roku_exception_handler - async def async_turn_on(self, **kwargs) -> None: + async def async_turn_on(self, **kwargs: Any) -> None: """Turn the device on.""" await self.coordinator.roku.remote("poweron") await self.coordinator.async_request_refresh() @roku_exception_handler - async def async_turn_off(self, **kwargs) -> None: + async def async_turn_off(self, **kwargs: Any) -> None: """Turn the device off.""" await self.coordinator.roku.remote("poweroff") await self.coordinator.async_request_refresh() @roku_exception_handler - async def async_send_command(self, command: list, **kwargs) -> None: + async def async_send_command(self, command: list, **kwargs: Any) -> None: """Send a command to one device.""" num_repeats = kwargs[ATTR_NUM_REPEATS] diff --git a/mypy.ini b/mypy.ini index 524ef2c5420..96d58927959 100644 --- a/mypy.ini +++ b/mypy.ini @@ -1489,6 +1489,17 @@ no_implicit_optional = true warn_return_any = true warn_unreachable = true +[mypy-homeassistant.components.roku.*] +check_untyped_defs = true +disallow_incomplete_defs = true +disallow_subclassing_any = true +disallow_untyped_calls = true +disallow_untyped_decorators = true +disallow_untyped_defs = true +no_implicit_optional = true +warn_return_any = true +warn_unreachable = true + [mypy-homeassistant.components.rpi_power.*] check_untyped_defs = true disallow_incomplete_defs = true diff --git a/requirements_all.txt b/requirements_all.txt index 39b2f6a8378..04df44d6fa8 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2111,7 +2111,7 @@ rjpl==0.3.6 rocketchat-API==0.6.1 # homeassistant.components.roku -rokuecp==0.12.0 +rokuecp==0.13.1 # homeassistant.components.roomba roombapy==1.6.5 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 9f89b0faa50..e3823228887 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1300,7 +1300,7 @@ rflink==0.0.62 ring_doorbell==0.7.2 # homeassistant.components.roku -rokuecp==0.12.0 +rokuecp==0.13.1 # homeassistant.components.roomba roombapy==1.6.5