From d5f4dfdd6bea6e804af18fb9155bd3d1a9272ad9 Mon Sep 17 00:00:00 2001 From: Raman Gupta <7243222+raman325@users.noreply.github.com> Date: Wed, 25 Mar 2020 12:35:36 -0400 Subject: [PATCH] =?UTF-8?q?Update=20vizio=20app=5Fid=20state=20attribute?= =?UTF-8?q?=20to=20show=20app=20config=20dict=20in=E2=80=A6=20(#32727)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * bump pyvizio and update app_id to show app config to aid in HA config generation. squashed from multiple commits to make a rebase on dev easier * bump pyvizio for bug fixes * fix pyvizio version number * only return app_id if app is unknown and explicitly create the dict that's returned * fix tests * fix docstring for app_id --- homeassistant/components/vizio/manifest.json | 2 +- .../components/vizio/media_player.py | 44 +++++----- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- tests/components/vizio/conftest.py | 6 +- tests/components/vizio/const.py | 6 ++ tests/components/vizio/test_media_player.py | 86 ++++++++++++++----- 7 files changed, 101 insertions(+), 47 deletions(-) diff --git a/homeassistant/components/vizio/manifest.json b/homeassistant/components/vizio/manifest.json index f1931f6fdb1..7608b6eae53 100644 --- a/homeassistant/components/vizio/manifest.json +++ b/homeassistant/components/vizio/manifest.json @@ -2,7 +2,7 @@ "domain": "vizio", "name": "Vizio SmartCast", "documentation": "https://www.home-assistant.io/integrations/vizio", - "requirements": ["pyvizio==0.1.35"], + "requirements": ["pyvizio==0.1.44"], "dependencies": [], "codeowners": ["@raman325"], "config_flow": true, diff --git a/homeassistant/components/vizio/media_player.py b/homeassistant/components/vizio/media_player.py index d97a82ca144..d463ebca36a 100644 --- a/homeassistant/components/vizio/media_player.py +++ b/homeassistant/components/vizio/media_player.py @@ -4,8 +4,8 @@ import logging from typing import Any, Callable, Dict, List, Optional from pyvizio import VizioAsync -from pyvizio.const import INPUT_APPS, NO_APP_RUNNING, UNKNOWN_APP -from pyvizio.helpers import find_app_name +from pyvizio.api.apps import find_app_name +from pyvizio.const import APP_HOME, APPS, INPUT_APPS, NO_APP_RUNNING, UNKNOWN_APP from homeassistant.components.media_player import ( DEVICE_CLASS_SPEAKER, @@ -132,6 +132,7 @@ class VizioDevice(MediaPlayerDevice): self._is_muted = None self._current_input = None self._current_app = None + self._current_app_config = None self._available_inputs = [] self._available_apps = [] self._conf_apps = config_entry.options.get(CONF_APPS, {}) @@ -157,20 +158,6 @@ class VizioDevice(MediaPlayerDevice): return apps - async def _current_app_name(self) -> Optional[str]: - """Return name of the currently running app by parsing pyvizio output.""" - app = await self._device.get_current_app(log_api_exception=False) - if app in [None, NO_APP_RUNNING]: - return None - - if app == UNKNOWN_APP and self._additional_app_configs: - return find_app_name( - await self._device.get_current_app_config(log_api_exception=False), - self._additional_app_configs, - ) - - return app - async def async_update(self) -> None: """Retrieve latest state of the device.""" if not self._model: @@ -202,6 +189,7 @@ class VizioDevice(MediaPlayerDevice): self._current_input = None self._available_inputs = None self._current_app = None + self._current_app_config = None self._available_apps = None return @@ -237,9 +225,16 @@ class VizioDevice(MediaPlayerDevice): if not self._available_apps: self._available_apps = self._apps_list(self._device.get_apps_list()) - # Attempt to get current app name. If app name is unknown, check list - # of additional apps specified in configuration - self._current_app = await self._current_app_name() + self._current_app_config = await self._device.get_current_app_config( + log_api_exception=False + ) + + self._current_app = find_app_name( + self._current_app_config, [APP_HOME, *APPS, *self._additional_app_configs] + ) + + if self._current_app == NO_APP_RUNNING: + self._current_app = None def _get_additional_app_names(self) -> List[Dict[str, Any]]: """Return list of additional apps that were included in configuration.yaml.""" @@ -346,8 +341,15 @@ class VizioDevice(MediaPlayerDevice): @property def app_id(self) -> Optional[str]: - """Return the current app.""" - return self._current_app + """Return the ID of the current app if it is unknown by pyvizio.""" + if self._current_app_config and self.app_name == UNKNOWN_APP: + return { + "APP_ID": self._current_app_config.APP_ID, + "NAME_SPACE": self._current_app_config.NAME_SPACE, + "MESSAGE": self._current_app_config.MESSAGE, + } + + return None @property def app_name(self) -> Optional[str]: diff --git a/requirements_all.txt b/requirements_all.txt index 75027277f24..7defeea4c9c 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1732,7 +1732,7 @@ pyversasense==0.0.6 pyvesync==1.1.0 # homeassistant.components.vizio -pyvizio==0.1.35 +pyvizio==0.1.44 # homeassistant.components.velux pyvlx==0.2.12 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index a4f6a0e1add..ff686fe5e30 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -635,7 +635,7 @@ pyvera==0.3.7 pyvesync==1.1.0 # homeassistant.components.vizio -pyvizio==0.1.35 +pyvizio==0.1.44 # homeassistant.components.html5 pywebpush==1.9.2 diff --git a/tests/components/vizio/conftest.py b/tests/components/vizio/conftest.py index ab581bdf3c6..868bf44a11b 100644 --- a/tests/components/vizio/conftest.py +++ b/tests/components/vizio/conftest.py @@ -7,7 +7,7 @@ from .const import ( ACCESS_TOKEN, APP_LIST, CH_TYPE, - CURRENT_APP, + CURRENT_APP_CONFIG, CURRENT_INPUT, INPUT_LIST, INPUT_LIST_WITH_APPS, @@ -172,7 +172,7 @@ def vizio_update_with_apps_fixture(vizio_update: pytest.fixture): "homeassistant.components.vizio.media_player.VizioAsync.get_current_input", return_value="CAST", ), patch( - "homeassistant.components.vizio.media_player.VizioAsync.get_current_app", - return_value=CURRENT_APP, + "homeassistant.components.vizio.media_player.VizioAsync.get_current_app_config", + return_value=CURRENT_APP_CONFIG, ): yield diff --git a/tests/components/vizio/const.py b/tests/components/vizio/const.py index 25abd01d53b..f1ddc4abba6 100644 --- a/tests/components/vizio/const.py +++ b/tests/components/vizio/const.py @@ -68,6 +68,7 @@ CURRENT_INPUT = "HDMI" INPUT_LIST = ["HDMI", "USB", "Bluetooth", "AUX"] CURRENT_APP = "Hulu" +CURRENT_APP_CONFIG = {CONF_APP_ID: "3", CONF_NAME_SPACE: 4, CONF_MESSAGE: None} APP_LIST = ["Hulu", "Netflix"] INPUT_LIST_WITH_APPS = INPUT_LIST + ["CAST"] CUSTOM_CONFIG = {CONF_APP_ID: "test", CONF_MESSAGE: None, CONF_NAME_SPACE: 10} @@ -75,6 +76,11 @@ ADDITIONAL_APP_CONFIG = { "name": CURRENT_APP, CONF_CONFIG: CUSTOM_CONFIG, } +UNKNOWN_APP_CONFIG = { + "APP_ID": "UNKNOWN", + "NAME_SPACE": 10, + "MESSAGE": None, +} ENTITY_ID = f"{MP_DOMAIN}.{slugify(NAME)}" diff --git a/tests/components/vizio/test_media_player.py b/tests/components/vizio/test_media_player.py index ebeef1661ed..f860c1cec4f 100644 --- a/tests/components/vizio/test_media_player.py +++ b/tests/components/vizio/test_media_player.py @@ -1,13 +1,13 @@ """Tests for Vizio config flow.""" from datetime import timedelta import logging -from typing import Any, Dict +from typing import Any, Dict, Optional from unittest.mock import call from asynctest import patch import pytest from pytest import raises -from pyvizio._api.apps import AppConfig +from pyvizio.api.apps import AppConfig from pyvizio.const import ( DEVICE_CLASS_SPEAKER as VIZIO_DEVICE_CLASS_SPEAKER, DEVICE_CLASS_TV as VIZIO_DEVICE_CLASS_TV, @@ -57,6 +57,7 @@ from .const import ( ADDITIONAL_APP_CONFIG, APP_LIST, CURRENT_APP, + CURRENT_APP_CONFIG, CURRENT_INPUT, CUSTOM_CONFIG, ENTITY_ID, @@ -71,6 +72,7 @@ from .const import ( MOCK_USER_VALID_TV_CONFIG, NAME, UNIQUE_ID, + UNKNOWN_APP_CONFIG, VOLUME_STEP, ) @@ -80,7 +82,7 @@ _LOGGER = logging.getLogger(__name__) async def _test_setup( - hass: HomeAssistantType, ha_device_class: str, vizio_power_state: bool + hass: HomeAssistantType, ha_device_class: str, vizio_power_state: Optional[bool] ) -> None: """Test Vizio Device entity setup.""" if vizio_power_state: @@ -112,7 +114,7 @@ async def _test_setup( "homeassistant.components.vizio.media_player.VizioAsync.get_power_state", return_value=vizio_power_state, ), patch( - "homeassistant.components.vizio.media_player.VizioAsync.get_current_app", + "homeassistant.components.vizio.media_player.VizioAsync.get_current_app_config", ) as service_call: config_entry.add_to_hass(hass) assert await hass.config_entries.async_setup(config_entry.entry_id) @@ -136,7 +138,10 @@ async def _test_setup( async def _test_setup_with_apps( - hass: HomeAssistantType, device_config: Dict[str, Any], app: str + hass: HomeAssistantType, + device_config: Dict[str, Any], + app: Optional[str], + app_config: Dict[str, Any], ) -> None: """Test Vizio Device with apps entity setup.""" config_entry = MockConfigEntry( @@ -152,12 +157,9 @@ async def _test_setup_with_apps( ), patch( "homeassistant.components.vizio.media_player.VizioAsync.get_power_state", return_value=True, - ), patch( - "homeassistant.components.vizio.media_player.VizioAsync.get_current_app", - return_value=app, ), patch( "homeassistant.components.vizio.media_player.VizioAsync.get_current_app_config", - return_value=AppConfig(**ADDITIONAL_APP_CONFIG["config"]), + return_value=AppConfig(**app_config), ): config_entry.add_to_hass(hass) assert await hass.config_entries.async_setup(config_entry.entry_id) @@ -193,11 +195,20 @@ async def _test_setup_with_apps( list_to_test.remove(app_to_remove) assert attr["source_list"] == list_to_test - assert app in attr["source_list"] or app == UNKNOWN_APP - if app == UNKNOWN_APP: - assert attr["source"] == ADDITIONAL_APP_CONFIG["name"] - else: + + if app: + assert app in attr["source_list"] or app == UNKNOWN_APP assert attr["source"] == app + assert attr["app_name"] == app + if app == UNKNOWN_APP: + assert attr["app_id"] == app_config + else: + assert "app_id" not in attr + else: + assert attr["source"] == "CAST" + assert "app_id" not in attr + assert "app_name" not in attr + assert ( attr["volume_level"] == float(int(MAX_VOLUME[VIZIO_DEVICE_CLASS_TV] / 2)) @@ -222,7 +233,7 @@ async def _test_service( hass: HomeAssistantType, vizio_func_name: str, ha_service_name: str, - additional_service_data: dict, + additional_service_data: Optional[Dict[str, Any]], *args, **kwargs, ) -> None: @@ -363,8 +374,8 @@ async def test_options_update( async def _test_update_availability_switch( hass: HomeAssistantType, - initial_power_state: bool, - final_power_state: bool, + initial_power_state: Optional[bool], + final_power_state: Optional[bool], caplog: pytest.fixture, ) -> None: now = dt_util.utcnow() @@ -431,7 +442,9 @@ async def test_setup_with_apps( caplog: pytest.fixture, ) -> None: """Test device setup with apps.""" - await _test_setup_with_apps(hass, MOCK_USER_VALID_TV_CONFIG, CURRENT_APP) + await _test_setup_with_apps( + hass, MOCK_USER_VALID_TV_CONFIG, CURRENT_APP, CURRENT_APP_CONFIG + ) await _test_service( hass, "launch_app", @@ -448,7 +461,9 @@ async def test_setup_with_apps_include( caplog: pytest.fixture, ) -> None: """Test device setup with apps and apps["include"] in config.""" - await _test_setup_with_apps(hass, MOCK_TV_WITH_INCLUDE_CONFIG, CURRENT_APP) + await _test_setup_with_apps( + hass, MOCK_TV_WITH_INCLUDE_CONFIG, CURRENT_APP, CURRENT_APP_CONFIG + ) async def test_setup_with_apps_exclude( @@ -458,7 +473,9 @@ async def test_setup_with_apps_exclude( caplog: pytest.fixture, ) -> None: """Test device setup with apps and apps["exclude"] in config.""" - await _test_setup_with_apps(hass, MOCK_TV_WITH_EXCLUDE_CONFIG, CURRENT_APP) + await _test_setup_with_apps( + hass, MOCK_TV_WITH_EXCLUDE_CONFIG, CURRENT_APP, CURRENT_APP_CONFIG + ) async def test_setup_with_apps_additional_apps_config( @@ -468,7 +485,12 @@ async def test_setup_with_apps_additional_apps_config( caplog: pytest.fixture, ) -> None: """Test device setup with apps and apps["additional_configs"] in config.""" - await _test_setup_with_apps(hass, MOCK_TV_WITH_ADDITIONAL_APPS_CONFIG, UNKNOWN_APP) + await _test_setup_with_apps( + hass, + MOCK_TV_WITH_ADDITIONAL_APPS_CONFIG, + ADDITIONAL_APP_CONFIG["name"], + ADDITIONAL_APP_CONFIG["config"], + ) await _test_service( hass, @@ -508,3 +530,27 @@ def test_invalid_apps_config(hass: HomeAssistantType): with raises(vol.Invalid): vol.Schema(vol.All(VIZIO_SCHEMA, validate_apps))(MOCK_SPEAKER_APPS_FAILURE) + + +async def test_setup_with_unknown_app_config( + hass: HomeAssistantType, + vizio_connect: pytest.fixture, + vizio_update_with_apps: pytest.fixture, + caplog: pytest.fixture, +) -> None: + """Test device setup with apps where app config returned is unknown.""" + await _test_setup_with_apps( + hass, MOCK_USER_VALID_TV_CONFIG, UNKNOWN_APP, UNKNOWN_APP_CONFIG + ) + + +async def test_setup_with_no_running_app( + hass: HomeAssistantType, + vizio_connect: pytest.fixture, + vizio_update_with_apps: pytest.fixture, + caplog: pytest.fixture, +) -> None: + """Test device setup with apps where no app is running.""" + await _test_setup_with_apps( + hass, MOCK_USER_VALID_TV_CONFIG, None, vars(AppConfig()) + )