Add options flow to RainMachine (#42241)

* Add options flow to RainMachine

* Linting
This commit is contained in:
Aaron Bach 2020-10-28 15:52:42 -06:00 committed by GitHub
parent aef80263cd
commit e61e8fafee
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 114 additions and 16 deletions

View File

@ -83,10 +83,21 @@ async def async_setup(hass, config):
async def async_setup_entry(hass, config_entry): async def async_setup_entry(hass, config_entry):
"""Set up RainMachine as config entry.""" """Set up RainMachine as config entry."""
entry_updates = {}
if not config_entry.unique_id: if not config_entry.unique_id:
hass.config_entries.async_update_entry( # If the config entry doesn't already have a unique ID, set one:
config_entry, unique_id=config_entry.data[CONF_IP_ADDRESS] entry_updates["unique_id"] = config_entry.data[CONF_IP_ADDRESS]
) if CONF_ZONE_RUN_TIME in config_entry.data:
# If a zone run time exists in the config entry's data, pop it and move it to
# options:
data = {**config_entry.data}
entry_updates["data"] = data
entry_updates["options"] = {
**config_entry.options,
CONF_ZONE_RUN_TIME: data.pop(CONF_ZONE_RUN_TIME),
}
if entry_updates:
hass.config_entries.async_update_entry(config_entry, **entry_updates)
_verify_domain_control = verify_domain_control(hass, DOMAIN) _verify_domain_control = verify_domain_control(hass, DOMAIN)
@ -107,12 +118,7 @@ async def async_setup_entry(hass, config_entry):
# regenmaschine can load multiple controllers at once, but we only grab the one # regenmaschine can load multiple controllers at once, but we only grab the one
# we loaded above: # we loaded above:
controller = next(iter(client.controllers.values())) controller = next(iter(client.controllers.values()))
rainmachine = RainMachine(hass, config_entry, controller)
rainmachine = RainMachine(
hass,
controller,
config_entry.data.get(CONF_ZONE_RUN_TIME, DEFAULT_ZONE_RUN),
)
# Update the data object, which at this point (prior to any sensors registering # Update the data object, which at this point (prior to any sensors registering
# "interest" in the API), will focus on grabbing the latest program and zone data: # "interest" in the API), will focus on grabbing the latest program and zone data:
@ -207,6 +213,8 @@ async def async_setup_entry(hass, config_entry):
]: ]:
hass.services.async_register(DOMAIN, service, method, schema=schema) hass.services.async_register(DOMAIN, service, method, schema=schema)
config_entry.add_update_listener(async_reload_entry)
return True return True
@ -224,15 +232,20 @@ async def async_unload_entry(hass, config_entry):
return True return True
async def async_reload_entry(hass, config_entry):
"""Handle an options update."""
await hass.config_entries.async_reload(config_entry.entry_id)
class RainMachine: class RainMachine:
"""Define a generic RainMachine object.""" """Define a generic RainMachine object."""
def __init__(self, hass, controller, default_zone_runtime): def __init__(self, hass, config_entry, controller):
"""Initialize.""" """Initialize."""
self._async_cancel_time_interval_listener = None self._async_cancel_time_interval_listener = None
self.config_entry = config_entry
self.controller = controller self.controller = controller
self.data = {} self.data = {}
self.default_zone_runtime = default_zone_runtime
self.device_mac = controller.mac self.device_mac = controller.mac
self.hass = hass self.hass = hass

View File

