"""Tests for Vizio config flow.""" from __future__ import annotations from contextlib import asynccontextmanager from datetime import timedelta from typing import Any from unittest.mock import call, patch from freezegun import freeze_time import pytest from pyvizio.api.apps import AppConfig from pyvizio.const import ( APPS, DEVICE_CLASS_SPEAKER as VIZIO_DEVICE_CLASS_SPEAKER, DEVICE_CLASS_TV as VIZIO_DEVICE_CLASS_TV, INPUT_APPS, MAX_VOLUME, UNKNOWN_APP, ) import voluptuous as vol from homeassistant.components.media_player import ( ATTR_INPUT_SOURCE, ATTR_INPUT_SOURCE_LIST, ATTR_MEDIA_VOLUME_LEVEL, ATTR_MEDIA_VOLUME_MUTED, ATTR_SOUND_MODE, DOMAIN as MP_DOMAIN, SERVICE_MEDIA_NEXT_TRACK, SERVICE_MEDIA_PREVIOUS_TRACK, SERVICE_SELECT_SOUND_MODE, SERVICE_SELECT_SOURCE, SERVICE_TURN_OFF, SERVICE_TURN_ON, SERVICE_VOLUME_DOWN, SERVICE_VOLUME_MUTE, SERVICE_VOLUME_SET, SERVICE_VOLUME_UP, MediaPlayerDeviceClass, ) from homeassistant.components.vizio import validate_apps from homeassistant.components.vizio.const import ( CONF_ADDITIONAL_CONFIGS, CONF_APPS, CONF_VOLUME_STEP, DEFAULT_VOLUME_STEP, DOMAIN, SERVICE_UPDATE_SETTING, VIZIO_SCHEMA, ) from homeassistant.const import ATTR_ENTITY_ID, STATE_OFF, STATE_ON, STATE_UNAVAILABLE from homeassistant.core import HomeAssistant from homeassistant.util import dt as dt_util from .const import ( ADDITIONAL_APP_CONFIG, APP_LIST, APP_NAME_LIST, CURRENT_APP, CURRENT_APP_CONFIG, CURRENT_EQ, CURRENT_INPUT, CUSTOM_CONFIG, ENTITY_ID, EQ_LIST, INPUT_LIST, INPUT_LIST_WITH_APPS, MOCK_SPEAKER_APPS_FAILURE, MOCK_SPEAKER_CONFIG, MOCK_TV_APPS_FAILURE, MOCK_TV_WITH_ADDITIONAL_APPS_CONFIG, MOCK_TV_WITH_EXCLUDE_CONFIG, MOCK_TV_WITH_INCLUDE_CONFIG, MOCK_USER_VALID_TV_CONFIG, NAME, UNIQUE_ID, UNKNOWN_APP_CONFIG, VOLUME_STEP, ) from tests.common import MockConfigEntry, async_fire_time_changed async def _add_config_entry_to_hass( hass: HomeAssistant, config_entry: MockConfigEntry ) -> None: config_entry.add_to_hass(hass) assert await hass.config_entries.async_setup(config_entry.entry_id) await hass.async_block_till_done() def _get_ha_power_state(vizio_power_state: bool | None) -> str: """Return HA power state given Vizio power state.""" if vizio_power_state: return STATE_ON if vizio_power_state is False: return STATE_OFF return STATE_UNAVAILABLE def _assert_sources_and_volume(attr: dict[str, Any], vizio_device_class: str) -> None: """Assert source list, source, and volume level based on attr dict and device class.""" assert attr[ATTR_INPUT_SOURCE_LIST] == INPUT_LIST assert attr[ATTR_INPUT_SOURCE] == CURRENT_INPUT assert ( attr["volume_level"] == float(int(MAX_VOLUME[vizio_device_class] / 2)) / MAX_VOLUME[vizio_device_class] ) def _get_attr_and_assert_base_attr( hass: HomeAssistant, device_class: str, power_state: str ) -> dict[str, Any]: """Return entity attributes after asserting name, device class, and power state.""" attr = hass.states.get(ENTITY_ID).attributes assert attr["friendly_name"] == NAME assert attr["device_class"] == device_class assert hass.states.get(ENTITY_ID).state == power_state return attr @asynccontextmanager async def _cm_for_test_setup_without_apps( all_settings: dict[str, Any], vizio_power_state: bool | None ) -> None: """Context manager to setup test for Vizio devices without including app specific patches.""" with patch( "homeassistant.components.vizio.media_player.VizioAsync.get_all_settings", return_value=all_settings, ), patch( "homeassistant.components.vizio.media_player.VizioAsync.get_setting_options", return_value=EQ_LIST, ), patch( "homeassistant.components.vizio.media_player.VizioAsync.get_power_state", return_value=vizio_power_state, ): yield async def _test_setup_tv(hass: HomeAssistant, vizio_power_state: bool | None) -> None: """Test Vizio TV entity setup.""" ha_power_state = _get_ha_power_state(vizio_power_state) config_entry = MockConfigEntry( domain=DOMAIN, data=vol.Schema(VIZIO_SCHEMA)(MOCK_USER_VALID_TV_CONFIG), unique_id=UNIQUE_ID, ) async with _cm_for_test_setup_without_apps( {"volume": int(MAX_VOLUME[VIZIO_DEVICE_CLASS_TV] / 2), "mute": "Off"}, vizio_power_state, ): await _add_config_entry_to_hass(hass, config_entry) attr = _get_attr_and_assert_base_attr( hass, MediaPlayerDeviceClass.TV, ha_power_state ) if ha_power_state == STATE_ON: _assert_sources_and_volume(attr, VIZIO_DEVICE_CLASS_TV) assert "sound_mode" not in attr async def _test_setup_speaker( hass: HomeAssistant, vizio_power_state: bool | None ) -> None: """Test Vizio Speaker entity setup.""" ha_power_state = _get_ha_power_state(vizio_power_state) config_entry = MockConfigEntry( domain=DOMAIN, data=vol.Schema(VIZIO_SCHEMA)(MOCK_SPEAKER_CONFIG), unique_id=UNIQUE_ID, ) audio_settings = { "volume": int(MAX_VOLUME[VIZIO_DEVICE_CLASS_SPEAKER] / 2), "mute": "Off", "eq": CURRENT_EQ, } async with _cm_for_test_setup_without_apps( audio_settings, vizio_power_state, ): with patch( "homeassistant.components.vizio.media_player.VizioAsync.get_current_app_config", ) as service_call: await _add_config_entry_to_hass(hass, config_entry) attr = _get_attr_and_assert_base_attr( hass, MediaPlayerDeviceClass.SPEAKER, ha_power_state ) if ha_power_state == STATE_ON: _assert_sources_and_volume(attr, VIZIO_DEVICE_CLASS_SPEAKER) assert not service_call.called assert "sound_mode" in attr @asynccontextmanager async def _cm_for_test_setup_tv_with_apps( hass: HomeAssistant, device_config: dict[str, Any], app_config: dict[str, Any] ) -> None: """Context manager to setup test for Vizio TV with support for apps.""" config_entry = MockConfigEntry( domain=DOMAIN, data=vol.Schema(VIZIO_SCHEMA)(device_config), unique_id=UNIQUE_ID ) async with _cm_for_test_setup_without_apps( {"volume": int(MAX_VOLUME[VIZIO_DEVICE_CLASS_TV] / 2), "mute": "Off"}, True, ): with patch( "homeassistant.components.vizio.media_player.VizioAsync.get_current_app_config", return_value=AppConfig(**app_config), ): await _add_config_entry_to_hass(hass, config_entry) attr = _get_attr_and_assert_base_attr( hass, MediaPlayerDeviceClass.TV, STATE_ON ) assert ( attr["volume_level"] == float(int(MAX_VOLUME[VIZIO_DEVICE_CLASS_TV] / 2)) / MAX_VOLUME[VIZIO_DEVICE_CLASS_TV] ) yield def _assert_source_list_with_apps( list_to_test: list[str], attr: dict[str, Any] ) -> None: """Assert source list matches list_to_test after removing INPUT_APPS from list.""" for app_to_remove in INPUT_APPS: if app_to_remove in list_to_test: list_to_test.remove(app_to_remove) assert attr[ATTR_INPUT_SOURCE_LIST] == list_to_test async def _test_service( hass: HomeAssistant, domain: str, vizio_func_name: str, ha_service_name: str, additional_service_data: dict[str, Any] | None, *args, **kwargs, ) -> None: """Test generic Vizio media player entity service.""" kwargs["log_api_exception"] = False service_data = {ATTR_ENTITY_ID: ENTITY_ID} if additional_service_data: service_data.update(additional_service_data) with patch( f"homeassistant.components.vizio.media_player.VizioAsync.{vizio_func_name}" ) as service_call: await hass.services.async_call( domain, ha_service_name, service_data=service_data, blocking=True, ) assert service_call.called if args or kwargs: assert service_call.call_args == call(*args, **kwargs) async def test_speaker_on( hass: HomeAssistant, vizio_connect: pytest.fixture, vizio_update: pytest.fixture, ) -> None: """Test Vizio Speaker entity setup when on.""" await _test_setup_speaker(hass, True) async def test_speaker_off( hass: HomeAssistant, vizio_connect: pytest.fixture, vizio_update: pytest.fixture, ) -> None: """Test Vizio Speaker entity setup when off.""" await _test_setup_speaker(hass, False) async def test_speaker_unavailable( hass: HomeAssistant, vizio_connect: pytest.fixture, vizio_update: pytest.fixture, ) -> None: """Test Vizio Speaker entity setup when unavailable.""" await _test_setup_speaker(hass, None) async def test_init_tv_on( hass: HomeAssistant, vizio_connect: pytest.fixture, vizio_update: pytest.fixture, ) -> None: """Test Vizio TV entity setup when on.""" await _test_setup_tv(hass, True) async def test_init_tv_off( hass: HomeAssistant, vizio_connect: pytest.fixture, vizio_update: pytest.fixture, ) -> None: """Test Vizio TV entity setup when off.""" await _test_setup_tv(hass, False) async def test_init_tv_unavailable( hass: HomeAssistant, vizio_connect: pytest.fixture, vizio_update: pytest.fixture, ) -> None: """Test Vizio TV entity setup when unavailable.""" await _test_setup_tv(hass, None) async def test_setup_unavailable_speaker( hass: HomeAssistant, vizio_cant_connect: pytest.fixture ) -> None: """Test speaker entity sets up as unavailable.""" config_entry = MockConfigEntry( domain=DOMAIN, data=MOCK_SPEAKER_CONFIG, unique_id=UNIQUE_ID ) await _add_config_entry_to_hass(hass, config_entry) assert len(hass.states.async_entity_ids(MP_DOMAIN)) == 1 assert hass.states.get("media_player.vizio").state == STATE_UNAVAILABLE async def test_setup_unavailable_tv( hass: HomeAssistant, vizio_cant_connect: pytest.fixture ) -> None: """Test TV entity sets up as unavailable.""" config_entry = MockConfigEntry( domain=DOMAIN, data=MOCK_USER_VALID_TV_CONFIG, unique_id=UNIQUE_ID ) await _add_config_entry_to_hass(hass, config_entry) assert len(hass.states.async_entity_ids(MP_DOMAIN)) == 1 assert hass.states.get("media_player.vizio").state == STATE_UNAVAILABLE async def test_services( hass: HomeAssistant, vizio_connect: pytest.fixture, vizio_update: pytest.fixture, ) -> None: """Test all Vizio media player entity services.""" await _test_setup_tv(hass, True) await _test_service(hass, MP_DOMAIN, "pow_on", SERVICE_TURN_ON, None) await _test_service(hass, MP_DOMAIN, "pow_off", SERVICE_TURN_OFF, None) await _test_service( hass, MP_DOMAIN, "mute_on", SERVICE_VOLUME_MUTE, {ATTR_MEDIA_VOLUME_MUTED: True}, ) await _test_service( hass, MP_DOMAIN, "mute_off", SERVICE_VOLUME_MUTE, {ATTR_MEDIA_VOLUME_MUTED: False}, ) await _test_service( hass, MP_DOMAIN, "set_input", SERVICE_SELECT_SOURCE, {ATTR_INPUT_SOURCE: "USB"}, "USB", ) await _test_service( hass, MP_DOMAIN, "vol_up", SERVICE_VOLUME_UP, None, num=DEFAULT_VOLUME_STEP ) await _test_service( hass, MP_DOMAIN, "vol_down", SERVICE_VOLUME_DOWN, None, num=DEFAULT_VOLUME_STEP ) await _test_service( hass, MP_DOMAIN, "vol_up", SERVICE_VOLUME_SET, {ATTR_MEDIA_VOLUME_LEVEL: 1}, num=(100 - 15), ) await _test_service( hass, MP_DOMAIN, "vol_down", SERVICE_VOLUME_SET, {ATTR_MEDIA_VOLUME_LEVEL: 0}, num=(15 - 0), ) await _test_service(hass, MP_DOMAIN, "ch_up", SERVICE_MEDIA_NEXT_TRACK, None) await _test_service(hass, MP_DOMAIN, "ch_down", SERVICE_MEDIA_PREVIOUS_TRACK, None) await _test_service( hass, MP_DOMAIN, "set_setting", SERVICE_SELECT_SOUND_MODE, {ATTR_SOUND_MODE: "Music"}, "audio", "eq", "Music", ) # Test that the update_setting service does config validation/transformation correctly await _test_service( hass, DOMAIN, "set_setting", SERVICE_UPDATE_SETTING, {"setting_type": "Audio", "setting_name": "AV Delay", "new_value": "0"}, "audio", "av_delay", 0, ) await _test_service( hass, DOMAIN, "set_setting", SERVICE_UPDATE_SETTING, {"setting_type": "Audio", "setting_name": "EQ", "new_value": "Music"}, "audio", "eq", "Music", ) async def test_options_update( hass: HomeAssistant, vizio_connect: pytest.fixture, vizio_update: pytest.fixture, ) -> None: """Test when config entry update event fires.""" await _test_setup_speaker(hass, True) config_entry = hass.config_entries.async_entries(DOMAIN)[0] assert config_entry.options new_options = config_entry.options.copy() updated_options = {CONF_VOLUME_STEP: VOLUME_STEP} new_options.update(updated_options) hass.config_entries.async_update_entry( entry=config_entry, options=new_options, ) assert config_entry.options == updated_options await hass.async_block_till_done() await _test_service( hass, MP_DOMAIN, "vol_up", SERVICE_VOLUME_UP, None, num=VOLUME_STEP ) async def _test_update_availability_switch( hass: HomeAssistant, initial_power_state: bool | None, final_power_state: bool | None, caplog: pytest.fixture, ) -> None: now = dt_util.utcnow() future_interval = timedelta(minutes=1) # Setup device as if time is right now with freeze_time(now): await _test_setup_speaker(hass, initial_power_state) # Clear captured logs so that only availability state changes are captured for # future assertion caplog.clear() # Fast forward time to future twice to trigger update and assert vizio log message for i in range(1, 3): future = now + (future_interval * i) with patch( "homeassistant.components.vizio.media_player.VizioAsync.get_power_state", return_value=final_power_state, ), freeze_time(future): async_fire_time_changed(hass, future) await hass.async_block_till_done() if final_power_state is None: assert hass.states.get(ENTITY_ID).state == STATE_UNAVAILABLE else: assert hass.states.get(ENTITY_ID).state != STATE_UNAVAILABLE # Ensure connection status messages from vizio.media_player appear exactly once # (on availability state change) vizio_log_list = [ log for log in caplog.records if log.name == "homeassistant.components.vizio.media_player" ] assert len(vizio_log_list) == 1 async def test_update_unavailable_to_available( hass: HomeAssistant, vizio_connect: pytest.fixture, vizio_update: pytest.fixture, caplog: pytest.LogCaptureFixture, ) -> None: """Test device becomes available after being unavailable.""" await _test_update_availability_switch(hass, None, True, caplog) async def test_update_available_to_unavailable( hass: HomeAssistant, vizio_connect: pytest.fixture, vizio_update: pytest.fixture, caplog: pytest.LogCaptureFixture, ) -> None: """Test device becomes unavailable after being available.""" await _test_update_availability_switch(hass, True, None, caplog) async def test_setup_with_apps( hass: HomeAssistant, vizio_connect: pytest.fixture, vizio_update_with_apps: pytest.fixture, caplog: pytest.LogCaptureFixture, ) -> None: """Test device setup with apps.""" async with _cm_for_test_setup_tv_with_apps( hass, MOCK_USER_VALID_TV_CONFIG, CURRENT_APP_CONFIG ): attr = hass.states.get(ENTITY_ID).attributes _assert_source_list_with_apps(list(INPUT_LIST_WITH_APPS + APP_NAME_LIST), attr) assert CURRENT_APP in attr[ATTR_INPUT_SOURCE_LIST] assert attr[ATTR_INPUT_SOURCE] == CURRENT_APP assert attr["app_name"] == CURRENT_APP assert "app_id" not in attr await _test_service( hass, MP_DOMAIN, "launch_app", SERVICE_SELECT_SOURCE, {ATTR_INPUT_SOURCE: CURRENT_APP}, CURRENT_APP, APP_LIST, ) async def test_setup_with_apps_include( hass: HomeAssistant, vizio_connect: pytest.fixture, vizio_update_with_apps: pytest.fixture, caplog: pytest.LogCaptureFixture, ) -> None: """Test device setup with apps and apps["include"] in config.""" async with _cm_for_test_setup_tv_with_apps( hass, MOCK_TV_WITH_INCLUDE_CONFIG, CURRENT_APP_CONFIG ): attr = hass.states.get(ENTITY_ID).attributes _assert_source_list_with_apps(list(INPUT_LIST_WITH_APPS + [CURRENT_APP]), attr) assert CURRENT_APP in attr[ATTR_INPUT_SOURCE_LIST] assert attr[ATTR_INPUT_SOURCE] == CURRENT_APP assert attr["app_name"] == CURRENT_APP assert "app_id" not in attr async def test_setup_with_apps_exclude( hass: HomeAssistant, vizio_connect: pytest.fixture, vizio_update_with_apps: pytest.fixture, caplog: pytest.LogCaptureFixture, ) -> None: """Test device setup with apps and apps["exclude"] in config.""" async with _cm_for_test_setup_tv_with_apps( hass, MOCK_TV_WITH_EXCLUDE_CONFIG, CURRENT_APP_CONFIG ): attr = hass.states.get(ENTITY_ID).attributes _assert_source_list_with_apps(list(INPUT_LIST_WITH_APPS + [CURRENT_APP]), attr) assert CURRENT_APP in attr[ATTR_INPUT_SOURCE_LIST] assert attr[ATTR_INPUT_SOURCE] == CURRENT_APP assert attr["app_name"] == CURRENT_APP assert "app_id" not in attr async def test_setup_with_apps_additional_apps_config( hass: HomeAssistant, vizio_connect: pytest.fixture, vizio_update_with_apps: pytest.fixture, caplog: pytest.LogCaptureFixture, ) -> None: """Test device setup with apps and apps["additional_configs"] in config.""" async with _cm_for_test_setup_tv_with_apps( hass, MOCK_TV_WITH_ADDITIONAL_APPS_CONFIG, ADDITIONAL_APP_CONFIG["config"], ): attr = hass.states.get(ENTITY_ID).attributes assert attr[ATTR_INPUT_SOURCE_LIST].count(CURRENT_APP) == 1 _assert_source_list_with_apps( list( INPUT_LIST_WITH_APPS + APP_NAME_LIST + [ app["name"] for app in MOCK_TV_WITH_ADDITIONAL_APPS_CONFIG[CONF_APPS][ CONF_ADDITIONAL_CONFIGS ] if app["name"] not in APP_NAME_LIST ] ), attr, ) assert ADDITIONAL_APP_CONFIG["name"] in attr[ATTR_INPUT_SOURCE_LIST] assert attr[ATTR_INPUT_SOURCE] == ADDITIONAL_APP_CONFIG["name"] assert attr["app_name"] == ADDITIONAL_APP_CONFIG["name"] assert "app_id" not in attr await _test_service( hass, MP_DOMAIN, "launch_app", SERVICE_SELECT_SOURCE, {ATTR_INPUT_SOURCE: "Netflix"}, "Netflix", APP_LIST, ) await _test_service( hass, MP_DOMAIN, "launch_app_config", SERVICE_SELECT_SOURCE, {ATTR_INPUT_SOURCE: CURRENT_APP}, **CUSTOM_CONFIG, ) # Test that invalid app does nothing with patch( "homeassistant.components.vizio.media_player.VizioAsync.launch_app" ) as service_call1, patch( "homeassistant.components.vizio.media_player.VizioAsync.launch_app_config" ) as service_call2: await hass.services.async_call( MP_DOMAIN, SERVICE_SELECT_SOURCE, service_data={ATTR_ENTITY_ID: ENTITY_ID, ATTR_INPUT_SOURCE: "_"}, blocking=True, ) assert not service_call1.called assert not service_call2.called def test_invalid_apps_config(hass: HomeAssistant) -> None: """Test that schema validation fails on certain conditions.""" with pytest.raises(vol.Invalid): vol.Schema(vol.All(VIZIO_SCHEMA, validate_apps))(MOCK_TV_APPS_FAILURE) with pytest.raises(vol.Invalid): vol.Schema(vol.All(VIZIO_SCHEMA, validate_apps))(MOCK_SPEAKER_APPS_FAILURE) async def test_setup_with_unknown_app_config( hass: HomeAssistant, vizio_connect: pytest.fixture, vizio_update_with_apps: pytest.fixture, caplog: pytest.LogCaptureFixture, ) -> None: """Test device setup with apps where app config returned is unknown.""" async with _cm_for_test_setup_tv_with_apps( hass, MOCK_USER_VALID_TV_CONFIG, UNKNOWN_APP_CONFIG ): attr = hass.states.get(ENTITY_ID).attributes _assert_source_list_with_apps(list(INPUT_LIST_WITH_APPS + APP_NAME_LIST), attr) assert attr[ATTR_INPUT_SOURCE] == UNKNOWN_APP assert attr["app_name"] == UNKNOWN_APP assert attr["app_id"] == UNKNOWN_APP_CONFIG async def test_setup_with_no_running_app( hass: HomeAssistant, vizio_connect: pytest.fixture, vizio_update_with_apps: pytest.fixture, caplog: pytest.LogCaptureFixture, ) -> None: """Test device setup with apps where no app is running.""" async with _cm_for_test_setup_tv_with_apps( hass, MOCK_USER_VALID_TV_CONFIG, vars(AppConfig()) ): attr = hass.states.get(ENTITY_ID).attributes _assert_source_list_with_apps(list(INPUT_LIST_WITH_APPS + APP_NAME_LIST), attr) assert attr[ATTR_INPUT_SOURCE] == "CAST" assert "app_id" not in attr assert "app_name" not in attr async def test_setup_tv_without_mute( hass: HomeAssistant, vizio_connect: pytest.fixture, vizio_update: pytest.fixture, ) -> None: """Test Vizio TV entity setup when mute property isn't returned by Vizio API.""" config_entry = MockConfigEntry( domain=DOMAIN, data=vol.Schema(VIZIO_SCHEMA)(MOCK_USER_VALID_TV_CONFIG), unique_id=UNIQUE_ID, ) async with _cm_for_test_setup_without_apps( {"volume": int(MAX_VOLUME[VIZIO_DEVICE_CLASS_TV] / 2)}, STATE_ON, ): await _add_config_entry_to_hass(hass, config_entry) attr = _get_attr_and_assert_base_attr(hass, MediaPlayerDeviceClass.TV, STATE_ON) _assert_sources_and_volume(attr, VIZIO_DEVICE_CLASS_TV) assert "sound_mode" not in attr assert "is_volume_muted" not in attr async def test_apps_update( hass: HomeAssistant, vizio_connect: pytest.fixture, vizio_update_with_apps: pytest.fixture, caplog: pytest.LogCaptureFixture, ) -> None: """Test device setup with apps where no app is running.""" with patch( "homeassistant.components.vizio.gen_apps_list_from_url", return_value=None, ): async with _cm_for_test_setup_tv_with_apps( hass, MOCK_USER_VALID_TV_CONFIG, vars(AppConfig()) ): # Check source list, remove TV inputs, and verify that the integration is # using the default APPS list sources = hass.states.get(ENTITY_ID).attributes[ATTR_INPUT_SOURCE_LIST] apps = list(set(sources) - set(INPUT_LIST)) assert len(apps) == len(APPS) with patch( "homeassistant.components.vizio.gen_apps_list_from_url", return_value=APP_LIST, ): async_fire_time_changed(hass, dt_util.now() + timedelta(days=2)) await hass.async_block_till_done() async_fire_time_changed(hass, dt_util.now() + timedelta(days=2)) await hass.async_block_till_done() # Check source list, remove TV inputs, and verify that the integration is # now using the APP_LIST list sources = hass.states.get(ENTITY_ID).attributes[ATTR_INPUT_SOURCE_LIST] apps = list(set(sources) - set(INPUT_LIST)) assert len(apps) == len(APP_LIST) async def test_vizio_update_with_apps_on_input( hass: HomeAssistant, vizio_connect, vizio_update_with_apps_on_input ) -> None: """Test a vizio TV with apps that is on a TV input.""" config_entry = MockConfigEntry( domain=DOMAIN, data=vol.Schema(VIZIO_SCHEMA)(MOCK_USER_VALID_TV_CONFIG), unique_id=UNIQUE_ID, ) await _add_config_entry_to_hass(hass, config_entry) attr = _get_attr_and_assert_base_attr(hass, MediaPlayerDeviceClass.TV, STATE_ON) # app ID should not be in the attributes assert "app_id" not in attr