From b77335d6f9f5395293942e5031f7dbaf24163b01 Mon Sep 17 00:00:00 2001 From: Alex Henry Date: Wed, 4 Aug 2021 22:44:16 +1200 Subject: [PATCH] Fix Panasonic Viera TV going unavailable when turned off (#53788) --- .../components/panasonic_viera/__init__.py | 8 ++-- tests/components/panasonic_viera/conftest.py | 37 ++++++++++----- .../panasonic_viera/test_media_player.py | 47 +++++++++++++++++++ .../components/panasonic_viera/test_remote.py | 7 ++- 4 files changed, 82 insertions(+), 17 deletions(-) create mode 100644 tests/components/panasonic_viera/test_media_player.py diff --git a/homeassistant/components/panasonic_viera/__init__.py b/homeassistant/components/panasonic_viera/__init__.py index b217be4d4b6..e187b7c18a5 100644 --- a/homeassistant/components/panasonic_viera/__init__.py +++ b/homeassistant/components/panasonic_viera/__init__.py @@ -1,7 +1,7 @@ """The Panasonic Viera integration.""" from functools import partial import logging -from urllib.request import URLError +from urllib.request import HTTPError, URLError from panasonic_viera import EncryptionRequired, Keys, RemoteControl, SOAPError import voluptuous as vol @@ -247,11 +247,13 @@ class Remote: "The connection couldn't be encrypted. Please reconfigure your TV" ) self.available = False - except (SOAPError): + except (SOAPError, HTTPError) as err: + _LOGGER.debug("An error occurred: %s", err) self.state = STATE_OFF self.available = True await self.async_create_remote_control() - except (URLError, OSError): + except (URLError, OSError) as err: + _LOGGER.debug("An error occurred: %s", err) self.state = STATE_OFF self.available = self._on_action is not None await self.async_create_remote_control() diff --git a/tests/components/panasonic_viera/conftest.py b/tests/components/panasonic_viera/conftest.py index d1444f01477..e30c0f41e92 100644 --- a/tests/components/panasonic_viera/conftest.py +++ b/tests/components/panasonic_viera/conftest.py @@ -17,8 +17,12 @@ from homeassistant.components.panasonic_viera.const import ( DEFAULT_MODEL_NUMBER, DEFAULT_NAME, DEFAULT_PORT, + DOMAIN, ) from homeassistant.const import CONF_HOST, CONF_NAME, CONF_PORT +from homeassistant.core import HomeAssistant + +from tests.common import MockConfigEntry MOCK_BASIC_DATA = { CONF_HOST: "0.0.0.0", @@ -74,20 +78,11 @@ def get_mock_remote( mock_remote.authorize_pin_code = authorize_pin_code - def get_device_info(): - return device_info + mock_remote.get_device_info = Mock(return_value=device_info) - mock_remote.get_device_info = get_device_info + mock_remote.send_key = Mock() - def send_key(key): - return - - mock_remote.send_key = Mock(send_key) - - def get_volume(key): - return 100 - - mock_remote.get_volume = Mock(get_volume) + mock_remote.get_volume = Mock(return_value=100) return mock_remote @@ -102,3 +97,21 @@ def mock_remote_fixture(): return_value=mock_remote, ): yield mock_remote + + +@pytest.fixture +async def init_integration(hass: HomeAssistant, mock_remote: Mock) -> MockConfigEntry: + """Set up the Panasonic Viera integration for testing.""" + + mock_entry = MockConfigEntry( + domain=DOMAIN, + unique_id=MOCK_DEVICE_INFO[ATTR_UDN], + data={**MOCK_CONFIG_DATA, **MOCK_ENCRYPTION_DATA, **MOCK_DEVICE_INFO}, + ) + + mock_entry.add_to_hass(hass) + + await hass.config_entries.async_setup(mock_entry.entry_id) + await hass.async_block_till_done() + + return mock_entry diff --git a/tests/components/panasonic_viera/test_media_player.py b/tests/components/panasonic_viera/test_media_player.py new file mode 100644 index 00000000000..1203bf1ed51 --- /dev/null +++ b/tests/components/panasonic_viera/test_media_player.py @@ -0,0 +1,47 @@ +"""Test the Panasonic Viera media player entity.""" + +from datetime import timedelta +from unittest.mock import Mock +from urllib.error import HTTPError, URLError + +from homeassistant.const import STATE_OFF, STATE_ON, STATE_UNAVAILABLE +from homeassistant.core import HomeAssistant +from homeassistant.util.dt import utcnow + +from tests.common import MockConfigEntry, async_fire_time_changed + + +async def test_media_player_handle_URLerror( + hass: HomeAssistant, init_integration: MockConfigEntry, mock_remote: Mock +) -> None: + """Test remote handle URLError as Unavailable.""" + + state_tv = hass.states.get("media_player.panasonic_viera_tv") + assert state_tv.state == STATE_ON + + # simulate timeout error + mock_remote.get_mute = Mock(side_effect=URLError(None, None)) + + async_fire_time_changed(hass, utcnow() + timedelta(minutes=2)) + await hass.async_block_till_done() + + state_tv = hass.states.get("media_player.panasonic_viera_tv") + assert state_tv.state == STATE_UNAVAILABLE + + +async def test_media_player_handle_HTTPError( + hass: HomeAssistant, init_integration: MockConfigEntry, mock_remote: Mock +) -> None: + """Test remote handle HTTPError as Off.""" + + state_tv = hass.states.get("media_player.panasonic_viera_tv") + assert state_tv.state == STATE_ON + + # simulate http badrequest + mock_remote.get_mute = Mock(side_effect=HTTPError(None, 400, None, None, None)) + + async_fire_time_changed(hass, utcnow() + timedelta(minutes=2)) + await hass.async_block_till_done() + + state_tv = hass.states.get("media_player.panasonic_viera_tv") + assert state_tv.state == STATE_OFF diff --git a/tests/components/panasonic_viera/test_remote.py b/tests/components/panasonic_viera/test_remote.py index 6bfd7dee8eb..0cf80853351 100644 --- a/tests/components/panasonic_viera/test_remote.py +++ b/tests/components/panasonic_viera/test_remote.py @@ -1,8 +1,8 @@ """Test the Panasonic Viera remote entity.""" -from unittest.mock import call +from unittest.mock import Mock, call -from panasonic_viera import Keys +from panasonic_viera import Keys, SOAPError from homeassistant.components.panasonic_viera.const import ATTR_UDN, DOMAIN from homeassistant.components.remote import ( @@ -38,6 +38,9 @@ async def test_onoff(hass, mock_remote): data = {ATTR_ENTITY_ID: "remote.panasonic_viera_tv"} + # simulate tv off when async_update + mock_remote.get_mute = Mock(side_effect=SOAPError) + await hass.services.async_call(REMOTE_DOMAIN, SERVICE_TURN_OFF, data) await hass.services.async_call(REMOTE_DOMAIN, SERVICE_TURN_ON, data) await hass.async_block_till_done()