Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update vizio app_id state attribute to show app config dict instead of app name #32727

Merged
merged 6 commits into from Mar 25, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion homeassistant/components/vizio/manifest.json
Expand Up @@ -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,
Expand Down
44 changes: 23 additions & 21 deletions homeassistant/components/vizio/media_player.py
Expand Up @@ -4,8 +4,8 @@
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,
Expand Down Expand Up @@ -132,6 +132,7 @@ def __init__(
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, {})
Expand All @@ -157,20 +158,6 @@ def _apps_list(self, apps: List[str]) -> List[str]:

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:
Expand Down Expand Up @@ -202,6 +189,7 @@ async def async_update(self) -> None:
self._current_input = None
self._available_inputs = None
self._current_app = None
self._current_app_config = None
self._available_apps = None
return

Expand Down Expand Up @@ -237,9 +225,16 @@ async def async_update(self) -> None:
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."""
Expand Down Expand Up @@ -346,8 +341,15 @@ def source_list(self) -> List[str]:

@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]:
Expand Down
2 changes: 1 addition & 1 deletion requirements_all.txt
Expand Up @@ -1724,7 +1724,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
Expand Down
2 changes: 1 addition & 1 deletion requirements_test_all.txt
Expand Up @@ -628,7 +628,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
Expand Down
6 changes: 3 additions & 3 deletions tests/components/vizio/conftest.py
Expand Up @@ -7,7 +7,7 @@
ACCESS_TOKEN,
APP_LIST,
CH_TYPE,
CURRENT_APP,
CURRENT_APP_CONFIG,
CURRENT_INPUT,
INPUT_LIST,
INPUT_LIST_WITH_APPS,
Expand Down Expand Up @@ -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
6 changes: 6 additions & 0 deletions tests/components/vizio/const.py
Expand Up @@ -68,13 +68,19 @@ def __init__(self, auth_token: str) -> None:
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}
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)}"

Expand Down
86 changes: 66 additions & 20 deletions 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,
Expand Down Expand Up @@ -57,6 +57,7 @@
ADDITIONAL_APP_CONFIG,
APP_LIST,
CURRENT_APP,
CURRENT_APP_CONFIG,
CURRENT_INPUT,
CUSTOM_CONFIG,
ENTITY_ID,
Expand All @@ -71,6 +72,7 @@
MOCK_USER_VALID_TV_CONFIG,
NAME,
UNIQUE_ID,
UNKNOWN_APP_CONFIG,
VOLUME_STEP,
)

Expand All @@ -80,7 +82,7 @@


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:
Expand Down Expand Up @@ -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)
Expand All @@ -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(
Expand All @@ -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)
Expand Down Expand Up @@ -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))
Expand All @@ -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:
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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",
Expand All @@ -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(
Expand All @@ -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(
Expand All @@ -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,
Expand Down Expand Up @@ -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())
)