@ -5,7 +5,8 @@ import voluptuous as vol
from homeassistant import config_entries from homeassistant import config_entries
from homeassistant.const import CONF_IP_ADDRESS, CONF_PASSWORD, CONF_PORT, CONF_SSL from homeassistant.const import CONF_IP_ADDRESS, CONF_PASSWORD, CONF_PORT, CONF_SSL
from homeassistant.helpers import aiohttp_client from homeassistant.core import callback
from homeassistant.helpers import aiohttp_client, config_validation as cv
from .const import ( # pylint: disable=unused-import from .const import ( # pylint: disable=unused-import
CONF_ZONE_RUN_TIME, CONF_ZONE_RUN_TIME,
@ -39,6 +40,12 @@ class RainMachineFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
errors=errors if errors else {}, errors=errors if errors else {},
) )
@staticmethod
@callback
def async_get_options_flow(config_entry):
"""Define the config flow to handle options."""
return RainMachineOptionsFlowHandler(config_entry)
async def async_step_user(self, user_input=None): async def async_step_user(self, user_input=None):
"""Handle the start of the config flow.""" """Handle the start of the config flow."""
if not user_input: if not user_input:
@ -75,3 +82,28 @@ class RainMachineFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
), ),
}, },
) )
class RainMachineOptionsFlowHandler(config_entries.OptionsFlow):
"""Handle a RainMachine options flow."""
def __init__(self, config_entry):
"""Initialize."""
self.config_entry = config_entry
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=user_input)
return self.async_show_form(
step_id="init",
data_schema=vol.Schema(
{
vol.Optional(
CONF_ZONE_RUN_TIME,
default=self.config_entry.options.get(CONF_ZONE_RUN_TIME),
): cv.positive_int
}
),
)

View File

@ -16,5 +16,15 @@
"abort": { "abort": {
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]" "already_configured": "[%key:common::config_flow::abort::already_configured_device%]"
} }
},
"options": {
"step": {
"init": {
"title": "Configure RainMachine",
"data": {
"zone_run_time": "Default zone run time (in seconds)"
}
}
}
} }
} }

View File

@ -11,6 +11,7 @@ from homeassistant.helpers.dispatcher import async_dispatcher_connect
from . import RainMachineEntity from . import RainMachineEntity
from .const import ( from .const import (
CONF_ZONE_RUN_TIME,
DATA_CLIENT, DATA_CLIENT,
DATA_PROGRAMS, DATA_PROGRAMS,
DATA_ZONES, DATA_ZONES,
@ -268,7 +269,8 @@ class RainMachineZone(RainMachineSwitch):
"""Turn the zone on.""" """Turn the zone on."""
await self._async_run_switch_coroutine( await self._async_run_switch_coroutine(
self.rainmachine.controller.zones.start( self.rainmachine.controller.zones.start(
self._rainmachine_entity_id, self.rainmachine.default_zone_runtime self._rainmachine_entity_id,
self.rainmachine.config_entry.options[CONF_ZONE_RUN_TIME],
) )
) )

View File

@ -4,9 +4,7 @@
"already_configured": "Device is already configured" "already_configured": "Device is already configured"
}, },
"error": { "error": {
"identifier_exists": "Account is already configured", "invalid_auth": "Invalid authentication"
"invalid_auth": "Invalid authentication",
"invalid_credentials": "Invalid credentials"
}, },
"step": { "step": {
"user": { "user": {
@ -18,5 +16,15 @@
"title": "Fill in your information" "title": "Fill in your information"
} }
} }
},
"options": {
"step": {
"init": {
"data": {
"zone_run_time": "Default zone run time (in seconds)"
},
"title": "Configure RainMachine"
}
}
} }
} }

View File

@ -51,6 +51,39 @@ async def test_invalid_password(hass):
assert result["errors"] == {CONF_PASSWORD: "invalid_auth"} assert result["errors"] == {CONF_PASSWORD: "invalid_auth"}
async def test_options_flow(hass):
"""Test config flow options."""
conf = {
CONF_IP_ADDRESS: "192.168.1.100",
CONF_PASSWORD: "password",
CONF_PORT: 8080,
CONF_SSL: True,
}
config_entry = MockConfigEntry(
domain=DOMAIN,
unique_id="abcde12345",
data=conf,
options={CONF_ZONE_RUN_TIME: 900},
)
config_entry.add_to_hass(hass)
with patch(
"homeassistant.components.rainmachine.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_ZONE_RUN_TIME: 600}
)
assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
assert config_entry.options == {CONF_ZONE_RUN_TIME: 600}
async def test_show_form(hass): async def test_show_form(hass):
"""Test that the form is served with no input.""" """Test that the form is served with no input."""
flow = config_flow.RainMachineFlowHandler() flow = config_flow.RainMachineFlowHandler()