mirror of
https://github.com/home-assistant/core.git
synced 2025-04-24 17:27:52 +00:00
Options flow for Monoprice sources (#33156)
* Options flow for monoprice sources * Fix lint errors
This commit is contained in:
parent
2d002f3ef6
commit
b50281a917
@ -33,6 +33,8 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
|
||||
_LOGGER.error("Error connecting to Monoprice controller at %s", port)
|
||||
raise ConfigEntryNotReady
|
||||
|
||||
entry.add_update_listener(_update_listener)
|
||||
|
||||
for component in PLATFORMS:
|
||||
hass.async_create_task(
|
||||
hass.config_entries.async_forward_entry_setup(entry, component)
|
||||
@ -53,3 +55,8 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry):
|
||||
)
|
||||
|
||||
return unload_ok
|
||||
|
||||
|
||||
async def _update_listener(hass: HomeAssistant, entry: ConfigEntry):
|
||||
"""Handle options update."""
|
||||
await hass.config_entries.async_reload(entry.entry_id)
|
||||
|
@ -21,17 +21,31 @@ from .const import DOMAIN # pylint:disable=unused-import
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
DATA_SCHEMA = vol.Schema(
|
||||
{
|
||||
vol.Required(CONF_PORT): str,
|
||||
vol.Optional(CONF_SOURCE_1): str,
|
||||
vol.Optional(CONF_SOURCE_2): str,
|
||||
vol.Optional(CONF_SOURCE_3): str,
|
||||
vol.Optional(CONF_SOURCE_4): str,
|
||||
vol.Optional(CONF_SOURCE_5): str,
|
||||
vol.Optional(CONF_SOURCE_6): str,
|
||||
SOURCES = [
|
||||
CONF_SOURCE_1,
|
||||
CONF_SOURCE_2,
|
||||
CONF_SOURCE_3,
|
||||
CONF_SOURCE_4,
|
||||
CONF_SOURCE_5,
|
||||
CONF_SOURCE_6,
|
||||
]
|
||||
|
||||
OPTIONS_FOR_DATA = {vol.Optional(source): str for source in SOURCES}
|
||||
|
||||
DATA_SCHEMA = vol.Schema({vol.Required(CONF_PORT): str, **OPTIONS_FOR_DATA})
|
||||
|
||||
|
||||
@core.callback
|
||||
def _sources_from_config(data):
|
||||
sources_config = {
|
||||
str(idx + 1): data.get(source) for idx, source in enumerate(SOURCES)
|
||||
}
|
||||
|
||||
return {
|
||||
index: name.strip()
|
||||
for index, name in sources_config.items()
|
||||
if (name is not None and name.strip() != "")
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
async def validate_input(hass: core.HomeAssistant, data):
|
||||
@ -45,19 +59,8 @@ async def validate_input(hass: core.HomeAssistant, data):
|
||||
_LOGGER.error("Error connecting to Monoprice controller")
|
||||
raise CannotConnect
|
||||
|
||||
sources_config = {
|
||||
1: data.get(CONF_SOURCE_1),
|
||||
2: data.get(CONF_SOURCE_2),
|
||||
3: data.get(CONF_SOURCE_3),
|
||||
4: data.get(CONF_SOURCE_4),
|
||||
5: data.get(CONF_SOURCE_5),
|
||||
6: data.get(CONF_SOURCE_6),
|
||||
}
|
||||
sources = {
|
||||
index: name.strip()
|
||||
for index, name in sources_config.items()
|
||||
if (name is not None and name.strip() != "")
|
||||
}
|
||||
sources = _sources_from_config(data)
|
||||
|
||||
# Return info that you want to store in the config entry.
|
||||
return {CONF_PORT: data[CONF_PORT], CONF_SOURCES: sources}
|
||||
|
||||
@ -86,6 +89,55 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||
step_id="user", data_schema=DATA_SCHEMA, errors=errors
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
@core.callback
|
||||
def async_get_options_flow(config_entry):
|
||||
"""Define the config flow to handle options."""
|
||||
return MonopriceOptionsFlowHandler(config_entry)
|
||||
|
||||
|
||||
@core.callback
|
||||
def _key_for_source(index, source, previous_sources):
|
||||
if str(index) in previous_sources:
|
||||
key = vol.Optional(source, default=previous_sources[str(index)])
|
||||
else:
|
||||
key = vol.Optional(source)
|
||||
|
||||
return key
|
||||
|
||||
|
||||
class MonopriceOptionsFlowHandler(config_entries.OptionsFlow):
|
||||
"""Handle a Monoprice options flow."""
|
||||
|
||||
def __init__(self, config_entry):
|
||||
"""Initialize."""
|
||||
self.config_entry = config_entry
|
||||
|
||||
@core.callback
|
||||
def _previous_sources(self):
|
||||
if CONF_SOURCES in self.config_entry.options:
|
||||
previous = self.config_entry.options[CONF_SOURCES]
|
||||
else:
|
||||
previous = self.config_entry.data[CONF_SOURCES]
|
||||
|
||||
return previous
|
||||
|
||||
async def async_step_init(self, user_input=None):
|
||||
"""Manage the options."""
|
||||
if user_input is not None:
|
||||
return self.async_create_entry(
|
||||
title="", data={CONF_SOURCES: _sources_from_config(user_input)}
|
||||
)
|
||||
|
||||
previous_sources = self._previous_sources()
|
||||
|
||||
options = {
|
||||
_key_for_source(idx + 1, source, previous_sources): str
|
||||
for idx, source in enumerate(SOURCES)
|
||||
}
|
||||
|
||||
return self.async_show_form(step_id="init", data_schema=vol.Schema(options),)
|
||||
|
||||
|
||||
class CannotConnect(exceptions.HomeAssistantError):
|
||||
"""Error to indicate we cannot connect."""
|
||||
|
@ -1,6 +1,7 @@
|
||||
"""Support for interfacing with Monoprice 6 zone home audio controller."""
|
||||
import logging
|
||||
|
||||
from homeassistant import core
|
||||
from homeassistant.components.media_player import MediaPlayerDevice
|
||||
from homeassistant.components.media_player.const import (
|
||||
SUPPORT_SELECT_SOURCE,
|
||||
@ -27,7 +28,10 @@ SUPPORT_MONOPRICE = (
|
||||
)
|
||||
|
||||
|
||||
def _get_sources(sources_config):
|
||||
@core.callback
|
||||
def _get_sources_from_dict(data):
|
||||
sources_config = data[CONF_SOURCES]
|
||||
|
||||
source_id_name = {int(index): name for index, name in sources_config.items()}
|
||||
|
||||
source_name_id = {v: k for k, v in source_id_name.items()}
|
||||
@ -37,13 +41,22 @@ def _get_sources(sources_config):
|
||||
return [source_id_name, source_name_id, source_names]
|
||||
|
||||
|
||||
@core.callback
|
||||
def _get_sources(config_entry):
|
||||
if CONF_SOURCES in config_entry.options:
|
||||
data = config_entry.options
|
||||
else:
|
||||
data = config_entry.data
|
||||
return _get_sources_from_dict(data)
|
||||
|
||||
|
||||
async def async_setup_entry(hass, config_entry, async_add_entities):
|
||||
"""Set up the Monoprice 6-zone amplifier platform."""
|
||||
port = config_entry.data[CONF_PORT]
|
||||
|
||||
monoprice = hass.data[DOMAIN][config_entry.entry_id]
|
||||
|
||||
sources = _get_sources(config_entry.data.get(CONF_SOURCES))
|
||||
sources = _get_sources(config_entry)
|
||||
|
||||
entities = []
|
||||
for i in range(1, 4):
|
||||
|
@ -22,5 +22,20 @@
|
||||
"abort": {
|
||||
"already_configured": "Device is already configured"
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"step": {
|
||||
"init": {
|
||||
"title": "Configure sources",
|
||||
"data": {
|
||||
"source_1": "Name of source #1",
|
||||
"source_2": "Name of source #2",
|
||||
"source_3": "Name of source #3",
|
||||
"source_4": "Name of source #4",
|
||||
"source_5": "Name of source #5",
|
||||
"source_6": "Name of source #6"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -2,7 +2,7 @@
|
||||
from asynctest import patch
|
||||
from serial import SerialException
|
||||
|
||||
from homeassistant import config_entries, setup
|
||||
from homeassistant import config_entries, data_entry_flow, setup
|
||||
from homeassistant.components.monoprice.const import (
|
||||
CONF_SOURCE_1,
|
||||
CONF_SOURCE_4,
|
||||
@ -12,6 +12,8 @@ from homeassistant.components.monoprice.const import (
|
||||
)
|
||||
from homeassistant.const import CONF_PORT
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
|
||||
CONFIG = {
|
||||
CONF_PORT: "/test/port",
|
||||
CONF_SOURCE_1: "one",
|
||||
@ -45,7 +47,7 @@ async def test_form(hass):
|
||||
assert result2["title"] == CONFIG[CONF_PORT]
|
||||
assert result2["data"] == {
|
||||
CONF_PORT: CONFIG[CONF_PORT],
|
||||
CONF_SOURCES: {1: CONFIG[CONF_SOURCE_1], 4: CONFIG[CONF_SOURCE_4]},
|
||||
CONF_SOURCES: {"1": CONFIG[CONF_SOURCE_1], "4": CONFIG[CONF_SOURCE_4]},
|
||||
}
|
||||
await hass.async_block_till_done()
|
||||
assert len(mock_setup.mock_calls) == 1
|
||||
@ -86,3 +88,32 @@ async def test_generic_exception(hass):
|
||||
|
||||
assert result2["type"] == "form"
|
||||
assert result2["errors"] == {"base": "unknown"}
|
||||
|
||||
|
||||
async def test_options_flow(hass):
|
||||
"""Test config flow options."""
|
||||
conf = {CONF_PORT: "/test/port", CONF_SOURCES: {"4": "four"}}
|
||||
|
||||
config_entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
# unique_id="abcde12345",
|
||||
data=conf,
|
||||
# options={CONF_SHOW_ON_MAP: True},
|
||||
)
|
||||
config_entry.add_to_hass(hass)
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.monoprice.async_setup_entry", return_value=True
|
||||
):
|
||||
result = await hass.config_entries.options.async_init(config_entry.entry_id)
|
||||
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||
assert result["step_id"] == "init"
|
||||
|
||||
result = await hass.config_entries.options.async_configure(
|
||||
result["flow_id"],
|
||||
user_input={CONF_SOURCE_1: "one", CONF_SOURCE_4: "", CONF_SOURCE_5: "five"},
|
||||
)
|
||||
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
|
||||
assert config_entry.options[CONF_SOURCES] == {"1": "one", "5": "five"}
|
||||
|
@ -37,6 +37,7 @@ from homeassistant.helpers.entity_component import async_update_entity
|
||||
from tests.common import MockConfigEntry
|
||||
|
||||
MOCK_CONFIG = {CONF_PORT: "fake port", CONF_SOURCES: {"1": "one", "3": "three"}}
|
||||
MOCK_OPTIONS = {CONF_SOURCES: {"2": "two", "4": "four"}}
|
||||
|
||||
ZONE_1_ID = "media_player.zone_11"
|
||||
ZONE_2_ID = "media_player.zone_12"
|
||||
@ -117,6 +118,20 @@ async def _setup_monoprice(hass, monoprice):
|
||||
await hass.async_block_till_done()
|
||||
|
||||
|
||||
async def _setup_monoprice_with_options(hass, monoprice):
|
||||
with patch(
|
||||
"homeassistant.components.monoprice.get_monoprice", new=lambda *a: monoprice,
|
||||
):
|
||||
config_entry = MockConfigEntry(
|
||||
domain=DOMAIN, data=MOCK_CONFIG, options=MOCK_OPTIONS
|
||||
)
|
||||
config_entry.add_to_hass(hass)
|
||||
await hass.config_entries.async_setup(config_entry.entry_id)
|
||||
# setup_component(self.hass, DOMAIN, MOCK_CONFIG)
|
||||
# self.hass.async_block_till_done()
|
||||
await hass.async_block_till_done()
|
||||
|
||||
|
||||
async def _call_media_player_service(hass, name, data):
|
||||
await hass.services.async_call(
|
||||
MEDIA_PLAYER_DOMAIN, name, service_data=data, blocking=True
|
||||
@ -256,7 +271,6 @@ async def test_restore_without_snapshort(hass):
|
||||
|
||||
async def test_update(hass):
|
||||
"""Test updating values from monoprice."""
|
||||
"""Test snapshot save/restore service calls."""
|
||||
monoprice = MockMonoprice()
|
||||
await _setup_monoprice(hass, monoprice)
|
||||
|
||||
@ -305,6 +319,15 @@ async def test_source_list(hass):
|
||||
assert ["one", "three"] == state.attributes[ATTR_INPUT_SOURCE_LIST]
|
||||
|
||||
|
||||
async def test_source_list_with_options(hass):
|
||||
"""Test source list property."""
|
||||
await _setup_monoprice_with_options(hass, MockMonoprice())
|
||||
|
||||
state = hass.states.get(ZONE_1_ID)
|
||||
# Note, the list is sorted!
|
||||
assert ["two", "four"] == state.attributes[ATTR_INPUT_SOURCE_LIST]
|
||||
|
||||
|
||||
async def test_select_source(hass):
|
||||
"""Test source selection methods."""
|
||||
monoprice = MockMonoprice()
|
||||
|
Loading…
x
Reference in New Issue
Block a user