Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions homeassistant/components/monoprice/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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)
98 changes: 75 additions & 23 deletions homeassistant/components/monoprice/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,31 @@

_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):
Expand All @@ -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}

Expand Down Expand Up @@ -86,6 +89,55 @@ async def async_step_user(self, user_input=None):
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."""
17 changes: 15 additions & 2 deletions homeassistant/components/monoprice/media_player.py
Original file line number Diff line number Diff line change
@@ -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,
Expand All @@ -27,7 +28,10 @@
)


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()}
Expand All @@ -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):
Expand Down
15 changes: 15 additions & 0 deletions homeassistant/components/monoprice/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
}
}
}
}
35 changes: 33 additions & 2 deletions tests/components/monoprice/test_config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -12,6 +12,8 @@
)
from homeassistant.const import CONF_PORT

from tests.common import MockConfigEntry

CONFIG = {
CONF_PORT: "/test/port",
CONF_SOURCE_1: "one",
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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"}
25 changes: 24 additions & 1 deletion tests/components/monoprice/test_media_player.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
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"
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)

Expand Down Expand Up @@ -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()
Expand Down