Add RainMachine config option to use default run times from app (#80984)
parent
f9c16fab1a
commit
e785d04abf
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -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,
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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",
|
||||||
|
|
Loading…
Reference in New Issue