Skip to content

Commit

Permalink
Add skip and pause services
Browse files Browse the repository at this point in the history
  • Loading branch information
rgc99 committed Nov 30, 2023
1 parent d1ee479 commit 7f2bff1
Show file tree
Hide file tree
Showing 6 changed files with 468 additions and 0 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 @@ -130,6 +130,8 @@
SERVICE_MANUAL_RUN = "manual_run"
SERVICE_LOAD_SCHEDULE = "load_schedule"
SERVICE_SUSPEND = "suspend"
SERVICE_SKIP = "skip"
SERVICE_PAUSE = "pause"

# Events
EVENT_START = "start"
Expand Down
91 changes: 91 additions & 0 deletions custom_components/irrigation_unlimited/irrigation_unlimited.py
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,8 @@
SERVICE_TIME_ADJUST,
SERVICE_LOAD_SCHEDULE,
SERVICE_SUSPEND,
SERVICE_SKIP,
SERVICE_PAUSE,
STATUS_BLOCKED,
STATUS_PAUSED,
STATUS_DISABLED,
Expand Down Expand Up @@ -2449,6 +2451,8 @@ def __init__(
self._accumulated_duration = timedelta(0)
self._first_zone: IUZone = None
self._status = IURunStatus.UNKNOWN
self._paused = False
self._last_pause: datetime = None

@property
def sequence(self) -> "IUSequence":
Expand Down Expand Up @@ -2645,6 +2649,19 @@ def next_sequence_zone(
break
return result

def sequence_zone_runs(self, sequence_zone_run: IUSequenceZoneRun) -> list[IURun]:
"""Return all the run associated with the sequence zone"""
result: list[IURun] = []
found = False
for run, szr in self.runs.items():
if not found and szr == sequence_zone_run:
found = True
if found and szr.sequence_zone == sequence_zone_run.sequence_zone:
result.append(run)
if found and szr.sequence_zone != sequence_zone_run.sequence_zone:
break
return result

def advance(
self, stime: datetime, duration: timedelta, runs: list[IURun] = None
) -> None:
Expand Down Expand Up @@ -2689,6 +2706,56 @@ def update_run(stime: datetime, duration: timedelta, run: IURun) -> None:

self.update()

def skip(self, stime: datetime) -> None:
"""Skip to the next sequence zone"""
current_start: datetime = None
current_end: datetime = None
current_runs = self.sequence_zone_runs(self._current_zone)
for run in current_runs:
if current_start is None or run.start_time < current_start:
current_start = run.start_time
if current_end is None or run.end_time > current_end:
current_end = run.end_time

next_start: datetime = None
next_end: datetime = None
nsz = self.next_sequence_zone(self._current_zone)
if nsz is not None:
for run in self.sequence_zone_runs(nsz):
if next_start is None or run.start_time < next_start:
next_start = run.start_time
if next_end is None or run.end_time > next_end:
next_end = run.end_time
duration = next_start - stime
if self._active_zone is not None:
delay = max(next_start - current_end, timedelta(0))
else:
delay = max(current_start - stime, timedelta(0))
else:
duration = current_end - stime
delay = timedelta(0)

# Next zone is overlapped with current
if next_start is not None and next_start < current_end:
self.advance(stime, -(current_end - stime), current_runs)
self.advance(stime, -(duration - delay))

def pause(self, stime: datetime) -> None:
"""Skip to the next sequence zone"""
self._paused = not self._paused
if self._paused:
self._last_pause = stime
else:
self._last_pause = None

def update_pause(self, stime: datetime) -> bool:
"""Muster this sequence run"""
if self._paused and stime != self._last_pause:
self.advance(stime, stime - self._last_pause)
self._last_pause = stime
return True
return False

def update(self) -> bool:
"""Update the status of the sequence"""
result = False
Expand Down Expand Up @@ -3464,6 +3531,20 @@ def finalise(self) -> None:
if not self._finalised:
self._finalised = True

def service_skip(self, data: MappingProxyType, stime: datetime) -> bool:
"""Skip to the next sequence zone"""
# pylint: disable=unused-argument
for sqr in self._run_queue:
if sqr.running:
sqr.skip(stime)

def service_pause(self, data: MappingProxyType, stime: datetime) -> bool:
"""Pause the sequence"""
# pylint: disable=unused-argument
for sqr in self._run_queue:
if sqr.running:
sqr.pause(stime)


class IUController(IUBase):
"""Irrigation Unlimited Controller (Master) class"""
Expand Down Expand Up @@ -3903,6 +3984,10 @@ def muster(self, stime: datetime, force: bool) -> IURQStatus:
self.clear_zones(None)
status |= IURQStatus.CLEARED
else:
for sequence in self._sequences:
for sqr in sequence.runs:
if sqr.update_pause(stime):
status |= IURQStatus.CHANGED
for zone in self._zones:
zone.runs.update_run_status(stime)
self._run_queue.update_run_status(stime)
Expand Down Expand Up @@ -5854,6 +5939,12 @@ def service_call(
zone.service_manual_run(data1, stime)
else:
controller.service_manual_run(data1, stime)
elif service == SERVICE_SKIP:
if sequence is not None:
sequence.service_skip(data1, stime)
elif service == SERVICE_PAUSE:
if sequence is not None:
sequence.service_pause(data1, stime)
elif service == SERVICE_LOAD_SCHEDULE:
render_positive_time_period(data1, CONF_DURATION)
self.service_load_schedule(data1)
Expand Down
70 changes: 70 additions & 0 deletions tests/configs/service_sequence_pause.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
irrigation_unlimited:
refresh_interval: 2000
controllers:
- name: Test Controller 1
preamble: "0:01:00"
postamble: "0:01:00"
zones:
- name: 'Zone 1'
- name: 'Zone 2'
- name: 'Zone 3'
- name: 'Zone 4'
sequences:
- name: "Seq 1"
duration: "0:05:00"
delay: "0:01:00"
schedules:
- time: "06:05"
zones:
- zone_id: 1
- zone_id: [2,3]
- zone_id: 4
testing:
enabled: true
speed: 1.0
output_events: false
show_log: false
autoplay: false
times:
- name: '1-Normal run'
start: '2023-11-28 06:00'
end: '2023-11-28 07:00'
results:
- {t: '2023-11-28 06:04:00', c: 1, z: 0, s: 1}
- {t: '2023-11-28 06:05:00', c: 1, z: 1, s: 1}
- {t: '2023-11-28 06:10:00', c: 1, z: 1, s: 0}
- {t: '2023-11-28 06:11:00', c: 1, z: 2, s: 1}
- {t: '2023-11-28 06:11:00', c: 1, z: 3, s: 1}
- {t: '2023-11-28 06:16:00', c: 1, z: 2, s: 0}
- {t: '2023-11-28 06:16:00', c: 1, z: 3, s: 0}
- {t: '2023-11-28 06:17:00', c: 1, z: 4, s: 1}
- {t: '2023-11-28 06:22:00', c: 1, z: 4, s: 0}
- {t: '2023-11-28 06:23:00', c: 1, z: 0, s: 0}
- name: '2-Pause while off for 10min'
start: '2023-11-28 06:00'
end: '2023-11-28 07:00'
results:
- {t: '2023-11-28 06:04:00', c: 1, z: 0, s: 1}
- {t: '2023-11-28 06:05:00', c: 1, z: 1, s: 1}
- {t: '2023-11-28 06:10:00', c: 1, z: 1, s: 0}
- {t: '2023-11-28 06:21:00', c: 1, z: 2, s: 1}
- {t: '2023-11-28 06:21:00', c: 1, z: 3, s: 1}
- {t: '2023-11-28 06:26:00', c: 1, z: 2, s: 0}
- {t: '2023-11-28 06:26:00', c: 1, z: 3, s: 0}
- {t: '2023-11-28 06:27:00', c: 1, z: 4, s: 1}
- {t: '2023-11-28 06:32:00', c: 1, z: 4, s: 0}
- {t: '2023-11-28 06:33:00', c: 1, z: 0, s: 0}
- name: '3-Pause while on for 10min'
start: '2023-11-28 06:00'
end: '2023-11-28 07:00'
results:
- {t: '2023-11-28 06:04:00', c: 1, z: 0, s: 1}
- {t: '2023-11-28 06:05:00', c: 1, z: 1, s: 1}
- {t: '2023-11-28 06:19:00', c: 1, z: 1, s: 0}
- {t: '2023-11-28 06:20:00', c: 1, z: 2, s: 1}
- {t: '2023-11-28 06:20:00', c: 1, z: 3, s: 1}
- {t: '2023-11-28 06:25:00', c: 1, z: 2, s: 0}
- {t: '2023-11-28 06:25:00', c: 1, z: 3, s: 0}
- {t: '2023-11-28 06:26:00', c: 1, z: 4, s: 1}
- {t: '2023-11-28 06:31:00', c: 1, z: 4, s: 0}
- {t: '2023-11-28 06:32:00', c: 1, z: 0, s: 0}
Loading

0 comments on commit 7f2bff1

Please sign in to comment.