diff --git a/homeassistant/components/rainmachine/__init__.py b/homeassistant/components/rainmachine/__init__.py index 5955cab7a04713e811487ae91473bc0dcf104335..ccabc0bc55efa5b6bd52ecccbb3a02c58f70132b 100644 --- a/homeassistant/components/rainmachine/__init__.py +++ b/homeassistant/components/rainmachine/__init__.py @@ -83,10 +83,21 @@ async def async_setup(hass, config): async def async_setup_entry(hass, config_entry): """Set up RainMachine as config entry.""" + entry_updates = {} if not config_entry.unique_id: - hass.config_entries.async_update_entry( - config_entry, unique_id=config_entry.data[CONF_IP_ADDRESS] - ) + # If the config entry doesn't already have a unique ID, set one: + 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) @@ -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 # we loaded above: controller = next(iter(client.controllers.values())) - - rainmachine = RainMachine( - hass, - controller, - config_entry.data.get(CONF_ZONE_RUN_TIME, DEFAULT_ZONE_RUN), - ) + rainmachine = RainMachine(hass, config_entry, controller) # 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: @@ -207,6 +213,8 @@ async def async_setup_entry(hass, config_entry): ]: hass.services.async_register(DOMAIN, service, method, schema=schema) + config_entry.add_update_listener(async_reload_entry) + return True @@ -224,15 +232,20 @@ async def async_unload_entry(hass, config_entry): 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: """Define a generic RainMachine object.""" - def __init__(self, hass, controller, default_zone_runtime): + def __init__(self, hass, config_entry, controller): """Initialize.""" self._async_cancel_time_interval_listener = None + self.config_entry = config_entry self.controller = controller self.data = {} - self.default_zone_runtime = default_zone_runtime self.device_mac = controller.mac self.hass = hass diff --git a/homeassistant/components/rainmachine/config_flow.py b/homeassistant/components/rainmachine/config_flow.py index 9c32be62cd2881603cadd496b11d335c1e3cab43..49eba95d0474db619b756e461e795f1d329db7af 100644 --- a/homeassistant/components/rainmachine/config_flow.py +++ b/homeassistant/components/rainmachine/config_flow.py @@ -5,7 +5,8 @@ import voluptuous as vol from homeassistant import config_entries 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 CONF_ZONE_RUN_TIME, @@ -39,6 +40,12 @@ class RainMachineFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): 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): """Handle the start of the config flow.""" 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 + } + ), + ) diff --git a/homeassistant/components/rainmachine/strings.json b/homeassistant/components/rainmachine/strings.json index d0a6adf468736c2bc36ebde5eff4bfc88b89d5fb..1f5a21d37d807e777542c2a068ee0af03152e5e1 100644 --- a/homeassistant/components/rainmachine/strings.json +++ b/homeassistant/components/rainmachine/strings.json @@ -16,5 +16,15 @@ "abort": { "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)" + } + } + } } } diff --git a/homeassistant/components/rainmachine/switch.py b/homeassistant/components/rainmachine/switch.py index c0dc450ee2bd7f423ad5ea7988ab16cbcb4e13cf..f31d6386e065bd2f98a1107a64d6706108e8d113 100644 --- a/homeassistant/components/rainmachine/switch.py +++ b/homeassistant/components/rainmachine/switch.py @@ -11,6 +11,7 @@ from homeassistant.helpers.dispatcher import async_dispatcher_connect from . import RainMachineEntity from .const import ( + CONF_ZONE_RUN_TIME, DATA_CLIENT, DATA_PROGRAMS, DATA_ZONES, @@ -268,7 +269,8 @@ class RainMachineZone(RainMachineSwitch): """Turn the zone on.""" await self._async_run_switch_coroutine( 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], ) ) diff --git a/homeassistant/components/rainmachine/translations/en.json b/homeassistant/components/rainmachine/translations/en.json index d734cbd5d388e17ebaf26dddd7b5817599008f1e..f65463626e46f52420b51e96282491935ede8568 100644 --- a/homeassistant/components/rainmachine/translations/en.json +++ b/homeassistant/components/rainmachine/translations/en.json @@ -4,9 +4,7 @@ "already_configured": "Device is already configured" }, "error": { - "identifier_exists": "Account is already configured", - "invalid_auth": "Invalid authentication", - "invalid_credentials": "Invalid credentials" + "invalid_auth": "Invalid authentication" }, "step": { "user": { @@ -18,5 +16,15 @@ "title": "Fill in your information" } } + }, + "options": { + "step": { + "init": { + "data": { + "zone_run_time": "Default zone run time (in seconds)" + }, + "title": "Configure RainMachine" + } + } } } \ No newline at end of file diff --git a/tests/components/rainmachine/test_config_flow.py b/tests/components/rainmachine/test_config_flow.py index 26e5ecba8baaf3c3ab83a8eb2777c878e48c74f2..1bfd412c3c7003c352c13938f188e50c52244d6a 100644 --- a/tests/components/rainmachine/test_config_flow.py +++ b/tests/components/rainmachine/test_config_flow.py @@ -51,6 +51,39 @@ async def test_invalid_password(hass): 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): """Test that the form is served with no input.""" flow = config_flow.RainMachineFlowHandler()