mirror of
https://github.com/home-assistant/core.git
synced 2025-07-19 11:17:21 +00:00
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:
parent
4a0a56ebdc
commit
d5f4dfdd6b
@ -2,7 +2,7 @@
|
|||||||
"domain": "vizio",
|
"domain": "vizio",
|
||||||
"name": "Vizio SmartCast",
|
"name": "Vizio SmartCast",
|
||||||
"documentation": "https://www.home-assistant.io/integrations/vizio",
|
"documentation": "https://www.home-assistant.io/integrations/vizio",
|
||||||
"requirements": ["pyvizio==0.1.35"],
|
"requirements": ["pyvizio==0.1.44"],
|
||||||
"dependencies": [],
|
"dependencies": [],
|
||||||
"codeowners": ["@raman325"],
|
"codeowners": ["@raman325"],
|
||||||
"config_flow": true,
|
"config_flow": true,
|
||||||
|
@ -4,8 +4,8 @@ import logging
|
|||||||
from typing import Any, Callable, Dict, List, Optional
|
from typing import Any, Callable, Dict, List, Optional
|
||||||
|
|
||||||
from pyvizio import VizioAsync
|
from pyvizio import VizioAsync
|
||||||
from pyvizio.const import INPUT_APPS, NO_APP_RUNNING, UNKNOWN_APP
|
from pyvizio.api.apps import find_app_name
|
||||||
from pyvizio.helpers import find_app_name
|
from pyvizio.const import APP_HOME, APPS, INPUT_APPS, NO_APP_RUNNING, UNKNOWN_APP
|
||||||
|
|
||||||
from homeassistant.components.media_player import (
|
from homeassistant.components.media_player import (
|
||||||
DEVICE_CLASS_SPEAKER,
|
DEVICE_CLASS_SPEAKER,
|
||||||
@ -132,6 +132,7 @@ class VizioDevice(MediaPlayerDevice):
|
|||||||
self._is_muted = None
|
self._is_muted = None
|
||||||
self._current_input = None
|
self._current_input = None
|
||||||
self._current_app = None
|
self._current_app = None
|
||||||
|
self._current_app_config = None
|
||||||
self._available_inputs = []
|
self._available_inputs = []
|
||||||
self._available_apps = []
|
self._available_apps = []
|
||||||
self._conf_apps = config_entry.options.get(CONF_APPS, {})
|
self._conf_apps = config_entry.options.get(CONF_APPS, {})
|
||||||
@ -157,20 +158,6 @@ class VizioDevice(MediaPlayerDevice):
|
|||||||
|
|
||||||
return apps
|
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:
|
async def async_update(self) -> None:
|
||||||
"""Retrieve latest state of the device."""
|
"""Retrieve latest state of the device."""
|
||||||
if not self._model:
|
if not self._model:
|
||||||
@ -202,6 +189,7 @@ class VizioDevice(MediaPlayerDevice):
|
|||||||
self._current_input = None
|
self._current_input = None
|
||||||
self._available_inputs = None
|
self._available_inputs = None
|
||||||
self._current_app = None
|
self._current_app = None
|
||||||
|
self._current_app_config = None
|
||||||
self._available_apps = None
|
self._available_apps = None
|
||||||
return
|
return
|
||||||
|
|
||||||
@ -237,9 +225,16 @@ class VizioDevice(MediaPlayerDevice):
|
|||||||
if not self._available_apps:
|
if not self._available_apps:
|
||||||
self._available_apps = self._apps_list(self._device.get_apps_list())
|
self._available_apps = self._apps_list(self._device.get_apps_list())
|
||||||
|
|
||||||
# Attempt to get current app name. If app name is unknown, check list
|
self._current_app_config = await self._device.get_current_app_config(
|
||||||
# of additional apps specified in configuration
|
log_api_exception=False
|
||||||
self._current_app = await self._current_app_name()
|
)
|
||||||
|
|
||||||
|
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]]:
|
def _get_additional_app_names(self) -> List[Dict[str, Any]]:
|
||||||
"""Return list of additional apps that were included in configuration.yaml."""
|
"""Return list of additional apps that were included in configuration.yaml."""
|
||||||
@ -346,8 +341,15 @@ class VizioDevice(MediaPlayerDevice):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def app_id(self) -> Optional[str]:
|
def app_id(self) -> Optional[str]:
|
||||||
"""Return the current app."""
|
"""Return the ID of the current app if it is unknown by pyvizio."""
|
||||||
return self._current_app
|
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
|
@property
|
||||||
def app_name(self) -> Optional[str]:
|
def app_name(self) -> Optional[str]:
|
||||||
|
@ -1732,7 +1732,7 @@ pyversasense==0.0.6
|
|||||||
pyvesync==1.1.0
|
pyvesync==1.1.0
|
||||||
|
|
||||||
# homeassistant.components.vizio
|
# homeassistant.components.vizio
|
||||||
pyvizio==0.1.35
|
pyvizio==0.1.44
|
||||||
|
|
||||||
# homeassistant.components.velux
|
# homeassistant.components.velux
|
||||||
pyvlx==0.2.12
|
pyvlx==0.2.12
|
||||||
|
@ -635,7 +635,7 @@ pyvera==0.3.7
|
|||||||
pyvesync==1.1.0
|
pyvesync==1.1.0
|
||||||
|
|
||||||
# homeassistant.components.vizio
|
# homeassistant.components.vizio
|
||||||
pyvizio==0.1.35
|
pyvizio==0.1.44
|
||||||
|
|
||||||
# homeassistant.components.html5
|
# homeassistant.components.html5
|
||||||
pywebpush==1.9.2
|
pywebpush==1.9.2
|
||||||
|
@ -7,7 +7,7 @@ from .const import (
|
|||||||
ACCESS_TOKEN,
|
ACCESS_TOKEN,
|
||||||
APP_LIST,
|
APP_LIST,
|
||||||
CH_TYPE,
|
CH_TYPE,
|
||||||
CURRENT_APP,
|
CURRENT_APP_CONFIG,
|
||||||
CURRENT_INPUT,
|
CURRENT_INPUT,
|
||||||
INPUT_LIST,
|
INPUT_LIST,
|
||||||
INPUT_LIST_WITH_APPS,
|
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",
|
"homeassistant.components.vizio.media_player.VizioAsync.get_current_input",
|
||||||
return_value="CAST",
|
return_value="CAST",
|
||||||
), patch(
|
), patch(
|
||||||
"homeassistant.components.vizio.media_player.VizioAsync.get_current_app",
|
"homeassistant.components.vizio.media_player.VizioAsync.get_current_app_config",
|
||||||
return_value=CURRENT_APP,
|
return_value=CURRENT_APP_CONFIG,
|
||||||
):
|
):
|
||||||
yield
|
yield
|
||||||
|
@ -68,6 +68,7 @@ CURRENT_INPUT = "HDMI"
|
|||||||
INPUT_LIST = ["HDMI", "USB", "Bluetooth", "AUX"]
|
INPUT_LIST = ["HDMI", "USB", "Bluetooth", "AUX"]
|
||||||
|
|
||||||
CURRENT_APP = "Hulu"
|
CURRENT_APP = "Hulu"
|
||||||
|
CURRENT_APP_CONFIG = {CONF_APP_ID: "3", CONF_NAME_SPACE: 4, CONF_MESSAGE: None}
|
||||||
APP_LIST = ["Hulu", "Netflix"]
|
APP_LIST = ["Hulu", "Netflix"]
|
||||||
INPUT_LIST_WITH_APPS = INPUT_LIST + ["CAST"]
|
INPUT_LIST_WITH_APPS = INPUT_LIST + ["CAST"]
|
||||||
CUSTOM_CONFIG = {CONF_APP_ID: "test", CONF_MESSAGE: None, CONF_NAME_SPACE: 10}
|
CUSTOM_CONFIG = {CONF_APP_ID: "test", CONF_MESSAGE: None, CONF_NAME_SPACE: 10}
|
||||||
@ -75,6 +76,11 @@ ADDITIONAL_APP_CONFIG = {
|
|||||||
"name": CURRENT_APP,
|
"name": CURRENT_APP,
|
||||||
CONF_CONFIG: CUSTOM_CONFIG,
|
CONF_CONFIG: CUSTOM_CONFIG,
|
||||||
}
|
}
|
||||||
|
UNKNOWN_APP_CONFIG = {
|
||||||
|
"APP_ID": "UNKNOWN",
|
||||||
|
"NAME_SPACE": 10,
|
||||||
|
"MESSAGE": None,
|
||||||
|
}
|
||||||
|
|
||||||
ENTITY_ID = f"{MP_DOMAIN}.{slugify(NAME)}"
|
ENTITY_ID = f"{MP_DOMAIN}.{slugify(NAME)}"
|
||||||
|
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
"""Tests for Vizio config flow."""
|
"""Tests for Vizio config flow."""
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
import logging
|
import logging
|
||||||
from typing import Any, Dict
|
from typing import Any, Dict, Optional
|
||||||
from unittest.mock import call
|
from unittest.mock import call
|
||||||
|
|
||||||
from asynctest import patch
|
from asynctest import patch
|
||||||
import pytest
|
import pytest
|
||||||
from pytest import raises
|
from pytest import raises
|
||||||
from pyvizio._api.apps import AppConfig
|
from pyvizio.api.apps import AppConfig
|
||||||
from pyvizio.const import (
|
from pyvizio.const import (
|
||||||
DEVICE_CLASS_SPEAKER as VIZIO_DEVICE_CLASS_SPEAKER,
|
DEVICE_CLASS_SPEAKER as VIZIO_DEVICE_CLASS_SPEAKER,
|
||||||
DEVICE_CLASS_TV as VIZIO_DEVICE_CLASS_TV,
|
DEVICE_CLASS_TV as VIZIO_DEVICE_CLASS_TV,
|
||||||
@ -57,6 +57,7 @@ from .const import (
|
|||||||
ADDITIONAL_APP_CONFIG,
|
ADDITIONAL_APP_CONFIG,
|
||||||
APP_LIST,
|
APP_LIST,
|
||||||
CURRENT_APP,
|
CURRENT_APP,
|
||||||
|
CURRENT_APP_CONFIG,
|
||||||
CURRENT_INPUT,
|
CURRENT_INPUT,
|
||||||
CUSTOM_CONFIG,
|
CUSTOM_CONFIG,
|
||||||
ENTITY_ID,
|
ENTITY_ID,
|
||||||
@ -71,6 +72,7 @@ from .const import (
|
|||||||
MOCK_USER_VALID_TV_CONFIG,
|
MOCK_USER_VALID_TV_CONFIG,
|
||||||
NAME,
|
NAME,
|
||||||
UNIQUE_ID,
|
UNIQUE_ID,
|
||||||
|
UNKNOWN_APP_CONFIG,
|
||||||
VOLUME_STEP,
|
VOLUME_STEP,
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -80,7 +82,7 @@ _LOGGER = logging.getLogger(__name__)
|
|||||||
|
|
||||||
|
|
||||||
async def _test_setup(
|
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:
|
) -> None:
|
||||||
"""Test Vizio Device entity setup."""
|
"""Test Vizio Device entity setup."""
|
||||||
if vizio_power_state:
|
if vizio_power_state:
|
||||||
@ -112,7 +114,7 @@ async def _test_setup(
|
|||||||
"homeassistant.components.vizio.media_player.VizioAsync.get_power_state",
|
"homeassistant.components.vizio.media_player.VizioAsync.get_power_state",
|
||||||
return_value=vizio_power_state,
|
return_value=vizio_power_state,
|
||||||
), patch(
|
), patch(
|
||||||
"homeassistant.components.vizio.media_player.VizioAsync.get_current_app",
|
"homeassistant.components.vizio.media_player.VizioAsync.get_current_app_config",
|
||||||
) as service_call:
|
) as service_call:
|
||||||
config_entry.add_to_hass(hass)
|
config_entry.add_to_hass(hass)
|
||||||
assert await hass.config_entries.async_setup(config_entry.entry_id)
|
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(
|
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:
|
) -> None:
|
||||||
"""Test Vizio Device with apps entity setup."""
|
"""Test Vizio Device with apps entity setup."""
|
||||||
config_entry = MockConfigEntry(
|
config_entry = MockConfigEntry(
|
||||||
@ -152,12 +157,9 @@ async def _test_setup_with_apps(
|
|||||||
), patch(
|
), patch(
|
||||||
"homeassistant.components.vizio.media_player.VizioAsync.get_power_state",
|
"homeassistant.components.vizio.media_player.VizioAsync.get_power_state",
|
||||||
return_value=True,
|
return_value=True,
|
||||||
), patch(
|
|
||||||
"homeassistant.components.vizio.media_player.VizioAsync.get_current_app",
|
|
||||||
return_value=app,
|
|
||||||
), patch(
|
), patch(
|
||||||
"homeassistant.components.vizio.media_player.VizioAsync.get_current_app_config",
|
"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)
|
config_entry.add_to_hass(hass)
|
||||||
assert await hass.config_entries.async_setup(config_entry.entry_id)
|
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)
|
list_to_test.remove(app_to_remove)
|
||||||
|
|
||||||
assert attr["source_list"] == list_to_test
|
assert attr["source_list"] == list_to_test
|
||||||
assert app in attr["source_list"] or app == UNKNOWN_APP
|
|
||||||
if app == UNKNOWN_APP:
|
if app:
|
||||||
assert attr["source"] == ADDITIONAL_APP_CONFIG["name"]
|
assert app in attr["source_list"] or app == UNKNOWN_APP
|
||||||
else:
|
|
||||||
assert attr["source"] == 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 (
|
assert (
|
||||||
attr["volume_level"]
|
attr["volume_level"]
|
||||||
== float(int(MAX_VOLUME[VIZIO_DEVICE_CLASS_TV] / 2))
|
== float(int(MAX_VOLUME[VIZIO_DEVICE_CLASS_TV] / 2))
|
||||||
@ -222,7 +233,7 @@ async def _test_service(
|
|||||||
hass: HomeAssistantType,
|
hass: HomeAssistantType,
|
||||||
vizio_func_name: str,
|
vizio_func_name: str,
|
||||||
ha_service_name: str,
|
ha_service_name: str,
|
||||||
additional_service_data: dict,
|
additional_service_data: Optional[Dict[str, Any]],
|
||||||
*args,
|
*args,
|
||||||
**kwargs,
|
**kwargs,
|
||||||
) -> None:
|
) -> None:
|
||||||
@ -363,8 +374,8 @@ async def test_options_update(
|
|||||||
|
|
||||||
async def _test_update_availability_switch(
|
async def _test_update_availability_switch(
|
||||||
hass: HomeAssistantType,
|
hass: HomeAssistantType,
|
||||||
initial_power_state: bool,
|
initial_power_state: Optional[bool],
|
||||||
final_power_state: bool,
|
final_power_state: Optional[bool],
|
||||||
caplog: pytest.fixture,
|
caplog: pytest.fixture,
|
||||||
) -> None:
|
) -> None:
|
||||||
now = dt_util.utcnow()
|
now = dt_util.utcnow()
|
||||||
@ -431,7 +442,9 @@ async def test_setup_with_apps(
|
|||||||
caplog: pytest.fixture,
|
caplog: pytest.fixture,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test device setup with apps."""
|
"""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(
|
await _test_service(
|
||||||
hass,
|
hass,
|
||||||
"launch_app",
|
"launch_app",
|
||||||
@ -448,7 +461,9 @@ async def test_setup_with_apps_include(
|
|||||||
caplog: pytest.fixture,
|
caplog: pytest.fixture,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test device setup with apps and apps["include"] in config."""
|
"""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(
|
async def test_setup_with_apps_exclude(
|
||||||
@ -458,7 +473,9 @@ async def test_setup_with_apps_exclude(
|
|||||||
caplog: pytest.fixture,
|
caplog: pytest.fixture,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test device setup with apps and apps["exclude"] in config."""
|
"""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(
|
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,
|
caplog: pytest.fixture,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test device setup with apps and apps["additional_configs"] in config."""
|
"""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(
|
await _test_service(
|
||||||
hass,
|
hass,
|
||||||
@ -508,3 +530,27 @@ def test_invalid_apps_config(hass: HomeAssistantType):
|
|||||||
|
|
||||||
with raises(vol.Invalid):
|
with raises(vol.Invalid):
|
||||||
vol.Schema(vol.All(VIZIO_SCHEMA, validate_apps))(MOCK_SPEAKER_APPS_FAILURE)
|
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())
|
||||||
|
)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user