From e785d04abf0fb447d7edf7ff696973c320a7ab79 Mon Sep 17 00:00:00 2001 From: shbatm Date: Wed, 26 Oct 2022 23:27:08 -0500 Subject: [PATCH] Add RainMachine config option to use default run times from app (#80984) --- .../components/rainmachine/__init__.py | 11 ++++--- .../components/rainmachine/config_flow.py | 24 +++++++++++---- homeassistant/components/rainmachine/const.py | 4 ++- .../components/rainmachine/strings.json | 3 +- .../components/rainmachine/switch.py | 29 +++++++++++++++++-- .../rainmachine/translations/en.json | 5 ++-- .../rainmachine/test_config_flow.py | 18 ++++++++---- .../rainmachine/test_diagnostics.py | 4 +-- 8 files changed, 74 insertions(+), 24 deletions(-) diff --git a/homeassistant/components/rainmachine/__init__.py b/homeassistant/components/rainmachine/__init__.py index a0b11653272..321a3a057af 100644 --- a/homeassistant/components/rainmachine/__init__.py +++ b/homeassistant/components/rainmachine/__init__.py @@ -38,7 +38,9 @@ from homeassistant.util.network import is_ip_address from .config_flow import get_client_controller from .const import ( - CONF_ZONE_RUN_TIME, + CONF_DEFAULT_ZONE_RUN_TIME, + CONF_DURATION, + CONF_USE_APP_RUN_TIMES, DATA_API_VERSIONS, DATA_MACHINE_FIRMWARE_UPDATE_STATUS, DATA_PROGRAMS, @@ -67,7 +69,6 @@ PLATFORMS = [ CONF_CONDITION = "condition" CONF_DEWPOINT = "dewpoint" -CONF_DURATION = "duration" CONF_ET = "et" CONF_MAXRH = "maxrh" CONF_MAXTEMP = "maxtemp" @@ -237,15 +238,17 @@ async def async_setup_entry( # noqa: C901 if not entry.unique_id or is_ip_address(entry.unique_id): # If the config entry doesn't already have a unique ID, set one: entry_updates["unique_id"] = controller.mac - if CONF_ZONE_RUN_TIME in entry.data: + if CONF_DEFAULT_ZONE_RUN_TIME in entry.data: # If a zone run time exists in the config entry's data, pop it and move it to # options: data = {**entry.data} entry_updates["data"] = data entry_updates["options"] = { **entry.options, - CONF_ZONE_RUN_TIME: data.pop(CONF_ZONE_RUN_TIME), + CONF_DEFAULT_ZONE_RUN_TIME: data.pop(CONF_DEFAULT_ZONE_RUN_TIME), } + if CONF_USE_APP_RUN_TIMES not in entry.options: + entry_updates["options"] = {**entry.options, CONF_USE_APP_RUN_TIMES: False} if entry_updates: hass.config_entries.async_update_entry(entry, **entry_updates) diff --git a/homeassistant/components/rainmachine/config_flow.py b/homeassistant/components/rainmachine/config_flow.py index eed80b9c145..b5ae42559bb 100644 --- a/homeassistant/components/rainmachine/config_flow.py +++ b/homeassistant/components/rainmachine/config_flow.py @@ -16,7 +16,13 @@ from homeassistant.core import HomeAssistant, callback from homeassistant.data_entry_flow import FlowResult from homeassistant.helpers import aiohttp_client, config_validation as cv -from .const import CONF_ZONE_RUN_TIME, DEFAULT_PORT, DEFAULT_ZONE_RUN, DOMAIN +from .const import ( + CONF_DEFAULT_ZONE_RUN_TIME, + CONF_USE_APP_RUN_TIMES, + DEFAULT_PORT, + DEFAULT_ZONE_RUN, + DOMAIN, +) @callback @@ -138,8 +144,8 @@ class RainMachineFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): CONF_PASSWORD: user_input[CONF_PASSWORD], CONF_PORT: user_input[CONF_PORT], CONF_SSL: user_input.get(CONF_SSL, True), - CONF_ZONE_RUN_TIME: user_input.get( - CONF_ZONE_RUN_TIME, DEFAULT_ZONE_RUN + CONF_DEFAULT_ZONE_RUN_TIME: user_input.get( + CONF_DEFAULT_ZONE_RUN_TIME, DEFAULT_ZONE_RUN ), }, ) @@ -173,9 +179,15 @@ class RainMachineOptionsFlowHandler(config_entries.OptionsFlow): data_schema=vol.Schema( { vol.Optional( - CONF_ZONE_RUN_TIME, - default=self.config_entry.options.get(CONF_ZONE_RUN_TIME), - ): cv.positive_int + CONF_DEFAULT_ZONE_RUN_TIME, + default=self.config_entry.options.get( + CONF_DEFAULT_ZONE_RUN_TIME + ), + ): cv.positive_int, + vol.Optional( + CONF_USE_APP_RUN_TIMES, + default=self.config_entry.options.get(CONF_USE_APP_RUN_TIMES), + ): bool, } ), ) diff --git a/homeassistant/components/rainmachine/const.py b/homeassistant/components/rainmachine/const.py index d1b5bd9bd52..00af0bd0b75 100644 --- a/homeassistant/components/rainmachine/const.py +++ b/homeassistant/components/rainmachine/const.py @@ -5,7 +5,9 @@ LOGGER = logging.getLogger(__package__) DOMAIN = "rainmachine" -CONF_ZONE_RUN_TIME = "zone_run_time" +CONF_DURATION = "duration" +CONF_DEFAULT_ZONE_RUN_TIME = "zone_run_time" +CONF_USE_APP_RUN_TIMES = "use_app_run_times" DATA_API_VERSIONS = "api.versions" DATA_MACHINE_FIRMWARE_UPDATE_STATUS = "machine.firmware_update_status" diff --git a/homeassistant/components/rainmachine/strings.json b/homeassistant/components/rainmachine/strings.json index 7634c0a69c5..9991fd31e03 100644 --- a/homeassistant/components/rainmachine/strings.json +++ b/homeassistant/components/rainmachine/strings.json @@ -23,7 +23,8 @@ "init": { "title": "Configure RainMachine", "data": { - "zone_run_time": "Default zone run time (in seconds)" + "zone_run_time": "Default zone run time (in seconds)", + "use_app_run_times": "Use zone run times from RainMachine app" } } } diff --git a/homeassistant/components/rainmachine/switch.py b/homeassistant/components/rainmachine/switch.py index db560c3c64c..35b0e5eab2b 100644 --- a/homeassistant/components/rainmachine/switch.py +++ b/homeassistant/components/rainmachine/switch.py @@ -22,8 +22,11 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback from . import RainMachineData, RainMachineEntity, async_update_programs_and_zones from .const import ( - CONF_ZONE_RUN_TIME, + CONF_DEFAULT_ZONE_RUN_TIME, + CONF_DURATION, + CONF_USE_APP_RUN_TIMES, DATA_PROGRAMS, + DATA_PROVISION_SETTINGS, DATA_RESTRICTIONS_UNIVERSAL, DATA_ZONES, DEFAULT_ZONE_RUN, @@ -40,6 +43,7 @@ ATTR_AREA = "area" ATTR_CS_ON = "cs_on" ATTR_CURRENT_CYCLE = "current_cycle" ATTR_CYCLES = "cycles" +ATTR_ZONE_RUN_TIME = "zone_run_time_from_app" ATTR_DELAY = "delay" ATTR_DELAY_ON = "delay_on" ATTR_FIELD_CAPACITY = "field_capacity" @@ -186,7 +190,7 @@ async def async_setup_entry( "start_zone", { vol.Optional( - CONF_ZONE_RUN_TIME, default=DEFAULT_ZONE_RUN + CONF_DEFAULT_ZONE_RUN_TIME, default=DEFAULT_ZONE_RUN ): cv.positive_int }, "async_start_zone", @@ -459,9 +463,21 @@ class RainMachineZone(RainMachineActivitySwitch): @raise_on_request_error async def async_turn_on_when_active(self, **kwargs: Any) -> None: """Turn the switch on when its associated activity is active.""" + # 1. Use duration parameter if provided from service call + duration = kwargs.get(CONF_DURATION) + if not duration: + if ( + self._entry.options[CONF_USE_APP_RUN_TIMES] + and ATTR_ZONE_RUN_TIME in self._attr_extra_state_attributes + ): + # 2. Use app's zone-specific default, if enabled and available + duration = self._attr_extra_state_attributes[ATTR_ZONE_RUN_TIME] + else: + # 3. Fall back to global zone default duration + duration = self._entry.options[CONF_DEFAULT_ZONE_RUN_TIME] await self._data.controller.zones.start( self.entity_description.uid, - kwargs.get("duration", self._entry.options[CONF_ZONE_RUN_TIME]), + duration, ) self._update_activities() @@ -497,6 +513,13 @@ class RainMachineZone(RainMachineActivitySwitch): data["waterSense"]["precipitationRate"], 2 ) + if self._entry.options[CONF_USE_APP_RUN_TIMES]: + provision_data = self._data.coordinators[DATA_PROVISION_SETTINGS].data + if zone_durations := provision_data.get("system", {}).get("zoneDuration"): + attrs[ATTR_ZONE_RUN_TIME] = zone_durations[ + list(self.coordinator.data).index(self.entity_description.uid) + ] + self._attr_extra_state_attributes.update(attrs) diff --git a/homeassistant/components/rainmachine/translations/en.json b/homeassistant/components/rainmachine/translations/en.json index 3e5d824ee08..15155f52d08 100644 --- a/homeassistant/components/rainmachine/translations/en.json +++ b/homeassistant/components/rainmachine/translations/en.json @@ -35,10 +35,11 @@ "step": { "init": { "data": { - "zone_run_time": "Default zone run time (in seconds)" + "zone_run_time": "Default zone run time (in seconds)", + "use_app_run_times": "Use zone run times from RainMachine app" }, "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 deb03d65cb5..5f5b769b6dd 100644 --- a/tests/components/rainmachine/test_config_flow.py +++ b/tests/components/rainmachine/test_config_flow.py @@ -6,7 +6,11 @@ from regenmaschine.errors import RainMachineError from homeassistant import config_entries, data_entry_flow, setup from homeassistant.components import zeroconf -from homeassistant.components.rainmachine import CONF_ZONE_RUN_TIME, DOMAIN +from homeassistant.components.rainmachine import ( + CONF_DEFAULT_ZONE_RUN_TIME, + CONF_USE_APP_RUN_TIMES, + DOMAIN, +) from homeassistant.const import CONF_IP_ADDRESS, CONF_PASSWORD, CONF_PORT, CONF_SSL from homeassistant.helpers import entity_registry as er @@ -99,10 +103,14 @@ async def test_options_flow(hass, config, config_entry): assert result["step_id"] == "init" result = await hass.config_entries.options.async_configure( - result["flow_id"], user_input={CONF_ZONE_RUN_TIME: 600} + result["flow_id"], + user_input={CONF_DEFAULT_ZONE_RUN_TIME: 600, CONF_USE_APP_RUN_TIMES: False}, ) assert result["type"] == data_entry_flow.FlowResultType.CREATE_ENTRY - assert config_entry.options == {CONF_ZONE_RUN_TIME: 600} + assert config_entry.options == { + CONF_DEFAULT_ZONE_RUN_TIME: 600, + CONF_USE_APP_RUN_TIMES: False, + } async def test_show_form(hass): @@ -130,7 +138,7 @@ async def test_step_user(hass, config, setup_rainmachine): CONF_PASSWORD: "password", CONF_PORT: 8080, CONF_SSL: True, - CONF_ZONE_RUN_TIME: 600, + CONF_DEFAULT_ZONE_RUN_TIME: 600, } @@ -238,7 +246,7 @@ async def test_step_homekit_zeroconf_new_controller_when_some_exist( CONF_PASSWORD: "password", CONF_PORT: 8080, CONF_SSL: True, - CONF_ZONE_RUN_TIME: 600, + CONF_DEFAULT_ZONE_RUN_TIME: 600, } diff --git a/tests/components/rainmachine/test_diagnostics.py b/tests/components/rainmachine/test_diagnostics.py index a3c03c956a4..084818eeef1 100644 --- a/tests/components/rainmachine/test_diagnostics.py +++ b/tests/components/rainmachine/test_diagnostics.py @@ -20,7 +20,7 @@ async def test_entry_diagnostics(hass, config_entry, hass_client, setup_rainmach "port": 8080, "ssl": True, }, - "options": {}, + "options": {"use_app_run_times": False}, "pref_disable_new_entities": False, "pref_disable_polling": False, "source": "user", @@ -642,7 +642,7 @@ async def test_entry_diagnostics_failed_controller_diagnostics( "port": 8080, "ssl": True, }, - "options": {}, + "options": {"use_app_run_times": False}, "pref_disable_new_entities": False, "pref_disable_polling": False, "source": "user",