Add RainMachine config option to use default run times from app (#80984)

pull/81062/head
shbatm 2022-10-26 23:27:08 -05:00 committed by GitHub
parent f9c16fab1a
commit e785d04abf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 74 additions and 24 deletions

View File

@ -38,7 +38,9 @@ from homeassistant.util.network import is_ip_address
from .config_flow import get_client_controller from .config_flow import get_client_controller
from .const import ( from .const import (
CONF_ZONE_RUN_TIME, CONF_DEFAULT_ZONE_RUN_TIME,
CONF_DURATION,
CONF_USE_APP_RUN_TIMES,
DATA_API_VERSIONS, DATA_API_VERSIONS,
DATA_MACHINE_FIRMWARE_UPDATE_STATUS, DATA_MACHINE_FIRMWARE_UPDATE_STATUS,
DATA_PROGRAMS, DATA_PROGRAMS,
@ -67,7 +69,6 @@ PLATFORMS = [
CONF_CONDITION = "condition" CONF_CONDITION = "condition"
CONF_DEWPOINT = "dewpoint" CONF_DEWPOINT = "dewpoint"
CONF_DURATION = "duration"
CONF_ET = "et" CONF_ET = "et"
CONF_MAXRH = "maxrh" CONF_MAXRH = "maxrh"
CONF_MAXTEMP = "maxtemp" 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 not entry.unique_id or is_ip_address(entry.unique_id):
# If the config entry doesn't already have a unique ID, set one: # If the config entry doesn't already have a unique ID, set one:
entry_updates["unique_id"] = controller.mac 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 # If a zone run time exists in the config entry's data, pop it and move it to
# options: # options:
data = {**entry.data} data = {**entry.data}
entry_updates["data"] = data entry_updates["data"] = data
entry_updates["options"] = { entry_updates["options"] = {
**entry.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: if entry_updates:
hass.config_entries.async_update_entry(entry, **entry_updates) hass.config_entries.async_update_entry(entry, **entry_updates)

View File

@ -16,7 +16,13 @@ from homeassistant.core import HomeAssistant, callback
from homeassistant.data_entry_flow import FlowResult from homeassistant.data_entry_flow import FlowResult
from homeassistant.helpers import aiohttp_client, config_validation as cv 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 @callback
@ -138,8 +144,8 @@ class RainMachineFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
CONF_PASSWORD: user_input[CONF_PASSWORD], CONF_PASSWORD: user_input[CONF_PASSWORD],
CONF_PORT: user_input[CONF_PORT], CONF_PORT: user_input[CONF_PORT],
CONF_SSL: user_input.get(CONF_SSL, True), CONF_SSL: user_input.get(CONF_SSL, True),
CONF_ZONE_RUN_TIME: user_input.get( CONF_DEFAULT_ZONE_RUN_TIME: user_input.get(
CONF_ZONE_RUN_TIME, DEFAULT_ZONE_RUN CONF_DEFAULT_ZONE_RUN_TIME, DEFAULT_ZONE_RUN
), ),
}, },
) )
@ -173,9 +179,15 @@ class RainMachineOptionsFlowHandler(config_entries.OptionsFlow):
data_schema=vol.Schema( data_schema=vol.Schema(
{ {
vol.Optional( vol.Optional(
CONF_ZONE_RUN_TIME, CONF_DEFAULT_ZONE_RUN_TIME,
default=self.config_entry.options.get(CONF_ZONE_RUN_TIME), default=self.config_entry.options.get(
): cv.positive_int 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,
} }
), ),
) )

View File

@ -5,7 +5,9 @@ LOGGER = logging.getLogger(__package__)
DOMAIN = "rainmachine" 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_API_VERSIONS = "api.versions"
DATA_MACHINE_FIRMWARE_UPDATE_STATUS = "machine.firmware_update_status" DATA_MACHINE_FIRMWARE_UPDATE_STATUS = "machine.firmware_update_status"

View File

@ -23,7 +23,8 @@
"init": { "init": {
"title": "Configure RainMachine", "title": "Configure RainMachine",
"data": { "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"
} }
} }
} }

View File

