mirror of
https://github.com/home-assistant/core.git
synced 2025-04-23 16:57:53 +00:00
Remove YAML support from vizio (#132351)
This commit is contained in:
parent
1ec91e7c89
commit
24b1eeb900
@ -4,55 +4,18 @@ from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.media_player import MediaPlayerDeviceClass
|
||||
from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry, ConfigEntryState
|
||||
from homeassistant.const import Platform
|
||||
from homeassistant.config_entries import ConfigEntry, ConfigEntryState
|
||||
from homeassistant.const import CONF_DEVICE_CLASS, Platform
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import config_validation as cv
|
||||
from homeassistant.helpers.storage import Store
|
||||
from homeassistant.helpers.typing import ConfigType
|
||||
|
||||
from .const import CONF_APPS, CONF_DEVICE_CLASS, DOMAIN, VIZIO_SCHEMA
|
||||
from .const import CONF_APPS, DOMAIN
|
||||
from .coordinator import VizioAppsDataUpdateCoordinator
|
||||
|
||||
|
||||
def validate_apps(config: ConfigType) -> ConfigType:
|
||||
"""Validate CONF_APPS is only used when CONF_DEVICE_CLASS is MediaPlayerDeviceClass.TV."""
|
||||
if (
|
||||
config.get(CONF_APPS) is not None
|
||||
and config[CONF_DEVICE_CLASS] != MediaPlayerDeviceClass.TV
|
||||
):
|
||||
raise vol.Invalid(
|
||||
f"'{CONF_APPS}' can only be used if {CONF_DEVICE_CLASS}' is"
|
||||
f" '{MediaPlayerDeviceClass.TV}'"
|
||||
)
|
||||
|
||||
return config
|
||||
|
||||
|
||||
CONFIG_SCHEMA = vol.Schema(
|
||||
{DOMAIN: vol.All(cv.ensure_list, [vol.All(VIZIO_SCHEMA, validate_apps)])},
|
||||
extra=vol.ALLOW_EXTRA,
|
||||
)
|
||||
|
||||
PLATFORMS = [Platform.MEDIA_PLAYER]
|
||||
|
||||
|
||||
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
||||
"""Component setup, run import config flow for each entry in config."""
|
||||
if DOMAIN in config:
|
||||
for entry in config[DOMAIN]:
|
||||
hass.async_create_task(
|
||||
hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": SOURCE_IMPORT}, data=entry
|
||||
)
|
||||
)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
"""Load the saved entities."""
|
||||
|
||||
|
@ -14,8 +14,6 @@ import voluptuous as vol
|
||||
from homeassistant.components import zeroconf
|
||||
from homeassistant.components.media_player import MediaPlayerDeviceClass
|
||||
from homeassistant.config_entries import (
|
||||
SOURCE_IGNORE,
|
||||
SOURCE_IMPORT,
|
||||
SOURCE_ZEROCONF,
|
||||
ConfigEntry,
|
||||
ConfigFlow,
|
||||
@ -231,7 +229,7 @@ class VizioConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
errors[CONF_HOST] = "existing_config_entry_found"
|
||||
|
||||
if not errors:
|
||||
if self._must_show_form and self.source == SOURCE_ZEROCONF:
|
||||
if self._must_show_form and self.context["source"] == SOURCE_ZEROCONF:
|
||||
# Discovery should always display the config form before trying to
|
||||
# create entry so that user can update default config options
|
||||
self._must_show_form = False
|
||||
@ -251,98 +249,13 @@ class VizioConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
|
||||
if not errors:
|
||||
return await self._create_entry(user_input)
|
||||
elif self._must_show_form and self.source == SOURCE_IMPORT:
|
||||
# Import should always display the config form if CONF_ACCESS_TOKEN
|
||||
# wasn't included but is needed so that the user can choose to update
|
||||
# their configuration.yaml or to proceed with config flow pairing. We
|
||||
# will also provide contextual message to user explaining why
|
||||
_LOGGER.warning(
|
||||
(
|
||||
"Couldn't complete configuration.yaml import: '%s' key is "
|
||||
"missing. Either provide '%s' key in configuration.yaml or "
|
||||
"finish setup by completing configuration via frontend"
|
||||
),
|
||||
CONF_ACCESS_TOKEN,
|
||||
CONF_ACCESS_TOKEN,
|
||||
)
|
||||
self._must_show_form = False
|
||||
else:
|
||||
self._data = copy.deepcopy(user_input)
|
||||
return await self.async_step_pair_tv()
|
||||
|
||||
schema = self._user_schema or _get_config_schema()
|
||||
|
||||
if errors and self.source == SOURCE_IMPORT:
|
||||
# Log an error message if import config flow fails since otherwise failure is silent
|
||||
_LOGGER.error(
|
||||
"Importing from configuration.yaml failed: %s",
|
||||
", ".join(errors.values()),
|
||||
)
|
||||
|
||||
return self.async_show_form(step_id="user", data_schema=schema, errors=errors)
|
||||
|
||||
async def async_step_import(self, import_data: dict[str, Any]) -> ConfigFlowResult:
|
||||
"""Import a config entry from configuration.yaml."""
|
||||
# Check if new config entry matches any existing config entries
|
||||
for entry in self._async_current_entries():
|
||||
# If source is ignore bypass host check and continue through loop
|
||||
if entry.source == SOURCE_IGNORE:
|
||||
continue
|
||||
|
||||
if await self.hass.async_add_executor_job(
|
||||
_host_is_same, entry.data[CONF_HOST], import_data[CONF_HOST]
|
||||
):
|
||||
updated_options: dict[str, Any] = {}
|
||||
updated_data: dict[str, Any] = {}
|
||||
remove_apps = False
|
||||
|
||||
if entry.data[CONF_HOST] != import_data[CONF_HOST]:
|
||||
updated_data[CONF_HOST] = import_data[CONF_HOST]
|
||||
|
||||
if entry.data[CONF_NAME] != import_data[CONF_NAME]:
|
||||
updated_data[CONF_NAME] = import_data[CONF_NAME]
|
||||
|
||||
# Update entry.data[CONF_APPS] if import_config[CONF_APPS] differs, and
|
||||
# pop entry.data[CONF_APPS] if import_config[CONF_APPS] is not specified
|
||||
if entry.data.get(CONF_APPS) != import_data.get(CONF_APPS):
|
||||
if not import_data.get(CONF_APPS):
|
||||
remove_apps = True
|
||||
else:
|
||||
updated_options[CONF_APPS] = import_data[CONF_APPS]
|
||||
|
||||
if entry.data.get(CONF_VOLUME_STEP) != import_data[CONF_VOLUME_STEP]:
|
||||
updated_options[CONF_VOLUME_STEP] = import_data[CONF_VOLUME_STEP]
|
||||
|
||||
if updated_options or updated_data or remove_apps:
|
||||
new_data = entry.data.copy()
|
||||
new_options = entry.options.copy()
|
||||
|
||||
if remove_apps:
|
||||
new_data.pop(CONF_APPS)
|
||||
new_options.pop(CONF_APPS)
|
||||
|
||||
if updated_data:
|
||||
new_data.update(updated_data)
|
||||
|
||||
# options are stored in entry options and data so update both
|
||||
if updated_options:
|
||||
new_data.update(updated_options)
|
||||
new_options.update(updated_options)
|
||||
|
||||
self.hass.config_entries.async_update_entry(
|
||||
entry=entry, data=new_data, options=new_options
|
||||
)
|
||||
return self.async_abort(reason="updated_entry")
|
||||
|
||||
return self.async_abort(reason="already_configured_device")
|
||||
|
||||
self._must_show_form = True
|
||||
# Store config key/value pairs that are not configurable in user step so they
|
||||
# don't get lost on user step
|
||||
if import_data.get(CONF_APPS):
|
||||
self._apps = copy.deepcopy(import_data[CONF_APPS])
|
||||
return await self.async_step_user(user_input=import_data)
|
||||
|
||||
async def async_step_zeroconf(
|
||||
self, discovery_info: zeroconf.ZeroconfServiceInfo
|
||||
) -> ConfigFlowResult:
|
||||
@ -433,11 +346,6 @@ class VizioConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
if pair_data:
|
||||
self._data[CONF_ACCESS_TOKEN] = pair_data.auth_token
|
||||
self._must_show_form = True
|
||||
|
||||
if self.source == SOURCE_IMPORT:
|
||||
# If user is pairing via config import, show different message
|
||||
return await self.async_step_pairing_complete_import()
|
||||
|
||||
return await self.async_step_pairing_complete()
|
||||
|
||||
# If no data was retrieved, it's assumed that the pairing attempt was not
|
||||
|
@ -10,14 +10,6 @@ from homeassistant.components.media_player import (
|
||||
MediaPlayerDeviceClass,
|
||||
MediaPlayerEntityFeature,
|
||||
)
|
||||
from homeassistant.const import (
|
||||
CONF_ACCESS_TOKEN,
|
||||
CONF_DEVICE_CLASS,
|
||||
CONF_EXCLUDE,
|
||||
CONF_HOST,
|
||||
CONF_INCLUDE,
|
||||
CONF_NAME,
|
||||
)
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.helpers.typing import VolDictType
|
||||
|
||||
@ -84,43 +76,3 @@ VIZIO_DEVICE_CLASSES = {
|
||||
MediaPlayerDeviceClass.SPEAKER: VIZIO_DEVICE_CLASS_SPEAKER,
|
||||
MediaPlayerDeviceClass.TV: VIZIO_DEVICE_CLASS_TV,
|
||||
}
|
||||
|
||||
VIZIO_SCHEMA = {
|
||||
vol.Required(CONF_HOST): cv.string,
|
||||
vol.Optional(CONF_ACCESS_TOKEN): cv.string,
|
||||
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
|
||||
vol.Optional(CONF_DEVICE_CLASS, default=DEFAULT_DEVICE_CLASS): vol.All(
|
||||
cv.string,
|
||||
vol.Lower,
|
||||
vol.In([MediaPlayerDeviceClass.TV, MediaPlayerDeviceClass.SPEAKER]),
|
||||
),
|
||||
vol.Optional(CONF_VOLUME_STEP, default=DEFAULT_VOLUME_STEP): vol.All(
|
||||
vol.Coerce(int), vol.Range(min=1, max=10)
|
||||
),
|
||||
vol.Optional(CONF_APPS): vol.All(
|
||||
{
|
||||
vol.Exclusive(CONF_INCLUDE, "apps_filter"): vol.All(
|
||||
cv.ensure_list, [cv.string]
|
||||
),
|
||||
vol.Exclusive(CONF_EXCLUDE, "apps_filter"): vol.All(
|
||||
cv.ensure_list, [cv.string]
|
||||
),
|
||||
vol.Optional(CONF_ADDITIONAL_CONFIGS): vol.All(
|
||||
cv.ensure_list,
|
||||
[
|
||||
{
|
||||
vol.Required(CONF_NAME): cv.string,
|
||||
vol.Required(CONF_CONFIG): {
|
||||
vol.Required(CONF_APP_ID): cv.string,
|
||||
vol.Required(CONF_NAME_SPACE): vol.Coerce(int),
|
||||
vol.Optional(CONF_MESSAGE, default=None): vol.Or(
|
||||
cv.string, None
|
||||
),
|
||||
},
|
||||
},
|
||||
],
|
||||
),
|
||||
},
|
||||
cv.has_at_least_one_key(CONF_INCLUDE, CONF_EXCLUDE, CONF_ADDITIONAL_CONFIGS),
|
||||
),
|
||||
}
|
||||
|
@ -112,14 +112,6 @@ MOCK_OPTIONS = {
|
||||
CONF_VOLUME_STEP: VOLUME_STEP,
|
||||
}
|
||||
|
||||
MOCK_IMPORT_VALID_TV_CONFIG = {
|
||||
CONF_NAME: NAME,
|
||||
CONF_HOST: HOST,
|
||||
CONF_DEVICE_CLASS: MediaPlayerDeviceClass.TV,
|
||||
CONF_ACCESS_TOKEN: ACCESS_TOKEN,
|
||||
CONF_VOLUME_STEP: VOLUME_STEP,
|
||||
}
|
||||
|
||||
MOCK_TV_WITH_INCLUDE_CONFIG = {
|
||||
CONF_NAME: NAME,
|
||||
CONF_HOST: HOST,
|
||||
@ -147,23 +139,6 @@ MOCK_TV_WITH_ADDITIONAL_APPS_CONFIG = {
|
||||
CONF_APPS: {CONF_ADDITIONAL_CONFIGS: [ADDITIONAL_APP_CONFIG]},
|
||||
}
|
||||
|
||||
MOCK_SPEAKER_APPS_FAILURE = {
|
||||
CONF_NAME: NAME,
|
||||
CONF_HOST: HOST,
|
||||
CONF_DEVICE_CLASS: MediaPlayerDeviceClass.SPEAKER,
|
||||
CONF_ACCESS_TOKEN: ACCESS_TOKEN,
|
||||
CONF_VOLUME_STEP: VOLUME_STEP,
|
||||
CONF_APPS: {CONF_ADDITIONAL_CONFIGS: [ADDITIONAL_APP_CONFIG]},
|
||||
}
|
||||
|
||||
MOCK_TV_APPS_FAILURE = {
|
||||
CONF_NAME: NAME,
|
||||
CONF_HOST: HOST,
|
||||
CONF_DEVICE_CLASS: MediaPlayerDeviceClass.TV,
|
||||
CONF_ACCESS_TOKEN: ACCESS_TOKEN,
|
||||
CONF_VOLUME_STEP: VOLUME_STEP,
|
||||
CONF_APPS: None,
|
||||
}
|
||||
|
||||
MOCK_TV_APPS_WITH_VALID_APPS_CONFIG = {
|
||||
CONF_HOST: HOST,
|
||||
|
@ -3,30 +3,20 @@
|
||||
import dataclasses
|
||||
|
||||
import pytest
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.media_player import MediaPlayerDeviceClass
|
||||
from homeassistant.components.vizio.config_flow import _get_config_schema
|
||||
from homeassistant.components.vizio.const import (
|
||||
CONF_APPS,
|
||||
CONF_APPS_TO_INCLUDE_OR_EXCLUDE,
|
||||
CONF_INCLUDE,
|
||||
CONF_VOLUME_STEP,
|
||||
DEFAULT_NAME,
|
||||
DEFAULT_VOLUME_STEP,
|
||||
DOMAIN,
|
||||
VIZIO_SCHEMA,
|
||||
)
|
||||
from homeassistant.config_entries import (
|
||||
SOURCE_IGNORE,
|
||||
SOURCE_IMPORT,
|
||||
SOURCE_USER,
|
||||
SOURCE_ZEROCONF,
|
||||
)
|
||||
from homeassistant.config_entries import SOURCE_IGNORE, SOURCE_USER, SOURCE_ZEROCONF
|
||||
from homeassistant.const import (
|
||||
CONF_ACCESS_TOKEN,
|
||||
CONF_DEVICE_CLASS,
|
||||
CONF_HOST,
|
||||
CONF_INCLUDE,
|
||||
CONF_NAME,
|
||||
CONF_PIN,
|
||||
)
|
||||
@ -38,14 +28,11 @@ from .const import (
|
||||
CURRENT_APP,
|
||||
HOST,
|
||||
HOST2,
|
||||
MOCK_IMPORT_VALID_TV_CONFIG,
|
||||
MOCK_INCLUDE_APPS,
|
||||
MOCK_INCLUDE_NO_APPS,
|
||||
MOCK_PIN_CONFIG,
|
||||
MOCK_SPEAKER_CONFIG,
|
||||
MOCK_TV_CONFIG_NO_TOKEN,
|
||||
MOCK_TV_WITH_ADDITIONAL_APPS_CONFIG,
|
||||
MOCK_TV_WITH_EXCLUDE_CONFIG,
|
||||
MOCK_USER_VALID_TV_CONFIG,
|
||||
MOCK_ZEROCONF_SERVICE_INFO,
|
||||
NAME,
|
||||
@ -370,297 +357,6 @@ async def test_user_ignore(hass: HomeAssistant) -> None:
|
||||
assert result["type"] is FlowResultType.CREATE_ENTRY
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("vizio_connect", "vizio_bypass_setup")
|
||||
async def test_import_flow_minimum_fields(hass: HomeAssistant) -> None:
|
||||
"""Test import config flow with minimum fields."""
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={"source": SOURCE_IMPORT},
|
||||
data=vol.Schema(VIZIO_SCHEMA)(
|
||||
{CONF_HOST: HOST, CONF_DEVICE_CLASS: MediaPlayerDeviceClass.SPEAKER}
|
||||
),
|
||||
)
|
||||
|
||||
assert result["type"] is FlowResultType.CREATE_ENTRY
|
||||
assert result["title"] == DEFAULT_NAME
|
||||
assert result["data"][CONF_NAME] == DEFAULT_NAME
|
||||
assert result["data"][CONF_HOST] == HOST
|
||||
assert result["data"][CONF_DEVICE_CLASS] == MediaPlayerDeviceClass.SPEAKER
|
||||
assert result["data"][CONF_VOLUME_STEP] == DEFAULT_VOLUME_STEP
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("vizio_connect", "vizio_bypass_setup")
|
||||
async def test_import_flow_all_fields(hass: HomeAssistant) -> None:
|
||||
"""Test import config flow with all fields."""
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={"source": SOURCE_IMPORT},
|
||||
data=vol.Schema(VIZIO_SCHEMA)(MOCK_IMPORT_VALID_TV_CONFIG),
|
||||
)
|
||||
|
||||
assert result["type"] is FlowResultType.CREATE_ENTRY
|
||||
assert result["title"] == NAME
|
||||
assert result["data"][CONF_NAME] == NAME
|
||||
assert result["data"][CONF_HOST] == HOST
|
||||
assert result["data"][CONF_DEVICE_CLASS] == MediaPlayerDeviceClass.TV
|
||||
assert result["data"][CONF_ACCESS_TOKEN] == ACCESS_TOKEN
|
||||
assert result["data"][CONF_VOLUME_STEP] == VOLUME_STEP
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("vizio_connect", "vizio_bypass_setup")
|
||||
async def test_import_entity_already_configured(hass: HomeAssistant) -> None:
|
||||
"""Test entity is already configured during import setup."""
|
||||
entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
data=vol.Schema(VIZIO_SCHEMA)(MOCK_SPEAKER_CONFIG),
|
||||
options={CONF_VOLUME_STEP: VOLUME_STEP},
|
||||
)
|
||||
entry.add_to_hass(hass)
|
||||
fail_entry = vol.Schema(VIZIO_SCHEMA)(MOCK_SPEAKER_CONFIG.copy())
|
||||
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": SOURCE_IMPORT}, data=fail_entry
|
||||
)
|
||||
|
||||
assert result["type"] is FlowResultType.ABORT
|
||||
assert result["reason"] == "already_configured_device"
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("vizio_connect", "vizio_bypass_update")
|
||||
async def test_import_flow_update_options(hass: HomeAssistant) -> None:
|
||||
"""Test import config flow with updated options."""
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={"source": SOURCE_IMPORT},
|
||||
data=vol.Schema(VIZIO_SCHEMA)(MOCK_SPEAKER_CONFIG),
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert result["result"].options == {CONF_VOLUME_STEP: DEFAULT_VOLUME_STEP}
|
||||
assert result["type"] is FlowResultType.CREATE_ENTRY
|
||||
entry_id = result["result"].entry_id
|
||||
|
||||
updated_config = MOCK_SPEAKER_CONFIG.copy()
|
||||
updated_config[CONF_VOLUME_STEP] = VOLUME_STEP + 1
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={"source": SOURCE_IMPORT},
|
||||
data=vol.Schema(VIZIO_SCHEMA)(updated_config),
|
||||
)
|
||||
|
||||
assert result["type"] is FlowResultType.ABORT
|
||||
assert result["reason"] == "updated_entry"
|
||||
config_entry = hass.config_entries.async_get_entry(entry_id)
|
||||
assert config_entry.options[CONF_VOLUME_STEP] == VOLUME_STEP + 1
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("vizio_connect", "vizio_bypass_update")
|
||||
async def test_import_flow_update_name_and_apps(hass: HomeAssistant) -> None:
|
||||
"""Test import config flow with updated name and apps."""
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={"source": SOURCE_IMPORT},
|
||||
data=vol.Schema(VIZIO_SCHEMA)(MOCK_IMPORT_VALID_TV_CONFIG),
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert result["result"].data[CONF_NAME] == NAME
|
||||
assert result["type"] is FlowResultType.CREATE_ENTRY
|
||||
entry_id = result["result"].entry_id
|
||||
|
||||
updated_config = MOCK_IMPORT_VALID_TV_CONFIG.copy()
|
||||
updated_config[CONF_NAME] = NAME2
|
||||
updated_config[CONF_APPS] = {CONF_INCLUDE: [CURRENT_APP]}
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={"source": SOURCE_IMPORT},
|
||||
data=vol.Schema(VIZIO_SCHEMA)(updated_config),
|
||||
)
|
||||
|
||||
assert result["type"] is FlowResultType.ABORT
|
||||
assert result["reason"] == "updated_entry"
|
||||
config_entry = hass.config_entries.async_get_entry(entry_id)
|
||||
assert config_entry.data[CONF_NAME] == NAME2
|
||||
assert config_entry.data[CONF_APPS] == {CONF_INCLUDE: [CURRENT_APP]}
|
||||
assert config_entry.options[CONF_APPS] == {CONF_INCLUDE: [CURRENT_APP]}
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("vizio_connect", "vizio_bypass_update")
|
||||
async def test_import_flow_update_remove_apps(hass: HomeAssistant) -> None:
|
||||
"""Test import config flow with removed apps."""
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={"source": SOURCE_IMPORT},
|
||||
data=vol.Schema(VIZIO_SCHEMA)(MOCK_TV_WITH_EXCLUDE_CONFIG),
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert result["result"].data[CONF_NAME] == NAME
|
||||
assert result["type"] is FlowResultType.CREATE_ENTRY
|
||||
config_entry = hass.config_entries.async_get_entry(result["result"].entry_id)
|
||||
assert CONF_APPS in config_entry.data
|
||||
assert CONF_APPS in config_entry.options
|
||||
|
||||
updated_config = MOCK_TV_WITH_EXCLUDE_CONFIG.copy()
|
||||
updated_config.pop(CONF_APPS)
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={"source": SOURCE_IMPORT},
|
||||
data=vol.Schema(VIZIO_SCHEMA)(updated_config),
|
||||
)
|
||||
|
||||
assert result["type"] is FlowResultType.ABORT
|
||||
assert result["reason"] == "updated_entry"
|
||||
assert CONF_APPS not in config_entry.data
|
||||
assert CONF_APPS not in config_entry.options
|
||||
|
||||
|
||||
@pytest.mark.usefixtures(
|
||||
"vizio_connect", "vizio_bypass_setup", "vizio_complete_pairing"
|
||||
)
|
||||
async def test_import_needs_pairing(hass: HomeAssistant) -> None:
|
||||
"""Test pairing config flow when access token not provided for tv during import."""
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": SOURCE_IMPORT}, data=MOCK_TV_CONFIG_NO_TOKEN
|
||||
)
|
||||
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["step_id"] == "user"
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], user_input=MOCK_TV_CONFIG_NO_TOKEN
|
||||
)
|
||||
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["step_id"] == "pair_tv"
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], user_input=MOCK_PIN_CONFIG
|
||||
)
|
||||
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["step_id"] == "pairing_complete_import"
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(result["flow_id"])
|
||||
|
||||
assert result["type"] is FlowResultType.CREATE_ENTRY
|
||||
assert result["title"] == NAME
|
||||
assert result["data"][CONF_NAME] == NAME
|
||||
assert result["data"][CONF_HOST] == HOST
|
||||
assert result["data"][CONF_DEVICE_CLASS] == MediaPlayerDeviceClass.TV
|
||||
|
||||
|
||||
@pytest.mark.usefixtures(
|
||||
"vizio_connect", "vizio_bypass_setup", "vizio_complete_pairing"
|
||||
)
|
||||
async def test_import_with_apps_needs_pairing(hass: HomeAssistant) -> None:
|
||||
"""Test pairing config flow when access token not provided for tv but apps are included during import."""
|
||||
import_config = MOCK_TV_CONFIG_NO_TOKEN.copy()
|
||||
import_config[CONF_APPS] = {CONF_INCLUDE: [CURRENT_APP]}
|
||||
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": SOURCE_IMPORT}, data=import_config
|
||||
)
|
||||
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["step_id"] == "user"
|
||||
|
||||
# Mock inputting info without apps to make sure apps get stored
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
user_input=_get_config_schema(MOCK_TV_CONFIG_NO_TOKEN)(MOCK_TV_CONFIG_NO_TOKEN),
|
||||
)
|
||||
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["step_id"] == "pair_tv"
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], user_input=MOCK_PIN_CONFIG
|
||||
)
|
||||
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["step_id"] == "pairing_complete_import"
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(result["flow_id"])
|
||||
|
||||
assert result["type"] is FlowResultType.CREATE_ENTRY
|
||||
assert result["title"] == NAME
|
||||
assert result["data"][CONF_NAME] == NAME
|
||||
assert result["data"][CONF_HOST] == HOST
|
||||
assert result["data"][CONF_DEVICE_CLASS] == MediaPlayerDeviceClass.TV
|
||||
assert result["data"][CONF_APPS][CONF_INCLUDE] == [CURRENT_APP]
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("vizio_connect", "vizio_bypass_update")
|
||||
async def test_import_flow_additional_configs(hass: HomeAssistant) -> None:
|
||||
"""Test import config flow with additional configs defined in CONF_APPS."""
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={"source": SOURCE_IMPORT},
|
||||
data=vol.Schema(VIZIO_SCHEMA)(MOCK_TV_WITH_ADDITIONAL_APPS_CONFIG),
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert result["result"].data[CONF_NAME] == NAME
|
||||
assert result["type"] is FlowResultType.CREATE_ENTRY
|
||||
config_entry = hass.config_entries.async_get_entry(result["result"].entry_id)
|
||||
assert CONF_APPS in config_entry.data
|
||||
assert CONF_APPS not in config_entry.options
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("vizio_connect", "vizio_bypass_setup")
|
||||
async def test_import_error(
|
||||
hass: HomeAssistant,
|
||||
caplog: pytest.LogCaptureFixture,
|
||||
) -> None:
|
||||
"""Test that error is logged when import config has an error."""
|
||||
entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
data=vol.Schema(VIZIO_SCHEMA)(MOCK_SPEAKER_CONFIG),
|
||||
options={CONF_VOLUME_STEP: VOLUME_STEP},
|
||||
unique_id=UNIQUE_ID,
|
||||
)
|
||||
entry.add_to_hass(hass)
|
||||
fail_entry = MOCK_SPEAKER_CONFIG.copy()
|
||||
fail_entry[CONF_HOST] = "0.0.0.0"
|
||||
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={"source": SOURCE_IMPORT},
|
||||
data=vol.Schema(VIZIO_SCHEMA)(fail_entry),
|
||||
)
|
||||
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
|
||||
# Ensure error gets logged
|
||||
vizio_log_list = [
|
||||
log
|
||||
for log in caplog.records
|
||||
if log.name == "homeassistant.components.vizio.config_flow"
|
||||
]
|
||||
assert len(vizio_log_list) == 1
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("vizio_connect", "vizio_bypass_setup")
|
||||
async def test_import_ignore(hass: HomeAssistant) -> None:
|
||||
"""Test import config flow doesn't throw an error when there's an existing ignored source."""
|
||||
entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
data=MOCK_SPEAKER_CONFIG,
|
||||
options={CONF_VOLUME_STEP: VOLUME_STEP},
|
||||
source=SOURCE_IGNORE,
|
||||
)
|
||||
entry.add_to_hass(hass)
|
||||
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={"source": SOURCE_IMPORT},
|
||||
data=vol.Schema(VIZIO_SCHEMA)(MOCK_SPEAKER_CONFIG),
|
||||
)
|
||||
|
||||
assert result["type"] is FlowResultType.CREATE_ENTRY
|
||||
|
||||
|
||||
@pytest.mark.usefixtures(
|
||||
"vizio_connect", "vizio_bypass_setup", "vizio_guess_device_type"
|
||||
)
|
||||
@ -854,26 +550,3 @@ async def test_zeroconf_flow_already_configured_hostname(hass: HomeAssistant) ->
|
||||
# Flow should abort because device is already setup
|
||||
assert result["type"] is FlowResultType.ABORT
|
||||
assert result["reason"] == "already_configured"
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("vizio_connect", "vizio_bypass_setup", "vizio_hostname_check")
|
||||
async def test_import_flow_already_configured_hostname(hass: HomeAssistant) -> None:
|
||||
"""Test entity is already configured during import setup when existing entry uses hostname."""
|
||||
config = MOCK_SPEAKER_CONFIG.copy()
|
||||
config[CONF_HOST] = "hostname"
|
||||
entry = MockConfigEntry(
|
||||
domain=DOMAIN, data=config, options={CONF_VOLUME_STEP: VOLUME_STEP}
|
||||
)
|
||||
entry.add_to_hass(hass)
|
||||
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={"source": SOURCE_IMPORT},
|
||||
data=vol.Schema(VIZIO_SCHEMA)(MOCK_SPEAKER_CONFIG),
|
||||
)
|
||||
|
||||
# Flow should abort because device was updated
|
||||
assert result["type"] is FlowResultType.ABORT
|
||||
assert result["reason"] == "updated_entry"
|
||||
|
||||
assert entry.data[CONF_HOST] == HOST
|
||||
|
@ -7,7 +7,6 @@ import pytest
|
||||
from homeassistant.components.vizio.const import DOMAIN
|
||||
from homeassistant.const import STATE_UNAVAILABLE, Platform
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.setup import async_setup_component
|
||||
import homeassistant.util.dt as dt_util
|
||||
|
||||
from .const import MOCK_SPEAKER_CONFIG, MOCK_USER_VALID_TV_CONFIG, UNIQUE_ID
|
||||
@ -15,16 +14,6 @@ from .const import MOCK_SPEAKER_CONFIG, MOCK_USER_VALID_TV_CONFIG, UNIQUE_ID
|
||||
from tests.common import MockConfigEntry, async_fire_time_changed
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("vizio_connect", "vizio_update")
|
||||
async def test_setup_component(hass: HomeAssistant) -> None:
|
||||
"""Test component setup."""
|
||||
assert await async_setup_component(
|
||||
hass, DOMAIN, {DOMAIN: MOCK_USER_VALID_TV_CONFIG}
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
assert len(hass.states.async_entity_ids(Platform.MEDIA_PLAYER)) == 1
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("vizio_connect", "vizio_update")
|
||||
async def test_tv_load_and_unload(hass: HomeAssistant) -> None:
|
||||
"""Test loading and unloading TV entry."""
|
||||
|
@ -19,7 +19,6 @@ from pyvizio.const import (
|
||||
MAX_VOLUME,
|
||||
UNKNOWN_APP,
|
||||
)
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.media_player import (
|
||||
ATTR_INPUT_SOURCE,
|
||||
@ -42,7 +41,6 @@ from homeassistant.components.media_player import (
|
||||
SERVICE_VOLUME_UP,
|
||||
MediaPlayerDeviceClass,
|
||||
)
|
||||
from homeassistant.components.vizio import validate_apps
|
||||
from homeassistant.components.vizio.const import (
|
||||
CONF_ADDITIONAL_CONFIGS,
|
||||
CONF_APPS,
|
||||
@ -50,7 +48,6 @@ from homeassistant.components.vizio.const import (
|
||||
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
|
||||
@ -69,9 +66,7 @@ from .const import (
|
||||
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,
|
||||
@ -155,7 +150,7 @@ async def _test_setup_tv(hass: HomeAssistant, vizio_power_state: bool | None) ->
|
||||
|
||||
config_entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
data=vol.Schema(VIZIO_SCHEMA)(MOCK_USER_VALID_TV_CONFIG),
|
||||
data=MOCK_USER_VALID_TV_CONFIG,
|
||||
unique_id=UNIQUE_ID,
|
||||
)
|
||||
|
||||
@ -181,7 +176,7 @@ async def _test_setup_speaker(
|
||||
|
||||
config_entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
data=vol.Schema(VIZIO_SCHEMA)(MOCK_SPEAKER_CONFIG),
|
||||
data=MOCK_SPEAKER_CONFIG,
|
||||
unique_id=UNIQUE_ID,
|
||||
)
|
||||
|
||||
@ -215,7 +210,7 @@ async def _cm_for_test_setup_tv_with_apps(
|
||||
) -> AsyncIterator[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
|
||||
domain=DOMAIN, data=device_config, unique_id=UNIQUE_ID
|
||||
)
|
||||
|
||||
async with _cm_for_test_setup_without_apps(
|
||||
@ -641,15 +636,6 @@ async def test_setup_with_apps_additional_apps_config(
|
||||
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)
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("vizio_connect", "vizio_update_with_apps")
|
||||
async def test_setup_with_unknown_app_config(
|
||||
hass: HomeAssistant,
|
||||
@ -687,7 +673,7 @@ async def test_setup_tv_without_mute(hass: HomeAssistant) -> 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),
|
||||
data=MOCK_USER_VALID_TV_CONFIG,
|
||||
unique_id=UNIQUE_ID,
|
||||
)
|
||||
|
||||
@ -742,7 +728,7 @@ async def test_vizio_update_with_apps_on_input(hass: HomeAssistant) -> 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),
|
||||
data=MOCK_USER_VALID_TV_CONFIG,
|
||||
unique_id=UNIQUE_ID,
|
||||
)
|
||||
await _add_config_entry_to_hass(hass, config_entry)
|
||||
|
Loading…
x
Reference in New Issue
Block a user