forked from home-assistant/core
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add Config Flow support, Device Registry support, available property …
…to vizio component (home-assistant#30653) * add config flow support, device registry support, available property * raise PlatformNotReady if HA cant connect to device * remove test logging statement and fix integration title * store import and last user input values so user can see errors next to value that caused error * add PARALLEL_UPDATES * add missing type hints * add missing type hints to tests * fix options config flow title * changes based on review * better key name for message when cant connect * fix missed update to key name * fix comments * remove logger from test which was used to debug and update test function names and docstrings to be more accurate * add __init__.py to vizio tests module * readded options flow and updated main component to handle options updates, set unique ID to serial, fixes based on review * pop hass.data in media_player unload instead of in __init__ since it is set in media_player * update requirements_all and requirements_test_all * make unique_id key name a constant * remove additional line breaks after docstrings * unload entries during test_user_flow and test_import_flow tests to hopefully reduce teardown time * try to speed up tests * remove unnecessary code, use event bus to track options updates, move patches to pytest fixtures and fix patch scoping * fix comment * remove translations from commit * suppress API error logging when checking for device availability as it can spam logs * update requirements_all and requirements_test_all * dont pass hass to entity since it is passed to entity anyway, remove entity unload from tests, other misc changes from review * fix clearing listeners * use config_entry unique ID for unique ID and use config_entry entry ID as update signal * update config flow based on suggested changes * update volume step on config import if it doesn't match config_entry volume step * update config_entry data and options with new volume step value * copy entry.data and entry.options before updating when updating config_entry * fix test_import_entity_already_configured
- Loading branch information
1 parent
5fa7d6f
commit ac771ad
Showing
11 changed files
with
668 additions
and
71 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,171 @@ | ||
"""Config flow for Vizio.""" | ||
import logging | ||
from typing import Any, Dict | ||
|
||
from pyvizio import VizioAsync | ||
import voluptuous as vol | ||
|
||
from homeassistant import config_entries | ||
from homeassistant.config_entries import ConfigEntry | ||
from homeassistant.const import ( | ||
CONF_ACCESS_TOKEN, | ||
CONF_DEVICE_CLASS, | ||
CONF_HOST, | ||
CONF_NAME, | ||
) | ||
from homeassistant.core import callback | ||
|
||
from . import validate_auth | ||
from .const import ( | ||
CONF_VOLUME_STEP, | ||
DEFAULT_DEVICE_CLASS, | ||
DEFAULT_NAME, | ||
DEFAULT_VOLUME_STEP, | ||
DOMAIN, | ||
) | ||
|
||
_LOGGER = logging.getLogger(__name__) | ||
|
||
|
||
def update_schema_defaults(input_dict: Dict[str, Any]) -> vol.Schema: | ||
"""Update schema defaults based on user input/config dict. Retains info already provided for future form views.""" | ||
return vol.Schema( | ||
{ | ||
vol.Required( | ||
CONF_NAME, default=input_dict.get(CONF_NAME, DEFAULT_NAME) | ||
): str, | ||
vol.Required(CONF_HOST, default=input_dict.get(CONF_HOST)): str, | ||
vol.Optional( | ||
CONF_DEVICE_CLASS, | ||
default=input_dict.get(CONF_DEVICE_CLASS, DEFAULT_DEVICE_CLASS), | ||
): vol.All(str, vol.Lower, vol.In(["tv", "soundbar"])), | ||
vol.Optional( | ||
CONF_ACCESS_TOKEN, default=input_dict.get(CONF_ACCESS_TOKEN, "") | ||
): str, | ||
}, | ||
extra=vol.REMOVE_EXTRA, | ||
) | ||
|
||
|
||
class VizioConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): | ||
"""Handle a Vizio config flow.""" | ||
|
||
VERSION = 1 | ||
CONNECTION_CLASS = config_entries.CONN_CLASS_LOCAL_POLL | ||
|
||
@staticmethod | ||
@callback | ||
def async_get_options_flow(config_entry): | ||
"""Get the options flow for this handler.""" | ||
return VizioOptionsConfigFlow(config_entry) | ||
|
||
def __init__(self) -> None: | ||
"""Initialize config flow.""" | ||
self.import_schema = None | ||
self.user_schema = None | ||
|
||
async def async_step_user( | ||
self, user_input: Dict[str, Any] = None | ||
) -> Dict[str, Any]: | ||
"""Handle a flow initialized by the user.""" | ||
errors = {} | ||
|
||
if user_input is not None: | ||
# Store current values in case setup fails and user needs to edit | ||
self.user_schema = update_schema_defaults(user_input) | ||
|
||
# Check if new config entry matches any existing config entries | ||
for entry in self.hass.config_entries.async_entries(DOMAIN): | ||
if entry.data[CONF_HOST] == user_input[CONF_HOST]: | ||
errors[CONF_HOST] = "host_exists" | ||
break | ||
|
||
if entry.data[CONF_NAME] == user_input[CONF_NAME]: | ||
errors[CONF_NAME] = "name_exists" | ||
break | ||
|
||
if not errors: | ||
try: | ||
# Ensure schema passes custom validation, otherwise catch exception and add error | ||
validate_auth(user_input) | ||
|
||
# Ensure config is valid for a device | ||
if not await VizioAsync.validate_ha_config( | ||
user_input[CONF_HOST], | ||
user_input.get(CONF_ACCESS_TOKEN), | ||
user_input[CONF_DEVICE_CLASS], | ||
): | ||
errors["base"] = "cant_connect" | ||
except vol.Invalid: | ||
errors["base"] = "tv_needs_token" | ||
|
||
if not errors: | ||
unique_id = await VizioAsync.get_unique_id( | ||
user_input[CONF_HOST], | ||
user_input.get(CONF_ACCESS_TOKEN), | ||
user_input[CONF_DEVICE_CLASS], | ||
) | ||
|
||
# Abort flow if existing component with same unique ID matches new config entry | ||
if await self.async_set_unique_id( | ||
unique_id=unique_id, raise_on_progress=True | ||
): | ||
return self.async_abort(reason="already_setup") | ||
|
||
return self.async_create_entry( | ||
title=user_input[CONF_NAME], data=user_input | ||
) | ||
|
||
schema = self.user_schema or self.import_schema or update_schema_defaults({}) | ||
|
||
return self.async_show_form(step_id="user", data_schema=schema, errors=errors) | ||
|
||
async def async_step_import(self, import_config: Dict[str, Any]) -> Dict[str, Any]: | ||
"""Import a config entry from configuration.yaml.""" | ||
# Check if new config entry matches any existing config entries | ||
for entry in self.hass.config_entries.async_entries(DOMAIN): | ||
if entry.data[CONF_HOST] == import_config[CONF_HOST] and entry.data[ | ||
CONF_NAME | ||
] == import_config.get(CONF_NAME): | ||
if entry.data[CONF_VOLUME_STEP] != import_config[CONF_VOLUME_STEP]: | ||
new_volume_step = { | ||
CONF_VOLUME_STEP: import_config[CONF_VOLUME_STEP] | ||
} | ||
self.hass.config_entries.async_update_entry( | ||
entry=entry, | ||
data=entry.data.copy().update(new_volume_step), | ||
options=entry.options.copy().update(new_volume_step), | ||
) | ||
return self.async_abort(reason="updated_volume_step") | ||
return self.async_abort(reason="already_setup") | ||
|
||
# Store import values in case setup fails so user can see error | ||
self.import_schema = update_schema_defaults(import_config) | ||
|
||
return await self.async_step_user(user_input=import_config) | ||
|
||
|
||
class VizioOptionsConfigFlow(config_entries.OptionsFlow): | ||
"""Handle Transmission client options.""" | ||
|
||
def __init__(self, config_entry: ConfigEntry) -> None: | ||
"""Initialize vizio options flow.""" | ||
self.config_entry = config_entry | ||
|
||
async def async_step_init( | ||
self, user_input: Dict[str, Any] = None | ||
) -> Dict[str, Any]: | ||
"""Manage the vizio options.""" | ||
if user_input is not None: | ||
return self.async_create_entry(title="", data=user_input) | ||
|
||
options = { | ||
vol.Optional( | ||
CONF_VOLUME_STEP, | ||
default=self.config_entry.options.get( | ||
CONF_VOLUME_STEP, DEFAULT_VOLUME_STEP | ||
), | ||
): vol.All(vol.Coerce(int), vol.Range(min=1, max=10)) | ||
} | ||
|
||
return self.async_show_form(step_id="init", data_schema=vol.Schema(options)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,4 @@ | ||
"""Constants used by vizio component.""" | ||
|
||
CONF_VOLUME_STEP = "volume_step" | ||
|
||
DEFAULT_NAME = "Vizio SmartCast" | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.