Skip to content

Commit

Permalink
Allow manual runs to be queued
Browse files Browse the repository at this point in the history
  • Loading branch information
rgc99 committed Oct 5, 2023
1 parent eaad8bc commit 235d32d
Show file tree
Hide file tree
Showing 8 changed files with 548 additions and 303 deletions.
2 changes: 2 additions & 0 deletions custom_components/irrigation_unlimited/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,8 @@
CONF_FROM = "from"
CONF_VOLUME = "volume"
CONF_PRECISION = "precision"
CONF_QUEUE = "queue"
CONF_QUEUE_MANUAL = "queue_manual"

# Defaults
DEFAULT_NAME = DOMAIN
Expand Down
43 changes: 27 additions & 16 deletions custom_components/irrigation_unlimited/irrigation_unlimited.py
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,8 @@
CONF_FROM,
CONF_VOLUME,
CONF_PRECISION,
CONF_QUEUE,
CONF_QUEUE_MANUAL,
)

_LOGGER: Logger = getLogger(__package__)
Expand Down Expand Up @@ -1081,13 +1083,6 @@ def find_last_run(self, schedule: IUSchedule) -> IURun:
return self[i]
return None

def find_manual(self) -> IURun:
"""Find any manual run"""
for run in self:
if run.is_manual():
return run
return None

def last_time(self, stime: datetime) -> datetime:
"""Return the further most look ahead date"""
return stime + self._future_span
Expand Down Expand Up @@ -1254,7 +1249,7 @@ def add(
)

def add_manual(
self, start_time: datetime, duration: timedelta, zone: "IUZone"
self, start_time: datetime, duration: timedelta, zone: "IUZone", queue: bool
) -> IURun:
"""Add a manual run to the queue. Cancel any existing
manual or running schedule"""
Expand All @@ -1263,11 +1258,9 @@ def add_manual(
self.pop(0)

# Remove any existing manual schedules
while True:
run = self.find_manual()
if run is None:
break
self.remove(run)
if not queue:
for manual in (run for run in self if run.is_manual()):
self.remove(manual)

self._current_run = None
self._next_run = None
Expand Down Expand Up @@ -1811,15 +1804,18 @@ def service_manual_run(self, data: MappingProxyType, stime: datetime) -> None:
"""Add a manual run."""
if self._allow_manual or (self.is_enabled and self._controller.is_enabled):
duration = wash_td(data.get(CONF_TIME))
delay = wash_td(data.get(CONF_DELAY, timedelta(0)))
queue = data.get(CONF_QUEUE, self._controller.queue_manual)
if duration is None or duration == timedelta(0):
duration = self._duration
if duration is None:
return
duration = self._adjustment.adjust(duration)
self._run_queue.add_manual(
self._controller.manual_run_start(stime),
self._controller.manual_run_start(stime, delay, queue),
duration,
self,
queue,
)

def service_cancel(self, data: MappingProxyType, stime: datetime) -> None:
Expand Down Expand Up @@ -3054,6 +3050,7 @@ def __init__(
self._controller_id: str = None
self._preamble: timedelta = None
self._postamble: timedelta = None
self._queue_manual: bool = False
# Private variables
self._initialised: bool = False
self._finalised: bool = False
Expand Down Expand Up @@ -3165,6 +3162,11 @@ def preamble(self) -> timedelta:
"""Return the preamble time for the controller"""
return self._preamble

@property
def queue_manual(self) -> bool:
"""Return if manual runs should be queue"""
return self._queue_manual

@property
def status(self) -> str:
"""Return the state of the controller"""
Expand Down Expand Up @@ -3281,6 +3283,7 @@ def load(self, config: OrderedDict) -> "IUController":
self._controller_id = config.get(CONF_CONTROLLER_ID, str(self.index + 1))
self._preamble = wash_td(config.get(CONF_PREAMBLE))
self._postamble = wash_td(config.get(CONF_POSTAMBLE))
self._queue_manual = config.get(CONF_QUEUE_MANUAL, self._queue_manual)
all_zones = config.get(CONF_ALL_ZONES_CONFIG)
zidx: int = 0
for zidx, zone_config in enumerate(config[CONF_ZONES]):
Expand Down Expand Up @@ -3650,7 +3653,9 @@ def decode_sequence_id(
self._coordinator.logger.log_invalid_sequence(stime, self, sequence_id)
return sequence_list

def manual_run_start(self, stime: datetime) -> datetime:
def manual_run_start(
self, stime: datetime, delay: timedelta, queue: bool
) -> datetime:
"""Determine the next available start time for a manual run"""
nst = wash_dt(stime)
if not self._is_on:
Expand All @@ -3661,6 +3666,10 @@ def manual_run_start(self, stime: datetime) -> datetime:
and not self.is_on
):
nst += self.preamble
if queue:
end_times = [run.end_time for run in self._run_queue if run.is_manual()]
if len(end_times) > 0:
nst = max(end_times) + delay
return nst

def suspend_until_date(
Expand Down Expand Up @@ -3799,10 +3808,12 @@ def service_manual_run(self, data: MappingProxyType, stime: datetime) -> None:
sequence = self.get_sequence(sequence_id - 1)
if sequence is not None:
duration = wash_td(data.get(CONF_TIME))
delay = wash_td(data.get(CONF_DELAY, timedelta(0)))
queue = data.get(CONF_QUEUE, self._queue_manual)
if duration is not None and duration == timedelta(0):
duration = None
self.muster_sequence(
self.manual_run_start(stime), sequence, None, duration
self.manual_run_start(stime, delay, queue), sequence, None, duration
)
else:
self._coordinator.logger.log_invalid_sequence(stime, self, sequence_id)
Expand Down
5 changes: 5 additions & 0 deletions custom_components/irrigation_unlimited/schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,8 @@
CONF_FROM,
CONF_VOLUME,
CONF_PRECISION,
CONF_QUEUE,
CONF_QUEUE_MANUAL,
)

IU_ID = r"^[a-z0-9]+(_[a-z0-9]+)*$"
Expand Down Expand Up @@ -276,6 +278,7 @@ def _parse_dd_mmm(value: str) -> date | None:
vol.Optional(CONF_POSTAMBLE): cv.time_period,
vol.Optional(CONF_ENABLED): cv.boolean,
vol.Optional(CONF_ALL_ZONES_CONFIG): vol.All(ALL_ZONES_SCHEMA),
vol.Optional(CONF_QUEUE_MANUAL): cv.boolean,
}
)

