From 772df02cce3ce3df5a6fd71f61038e5af20607ea Mon Sep 17 00:00:00 2001 From: ollo69 <60491700+ollo69@users.noreply.github.com> Date: Mon, 30 Jan 2023 22:42:32 +0100 Subject: [PATCH] Catch AndroidTV exception on setup (#86819) fixes undefined --- .../components/androidtv/__init__.py | 34 +++++++++++++++++-- .../components/androidtv/media_player.py | 22 ++---------- tests/components/androidtv/patchers.py | 4 +-- .../components/androidtv/test_media_player.py | 16 +++++---- 4 files changed, 46 insertions(+), 30 deletions(-) diff --git a/homeassistant/components/androidtv/__init__.py b/homeassistant/components/androidtv/__init__.py index c2d83ab05e8..d10b1161da6 100644 --- a/homeassistant/components/androidtv/__init__.py +++ b/homeassistant/components/androidtv/__init__.py @@ -6,6 +6,13 @@ import os from typing import Any from adb_shell.auth.keygen import keygen +from adb_shell.exceptions import ( + AdbTimeoutError, + InvalidChecksumError, + InvalidCommandError, + InvalidResponseError, + TcpTimeoutException, +) from androidtv.adb_manager.adb_manager_sync import ADBPythonSync, PythonRSASigner from androidtv.setup_async import ( AndroidTVAsync, @@ -43,6 +50,18 @@ from .const import ( SIGNAL_CONFIG_ENTITY, ) +ADB_PYTHON_EXCEPTIONS: tuple = ( + AdbTimeoutError, + BrokenPipeError, + ConnectionResetError, + ValueError, + InvalidChecksumError, + InvalidCommandError, + InvalidResponseError, + TcpTimeoutException, +) +ADB_TCP_EXCEPTIONS: tuple = (ConnectionResetError, RuntimeError) + PLATFORMS = [Platform.MEDIA_PLAYER] RELOAD_OPTIONS = [CONF_STATE_DETECTION_RULES] @@ -132,9 +151,18 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up Android TV platform.""" state_det_rules = entry.options.get(CONF_STATE_DETECTION_RULES) - aftv, error_message = await async_connect_androidtv( - hass, entry.data, state_detection_rules=state_det_rules - ) + if CONF_ADB_SERVER_IP not in entry.data: + exceptions = ADB_PYTHON_EXCEPTIONS + else: + exceptions = ADB_TCP_EXCEPTIONS + + try: + aftv, error_message = await async_connect_androidtv( + hass, entry.data, state_detection_rules=state_det_rules + ) + except exceptions as exc: + raise ConfigEntryNotReady(exc) from exc + if not aftv: raise ConfigEntryNotReady(error_message) diff --git a/homeassistant/components/androidtv/media_player.py b/homeassistant/components/androidtv/media_player.py index d77c755110f..f4be6d6eea5 100644 --- a/homeassistant/components/androidtv/media_player.py +++ b/homeassistant/components/androidtv/media_player.py @@ -7,13 +7,6 @@ import functools import logging from typing import Any, Concatenate, ParamSpec, TypeVar -from adb_shell.exceptions import ( - AdbTimeoutError, - InvalidChecksumError, - InvalidCommandError, - InvalidResponseError, - TcpTimeoutException, -) from androidtv.constants import APPS, KEYS from androidtv.exceptions import LockNotAcquiredException import voluptuous as vol @@ -42,7 +35,7 @@ from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.entity_platform import AddEntitiesCallback -from . import get_androidtv_mac +from . import ADB_PYTHON_EXCEPTIONS, ADB_TCP_EXCEPTIONS, get_androidtv_mac from .const import ( ANDROID_DEV, ANDROID_DEV_OPT, @@ -252,19 +245,10 @@ class ADBDevice(MediaPlayerEntity): # ADB exceptions to catch if not aftv.adb_server_ip: # Using "adb_shell" (Python ADB implementation) - self.exceptions = ( - AdbTimeoutError, - BrokenPipeError, - ConnectionResetError, - ValueError, - InvalidChecksumError, - InvalidCommandError, - InvalidResponseError, - TcpTimeoutException, - ) + self.exceptions = ADB_PYTHON_EXCEPTIONS else: # Using "pure-python-adb" (communicate with ADB server) - self.exceptions = (ConnectionResetError, RuntimeError) + self.exceptions = ADB_TCP_EXCEPTIONS # Property attributes self._attr_extra_state_attributes = { diff --git a/tests/components/androidtv/patchers.py b/tests/components/androidtv/patchers.py index 31e9a9c82c3..5ebd95ccacd 100644 --- a/tests/components/androidtv/patchers.py +++ b/tests/components/androidtv/patchers.py @@ -111,7 +111,7 @@ def patch_connect(success): } -def patch_shell(response=None, error=False, mac_eth=False): +def patch_shell(response=None, error=False, mac_eth=False, exc=None): """Mock the `AdbDeviceTcpAsyncFake.shell` and `DeviceAsyncFake.shell` methods.""" async def shell_success(self, cmd, *args, **kwargs): @@ -128,7 +128,7 @@ def patch_shell(response=None, error=False, mac_eth=False): async def shell_fail_python(self, cmd, *args, **kwargs): """Mock the `AdbDeviceTcpAsyncFake.shell` method when it fails.""" self.shell_cmd = cmd - raise ValueError + raise exc or ValueError async def shell_fail_server(self, cmd): """Mock the `DeviceAsyncFake.shell` method when it fails.""" diff --git a/tests/components/androidtv/test_media_player.py b/tests/components/androidtv/test_media_player.py index 5aaf0e1b9c8..a0d6230ed6b 100644 --- a/tests/components/androidtv/test_media_player.py +++ b/tests/components/androidtv/test_media_player.py @@ -2,6 +2,7 @@ import logging from unittest.mock import Mock, patch +from adb_shell.exceptions import TcpTimeoutException as AdbShellTimeoutException from androidtv.constants import APPS as ANDROIDTV_APPS, KEYS from androidtv.exceptions import LockNotAcquiredException import pytest @@ -538,25 +539,28 @@ async def test_select_source_firetv(hass, source, expected_arg, method_patch): @pytest.mark.parametrize( - "config", + ["config", "connect"], [ - CONFIG_ANDROIDTV_DEFAULT, - CONFIG_FIRETV_DEFAULT, + (CONFIG_ANDROIDTV_DEFAULT, False), + (CONFIG_FIRETV_DEFAULT, False), + (CONFIG_ANDROIDTV_DEFAULT, True), + (CONFIG_FIRETV_DEFAULT, True), ], ) -async def test_setup_fail(hass, config): +async def test_setup_fail(hass, config, connect): """Test that the entity is not created when the ADB connection is not established.""" patch_key, entity_id, config_entry = _setup(config) config_entry.add_to_hass(hass) - with patchers.patch_connect(False)[patch_key], patchers.patch_shell( - SHELL_RESPONSE_OFF + with patchers.patch_connect(connect)[patch_key], patchers.patch_shell( + SHELL_RESPONSE_OFF, error=True, exc=AdbShellTimeoutException )[patch_key]: assert await hass.config_entries.async_setup(config_entry.entry_id) is False await hass.async_block_till_done() await async_update_entity(hass, entity_id) state = hass.states.get(entity_id) + assert config_entry.state == ConfigEntryState.SETUP_RETRY assert state is None