Skip to content

Commit d5f4dfd

Browse files
authored
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
1 parent 4a0a56e commit d5f4dfd

File tree

7 files changed

+101
-47
lines changed

7 files changed

+101
-47
lines changed

homeassistant/components/vizio/manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"domain": "vizio",
33
"name": "Vizio SmartCast",
44
"documentation": "https://www.home-assistant.io/integrations/vizio",
5-
"requirements": ["pyvizio==0.1.35"],
5+
"requirements": ["pyvizio==0.1.44"],
66
"dependencies": [],
77
"codeowners": ["@raman325"],
88
"config_flow": true,

homeassistant/components/vizio/media_player.py

Lines changed: 23 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44
from typing import Any, Callable, Dict, List, Optional
55

66
from pyvizio import VizioAsync
7-
from pyvizio.const import INPUT_APPS, NO_APP_RUNNING, UNKNOWN_APP
8-
from pyvizio.helpers import find_app_name
7+
from pyvizio.api.apps import find_app_name
8+
from pyvizio.const import APP_HOME, APPS, INPUT_APPS, NO_APP_RUNNING, UNKNOWN_APP
99

1010
from homeassistant.components.media_player import (
1111
DEVICE_CLASS_SPEAKER,
@@ -132,6 +132,7 @@ def __init__(
132132
self._is_muted = None
133133
self._current_input = None
134134
self._current_app = None
135+
self._current_app_config = None
135136
self._available_inputs = []
136137
self._available_apps = []
137138
self._conf_apps = config_entry.options.get(CONF_APPS, {})
@@ -157,20 +158,6 @@ def _apps_list(self, apps: List[str]) -> List[str]:
157158

158159
return apps
159160

160-
async def _current_app_name(self) -> Optional[str]:
161-
"""Return name of the currently running app by parsing pyvizio output."""
162-
app = await self._device.get_current_app(log_api_exception=False)
163-
if app in [None, NO_APP_RUNNING]:
164-
return None
165-
166-
if app == UNKNOWN_APP and self._additional_app_configs:
167-
return find_app_name(
168-
await self._device.get_current_app_config(log_api_exception=False),
169-
self._additional_app_configs,
170-
)
171-
172-
return app
173-
174161
async def async_update(self) -> None:
175162
"""Retrieve latest state of the device."""
176163
if not self._model:
@@ -202,6 +189,7 @@ async def async_update(self) -> None:
202189
self._current_input = None
203190
self._available_inputs = None
204191
self._current_app = None
192+
self._current_app_config = None
205193
self._available_apps = None
206194
return
207195

@@ -237,9 +225,16 @@ async def async_update(self) -> None:
237225
if not self._available_apps:
238226
self._available_apps = self._apps_list(self._device.get_apps_list())
239227

240-
# Attempt to get current app name. If app name is unknown, check list
241-
# of additional apps specified in configuration
242-
self._current_app = await self._current_app_name()
228+
self._current_app_config = await self._device.get_current_app_config(
229+
log_api_exception=False
230+
)
231+
232+
self._current_app = find_app_name(
233+
self._current_app_config, [APP_HOME, *APPS, *self._additional_app_configs]
234+
)
235+
236+
if self._current_app == NO_APP_RUNNING:
237+
self._current_app = None
243238

244239
def _get_additional_app_names(self) -> List[Dict[str, Any]]:
245240
"""Return list of additional apps that were included in configuration.yaml."""
@@ -346,8 +341,15 @@ def source_list(self) -> List[str]:
346341

347342
@property
348343
def app_id(self) -> Optional[str]:
349-
"""Return the current app."""
350-
return self._current_app
344+
"""Return the ID of the current app if it is unknown by pyvizio."""
345+
if self._current_app_config and self.app_name == UNKNOWN_APP:
346+
return {
347+
"APP_ID": self._current_app_config.APP_ID,
348+
"NAME_SPACE": self._current_app_config.NAME_SPACE,
349+
"MESSAGE": self._current_app_config.MESSAGE,
350+
}
351+
352+
return None
351353

352354
@property
353355
def app_name(self) -> Optional[str]:

requirements_all.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1732,7 +1732,7 @@ pyversasense==0.0.6
17321732
pyvesync==1.1.0
17331733

17341734
# homeassistant.components.vizio
1735-
pyvizio==0.1.35
1735+
pyvizio==0.1.44
17361736

17371737
# homeassistant.components.velux
17381738
pyvlx==0.2.12

requirements_test_all.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -635,7 +635,7 @@ pyvera==0.3.7
635635
pyvesync==1.1.0
636636

637637
# homeassistant.components.vizio
638-
pyvizio==0.1.35
638+
pyvizio==0.1.44
639639

640640
# homeassistant.components.html5
641641
pywebpush==1.9.2

tests/components/vizio/conftest.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
ACCESS_TOKEN,
88
APP_LIST,
99
CH_TYPE,
10-
CURRENT_APP,
10+
CURRENT_APP_CONFIG,
1111
CURRENT_INPUT,
1212
INPUT_LIST,
1313
INPUT_LIST_WITH_APPS,
@@ -172,7 +172,7 @@ def vizio_update_with_apps_fixture(vizio_update: pytest.fixture):
172172
"homeassistant.components.vizio.media_player.VizioAsync.get_current_input",
173173
return_value="CAST",
174174
), patch(
175-
"homeassistant.components.vizio.media_player.VizioAsync.get_current_app",
176-
return_value=CURRENT_APP,
175+
"homeassistant.components.vizio.media_player.VizioAsync.get_current_app_config",
176+
return_value=CURRENT_APP_CONFIG,
177177
):
178178
yield

tests/components/vizio/const.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,13 +68,19 @@ def __init__(self, auth_token: str) -> None:
6868
INPUT_LIST = ["HDMI", "USB", "Bluetooth", "AUX"]
6969

7070
CURRENT_APP = "Hulu"
71+
CURRENT_APP_CONFIG = {CONF_APP_ID: "3", CONF_NAME_SPACE: 4, CONF_MESSAGE: None}
7172
APP_LIST = ["Hulu", "Netflix"]
7273
INPUT_LIST_WITH_APPS = INPUT_LIST + ["CAST"]
7374
CUSTOM_CONFIG = {CONF_APP_ID: "test", CONF_MESSAGE: None, CONF_NAME_SPACE: 10}
7475
ADDITIONAL_APP_CONFIG = {
7576
"name": CURRENT_APP,
7677
CONF_CONFIG: CUSTOM_CONFIG,
7778
}
79+
UNKNOWN_APP_CONFIG = {
80+
"APP_ID": "UNKNOWN",
81+
"NAME_SPACE": 10,
82+
"MESSAGE": None,
83+
}
7884

7985
ENTITY_ID = f"{MP_DOMAIN}.{slugify(NAME)}"
8086

tests/components/vizio/test_media_player.py

Lines changed: 66 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
"""Tests for Vizio config flow."""
22
from datetime import timedelta
33
import logging
4-
from typing import Any, Dict
4+
from typing import Any, Dict, Optional
55
from unittest.mock import call
66

77
from asynctest import patch
88
import pytest
99
from pytest import raises
10-
from pyvizio._api.apps import AppConfig
10+
from pyvizio.api.apps import AppConfig
1111
from pyvizio.const import (
1212
DEVICE_CLASS_SPEAKER as VIZIO_DEVICE_CLASS_SPEAKER,
1313
DEVICE_CLASS_TV as VIZIO_DEVICE_CLASS_TV,
@@ -57,6 +57,7 @@
5757
ADDITIONAL_APP_CONFIG,
5858
APP_LIST,
5959
CURRENT_APP,
60+
CURRENT_APP_CONFIG,
6061
CURRENT_INPUT,
6162
CUSTOM_CONFIG,
6263
ENTITY_ID,
@@ -71,6 +72,7 @@
7172
MOCK_USER_VALID_TV_CONFIG,
7273
NAME,
7374
UNIQUE_ID,
75+
UNKNOWN_APP_CONFIG,
7476
VOLUME_STEP,
7577
)
7678

@@ -80,7 +82,7 @@
8082

8183

8284
async def _test_setup(
83-
hass: HomeAssistantType, ha_device_class: str, vizio_power_state: bool
85+
hass: HomeAssistantType, ha_device_class: str, vizio_power_state: Optional[bool]
8486
) -> None:
8587
"""Test Vizio Device entity setup."""
8688
if vizio_power_state:
@@ -112,7 +114,7 @@ async def _test_setup(
112114
"homeassistant.components.vizio.media_player.VizioAsync.get_power_state",
113115
return_value=vizio_power_state,
114116
), patch(
115-
"homeassistant.components.vizio.media_player.VizioAsync.get_current_app",
117+
"homeassistant.components.vizio.media_player.VizioAsync.get_current_app_config",
116118
) as service_call:
117119
config_entry.add_to_hass(hass)
118120
assert await hass.config_entries.async_setup(config_entry.entry_id)
@@ -136,7 +138,10 @@ async def _test_setup(
136138

137139

138140
async def _test_setup_with_apps(
139-
hass: HomeAssistantType, device_config: Dict[str, Any], app: str
141+
hass: HomeAssistantType,
142+
device_config: Dict[str, Any],
143+
app: Optional[str],
144+
app_config: Dict[str, Any],
140145
) -> None:
141146
"""Test Vizio Device with apps entity setup."""
142147
config_entry = MockConfigEntry(
@@ -152,12 +157,9 @@ async def _test_setup_with_apps(
152157
), patch(
153158
"homeassistant.components.vizio.media_player.VizioAsync.get_power_state",
154159
return_value=True,
155-
), patch(
156-
"homeassistant.components.vizio.media_player.VizioAsync.get_current_app",
157-
return_value=app,
158160
), patch(
159161
"homeassistant.components.vizio.media_player.VizioAsync.get_current_app_config",
160-
return_value=AppConfig(**ADDITIONAL_APP_CONFIG["config"]),
162+
return_value=AppConfig(**app_config),
161163
):
162164
config_entry.add_to_hass(hass)
163165
assert await hass.config_entries.async_setup(config_entry.entry_id)
@@ -193,11 +195,20 @@ async def _test_setup_with_apps(
193195
list_to_test.remove(app_to_remove)
194196

195197
assert attr["source_list"] == list_to_test
196-
assert app in attr["source_list"] or app == UNKNOWN_APP
197-
if app == UNKNOWN_APP:
198-
assert attr["source"] == ADDITIONAL_APP_CONFIG["name"]
199-
else:
198+
199+
if app:
200+
assert app in attr["source_list"] or app == UNKNOWN_APP
200201
assert attr["source"] == app
202+
assert attr["app_name"] == app
203+
if app == UNKNOWN_APP:
204+
assert attr["app_id"] == app_config
205+
else:
206+
assert "app_id" not in attr
207+
else:
208+
assert attr["source"] == "CAST"
209+
assert "app_id" not in attr
210+
assert "app_name" not in attr
211+
201212
assert (
202213
attr["volume_level"]
203214
== float(int(MAX_VOLUME[VIZIO_DEVICE_CLASS_TV] / 2))
@@ -222,7 +233,7 @@ async def _test_service(
222233
hass: HomeAssistantType,
223234
vizio_func_name: str,
224235
ha_service_name: str,
225-
additional_service_data: dict,
236+
additional_service_data: Optional[Dict[str, Any]],
226237
*args,
227238
**kwargs,
228239
) -> None:
@@ -363,8 +374,8 @@ async def test_options_update(
363374

364375
async def _test_update_availability_switch(
365376
hass: HomeAssistantType,
366-
initial_power_state: bool,
367-
final_power_state: bool,
377+
initial_power_state: Optional[bool],
378+
final_power_state: Optional[bool],
368379
caplog: pytest.fixture,
369380
) -> None:
370381
now = dt_util.utcnow()
@@ -431,7 +442,9 @@ async def test_setup_with_apps(
431442
caplog: pytest.fixture,
432443
) -> None:
433444
"""Test device setup with apps."""
434-
await _test_setup_with_apps(hass, MOCK_USER_VALID_TV_CONFIG, CURRENT_APP)
445+
await _test_setup_with_apps(
446+
hass, MOCK_USER_VALID_TV_CONFIG, CURRENT_APP, CURRENT_APP_CONFIG
447+
)
435448
await _test_service(
436449
hass,
437450
"launch_app",
@@ -448,7 +461,9 @@ async def test_setup_with_apps_include(
448461
caplog: pytest.fixture,
449462
) -> None:
450463
"""Test device setup with apps and apps["include"] in config."""
451-
await _test_setup_with_apps(hass, MOCK_TV_WITH_INCLUDE_CONFIG, CURRENT_APP)
464+
await _test_setup_with_apps(
465+
hass, MOCK_TV_WITH_INCLUDE_CONFIG, CURRENT_APP, CURRENT_APP_CONFIG
466+
)
452467

453468

454469
async def test_setup_with_apps_exclude(
@@ -458,7 +473,9 @@ async def test_setup_with_apps_exclude(
458473
caplog: pytest.fixture,
459474
) -> None:
460475
"""Test device setup with apps and apps["exclude"] in config."""
461-
await _test_setup_with_apps(hass, MOCK_TV_WITH_EXCLUDE_CONFIG, CURRENT_APP)
476+
await _test_setup_with_apps(
477+
hass, MOCK_TV_WITH_EXCLUDE_CONFIG, CURRENT_APP, CURRENT_APP_CONFIG
478+
)
462479

463480

464481
async def test_setup_with_apps_additional_apps_config(
@@ -468,7 +485,12 @@ async def test_setup_with_apps_additional_apps_config(
468485
caplog: pytest.fixture,
469486
) -> None:
470487
"""Test device setup with apps and apps["additional_configs"] in config."""
471-
await _test_setup_with_apps(hass, MOCK_TV_WITH_ADDITIONAL_APPS_CONFIG, UNKNOWN_APP)
488+
await _test_setup_with_apps(
489+
hass,
490+
MOCK_TV_WITH_ADDITIONAL_APPS_CONFIG,
491+
ADDITIONAL_APP_CONFIG["name"],
492+
ADDITIONAL_APP_CONFIG["config"],
493+
)
472494

473495
await _test_service(
474496
hass,
@@ -508,3 +530,27 @@ def test_invalid_apps_config(hass: HomeAssistantType):
508530

509531
with raises(vol.Invalid):
510532
vol.Schema(vol.All(VIZIO_SCHEMA, validate_apps))(MOCK_SPEAKER_APPS_FAILURE)
533+
534+
535+
async def test_setup_with_unknown_app_config(
536+
hass: HomeAssistantType,
537+
vizio_connect: pytest.fixture,
538+
vizio_update_with_apps: pytest.fixture,
539+
caplog: pytest.fixture,
540+
) -> None:
541+
"""Test device setup with apps where app config returned is unknown."""
542+
await _test_setup_with_apps(
543+
hass, MOCK_USER_VALID_TV_CONFIG, UNKNOWN_APP, UNKNOWN_APP_CONFIG
544+
)
545+
546+
547+
async def test_setup_with_no_running_app(
548+
hass: HomeAssistantType,
549+
vizio_connect: pytest.fixture,
550+
vizio_update_with_apps: pytest.fixture,
551+
caplog: pytest.fixture,
552+
) -> None:
553+
"""Test device setup with apps where no app is running."""
554+
await _test_setup_with_apps(
555+
hass, MOCK_USER_VALID_TV_CONFIG, None, vars(AppConfig())
556+
)

0 commit comments

Comments
 (0)