Expand Down Expand Up @@ -383,6 +386,8 @@ def _parse_dd_mmm(value: str) -> date | None:
MANUAL_RUN_SCHEMA = {
vol.Required(CONF_ENTITY_ID): cv.entity_ids,
vol.Optional(CONF_TIME): cv.positive_time_period_template,
vol.Optional(CONF_DELAY): cv.time_period,
vol.Optional(CONF_QUEUE): cv.boolean,
vol.Optional(CONF_ZONES): cv.ensure_list,
vol.Optional(CONF_SEQUENCE_ID): cv.positive_int,
}
Expand Down
15 changes: 15 additions & 0 deletions custom_components/irrigation_unlimited/services.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,21 @@ manual_run:
selector:
duration:

delay:
name: Delay
description: Delay between queued runs.
example: "00:00:30"
required: false
selector:
duration:

queue:
name: Queue
description: Start immediately or queue run.
example: false
selector:
boolean:

sequence_id:
name: Sequence Id
description: Id of the sequence to run.
Expand Down
8 changes: 8 additions & 0 deletions tests/configs/service_manual_run.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,14 @@ irrigation_unlimited:
- {t: '2021-01-04 08:16:00', c: 1, z: 1, s: 1}
- {t: '2021-01-04 08:23:30', c: 1, z: 1, s: 0}
- {t: '2021-01-04 08:24:30', c: 1, z: 0, s: 0}
- name: '15-Manual run override'
start: '2021-01-04 08:00'
end: '2021-01-04 09:00'
results:
- {t: '2021-01-04 08:00:01', c: 1, z: 0, s: 1}
- {t: '2021-01-04 08:01:01', c: 1, z: 1, s: 1}
- {t: '2021-01-04 08:16:01', c: 1, z: 1, s: 0}
- {t: '2021-01-04 08:17:01', c: 1, z: 0, s: 0}
controllers:
- name: "Test controller 1"
preamble: "00:01"
Expand Down
111 changes: 111 additions & 0 deletions tests/configs/service_manual_run_queue.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
irrigation_unlimited:
refresh_interval: 2000
controllers:
- name: "Test controller 1"
queue_manual: true
zones:
- name: "Zone 1"
duration: "00:15"
schedules:
- time: "09:05"
duration: "0:10:00"
- name: "Zone 2"
schedules:
- time: "10:05"
duration: "00:10"
- name: "Zone 3"
- name: "Zone 4"
sequences:
- name: "Multi zone"
duration: "0:06:00"
delay: "0:01:00"
schedules:
- time: "11:05"
zones:
- zone_id: 1
- zone_id: 2
- zone_id: 3
- zone_id: 4
testing:
enabled: true
speed: 1.0
output_events: false
show_log: false
autoplay: false
times:
- name: '1-Manual run queue zone 1'
start: '2023-10-04 06:00'
end: '2023-10-04 07:00'
results:
- {t: '2023-10-04 06:00:01', c: 1, z: 0, s: 1}
- {t: '2023-10-04 06:00:01', c: 1, z: 1, s: 1}
- {t: '2023-10-04 06:10:01', c: 1, z: 1, s: 0}
- {t: '2023-10-04 06:10:01', c: 1, z: 0, s: 0}
- {t: '2023-10-04 06:10:11', c: 1, z: 0, s: 1}
- {t: '2023-10-04 06:10:11', c: 1, z: 1, s: 1}
- {t: '2023-10-04 06:25:11', c: 1, z: 1, s: 0}
- {t: '2023-10-04 06:25:11', c: 1, z: 0, s: 0}
- name: '2-Manual run queue sequence 1'
start: '2023-10-04 06:00'
end: '2023-10-04 07:00'
results:
- {t: '2023-10-04 06:00:01', c: 1, z: 0, s: 1}
- {t: '2023-10-04 06:00:01', c: 1, z: 1, s: 1}
- {t: '2023-10-04 06:06:01', c: 1, z: 1, s: 0}
- {t: '2023-10-04 06:06:01', c: 1, z: 0, s: 0}
- {t: '2023-10-04 06:07:01', c: 1, z: 0, s: 1}
- {t: '2023-10-04 06:07:01', c: 1, z: 2, s: 1}
- {t: '2023-10-04 06:13:01', c: 1, z: 2, s: 0}
- {t: '2023-10-04 06:13:01', c: 1, z: 0, s: 0}
- {t: '2023-10-04 06:14:01', c: 1, z: 0, s: 1}
- {t: '2023-10-04 06:14:01', c: 1, z: 3, s: 1}
- {t: '2023-10-04 06:20:01', c: 1, z: 3, s: 0}
- {t: '2023-10-04 06:20:01', c: 1, z: 0, s: 0}
- {t: '2023-10-04 06:21:01', c: 1, z: 0, s: 1}
- {t: '2023-10-04 06:21:01', c: 1, z: 4, s: 1}
- {t: '2023-10-04 06:27:01', c: 1, z: 4, s: 0}
- {t: '2023-10-04 06:27:01', c: 1, z: 0, s: 0}
- {t: '2023-10-04 06:27:06', c: 1, z: 0, s: 1}
- {t: '2023-10-04 06:27:06', c: 1, z: 1, s: 1}
- {t: '2023-10-04 06:33:06', c: 1, z: 1, s: 0}
- {t: '2023-10-04 06:33:06', c: 1, z: 0, s: 0}
- {t: '2023-10-04 06:34:06', c: 1, z: 0, s: 1}
- {t: '2023-10-04 06:34:06', c: 1, z: 2, s: 1}
- {t: '2023-10-04 06:40:06', c: 1, z: 2, s: 0}
- {t: '2023-10-04 06:40:06', c: 1, z: 0, s: 0}
- {t: '2023-10-04 06:41:06', c: 1, z: 0, s: 1}
- {t: '2023-10-04 06:41:06', c: 1, z: 3, s: 1}
- {t: '2023-10-04 06:47:06', c: 1, z: 3, s: 0}
- {t: '2023-10-04 06:47:06', c: 1, z: 0, s: 0}
- {t: '2023-10-04 06:48:06', c: 1, z: 0, s: 1}
- {t: '2023-10-04 06:48:06', c: 1, z: 4, s: 1}
- {t: '2023-10-04 06:54:06', c: 1, z: 4, s: 0}
- {t: '2023-10-04 06:54:06', c: 1, z: 0, s: 0}
- name: '3-Manual run queue zone 1, sequence 1, zone 1'
start: '2023-10-04 06:00'
end: '2023-10-04 07:00'
results:
- {t: '2023-10-04 06:00:01', c: 1, z: 0, s: 1}
- {t: '2023-10-04 06:00:01', c: 1, z: 1, s: 1}
- {t: '2023-10-04 06:10:01', c: 1, z: 1, s: 0}
- {t: '2023-10-04 06:10:01', c: 1, z: 0, s: 0}
- {t: '2023-10-04 06:10:03', c: 1, z: 0, s: 1}
- {t: '2023-10-04 06:10:03', c: 1, z: 1, s: 1}
- {t: '2023-10-04 06:16:03', c: 1, z: 1, s: 0}
- {t: '2023-10-04 06:16:03', c: 1, z: 0, s: 0}
- {t: '2023-10-04 06:17:03', c: 1, z: 0, s: 1}
- {t: '2023-10-04 06:17:03', c: 1, z: 2, s: 1}
- {t: '2023-10-04 06:23:03', c: 1, z: 2, s: 0}
- {t: '2023-10-04 06:23:03', c: 1, z: 0, s: 0}
- {t: '2023-10-04 06:24:03', c: 1, z: 0, s: 1}
- {t: '2023-10-04 06:24:03', c: 1, z: 3, s: 1}
- {t: '2023-10-04 06:30:03', c: 1, z: 3, s: 0}
- {t: '2023-10-04 06:30:03', c: 1, z: 0, s: 0}
- {t: '2023-10-04 06:31:03', c: 1, z: 0, s: 1}
- {t: '2023-10-04 06:31:03', c: 1, z: 4, s: 1}
- {t: '2023-10-04 06:37:03', c: 1, z: 4, s: 0}
- {t: '2023-10-04 06:37:03', c: 1, z: 0, s: 0}
- {t: '2023-10-04 06:37:06', c: 1, z: 0, s: 1}
- {t: '2023-10-04 06:37:06', c: 1, z: 1, s: 1}
- {t: '2023-10-04 06:47:06', c: 1, z: 1, s: 0}
- {t: '2023-10-04 06:47:06', c: 1, z: 0, s: 0}
Loading

0 comments on commit 235d32d

Please sign in to comment.