@ -22,8 +22,11 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
from . import RainMachineData, RainMachineEntity, async_update_programs_and_zones from . import RainMachineData, RainMachineEntity, async_update_programs_and_zones
from .const import ( from .const import (
CONF_ZONE_RUN_TIME, CONF_DEFAULT_ZONE_RUN_TIME,
CONF_DURATION,
CONF_USE_APP_RUN_TIMES,
DATA_PROGRAMS, DATA_PROGRAMS,
DATA_PROVISION_SETTINGS,
DATA_RESTRICTIONS_UNIVERSAL, DATA_RESTRICTIONS_UNIVERSAL,
DATA_ZONES, DATA_ZONES,
DEFAULT_ZONE_RUN, DEFAULT_ZONE_RUN,
@ -40,6 +43,7 @@ ATTR_AREA = "area"
ATTR_CS_ON = "cs_on" ATTR_CS_ON = "cs_on"
ATTR_CURRENT_CYCLE = "current_cycle" ATTR_CURRENT_CYCLE = "current_cycle"
ATTR_CYCLES = "cycles" ATTR_CYCLES = "cycles"
ATTR_ZONE_RUN_TIME = "zone_run_time_from_app"
ATTR_DELAY = "delay" ATTR_DELAY = "delay"
ATTR_DELAY_ON = "delay_on" ATTR_DELAY_ON = "delay_on"
ATTR_FIELD_CAPACITY = "field_capacity" ATTR_FIELD_CAPACITY = "field_capacity"
@ -186,7 +190,7 @@ async def async_setup_entry(
"start_zone", "start_zone",
{ {
vol.Optional( vol.Optional(
CONF_ZONE_RUN_TIME, default=DEFAULT_ZONE_RUN CONF_DEFAULT_ZONE_RUN_TIME, default=DEFAULT_ZONE_RUN
): cv.positive_int ): cv.positive_int
}, },
"async_start_zone", "async_start_zone",
@ -459,9 +463,21 @@ class RainMachineZone(RainMachineActivitySwitch):
@raise_on_request_error @raise_on_request_error
async def async_turn_on_when_active(self, **kwargs: Any) -> None: async def async_turn_on_when_active(self, **kwargs: Any) -> None:
"""Turn the switch on when its associated activity is active.""" """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( await self._data.controller.zones.start(
self.entity_description.uid, self.entity_description.uid,
kwargs.get("duration", self._entry.options[CONF_ZONE_RUN_TIME]), duration,
) )
self._update_activities() self._update_activities()
@ -497,6 +513,13 @@ class RainMachineZone(RainMachineActivitySwitch):
data["waterSense"]["precipitationRate"], 2 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) self._attr_extra_state_attributes.update(attrs)

View File

@ -35,10 +35,11 @@
"step": { "step": {
"init": { "init": {
"data": { "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" "title": "Configure RainMachine"
} }
} }
} }
} }

View File

@ -6,7 +6,11 @@ from regenmaschine.errors import RainMachineError
from homeassistant import config_entries, data_entry_flow, setup from homeassistant import config_entries, data_entry_flow, setup
from homeassistant.components import zeroconf 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.const import CONF_IP_ADDRESS, CONF_PASSWORD, CONF_PORT, CONF_SSL
from homeassistant.helpers import entity_registry as er 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" assert result["step_id"] == "init"
result = await hass.config_entries.options.async_configure( 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 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): async def test_show_form(hass):
@ -130,7 +138,7 @@ async def test_step_user(hass, config, setup_rainmachine):
CONF_PASSWORD: "password", CONF_PASSWORD: "password",
CONF_PORT: 8080, CONF_PORT: 8080,
CONF_SSL: True, 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_PASSWORD: "password",
CONF_PORT: 8080, CONF_PORT: 8080,
CONF_SSL: True, CONF_SSL: True,
CONF_ZONE_RUN_TIME: 600, CONF_DEFAULT_ZONE_RUN_TIME: 600,
} }

View File

@ -20,7 +20,7 @@ async def test_entry_diagnostics(hass, config_entry, hass_client, setup_rainmach
"port": 8080, "port": 8080,
"ssl": True, "ssl": True,
}, },
"options": {}, "options": {"use_app_run_times": False},
"pref_disable_new_entities": False, "pref_disable_new_entities": False,
"pref_disable_polling": False, "pref_disable_polling": False,
"source": "user", "source": "user",
@ -642,7 +642,7 @@ async def test_entry_diagnostics_failed_controller_diagnostics(
"port": 8080, "port": 8080,
"ssl": True, "ssl": True,
}, },
"options": {}, "options": {"use_app_run_times": False},
"pref_disable_new_entities": False, "pref_disable_new_entities": False,
"pref_disable_polling": False, "pref_disable_polling": False,
"source": "user", "source": "user",