Update vizio app_id state attribute to show app config dict in… (#32727)

* 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
This commit is contained in:
Raman Gupta 2020-03-25 12:35:36 -04:00 committed by GitHub
parent 4a0a56ebdc
commit d5f4dfdd6b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 101 additions and 47 deletions

View File

@ -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,

View File

@ -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]:

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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)}"

View File

@ -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
if app:
assert app in attr["source_list"] or app == UNKNOWN_APP
if app == UNKNOWN_APP:
assert attr["source"] == ADDITIONAL_APP_CONFIG["name"]
else:
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())
)