Skip to content

Add native cycle-and-soak with minimum soak time and interleaving - #236

Open
JB09 wants to merge 520 commits into
rgc99:devfrom
JB09:master
Open

Add native cycle-and-soak with minimum soak time and interleaving#236
JB09 wants to merge 520 commits into
rgc99:devfrom
JB09:master

Conversation

@JB09

@JB09 JB09 commented Jun 4, 2026

Copy link
Copy Markdown

Closes #234.

Summary

Adds a native cycle-and-soak mode to sequences via a new cycle: config block. Each zone’s total runtime is split into evenly distributed cycles capped at a configurable maximum, with a guaranteed minimum soak between a zone’s own cycles. During a zone’s soak, other pending zones in the same sequence run (interleaving), so the soak window is used productively instead of sitting idle — matching how commercial controllers (RainMachine, Rain Bird) handle slow-infiltrating soils like clay.

Backwards compatible: sequences without a cycle: block behave exactly as before.

Config

sequences:
  - name: "Front/Side"
    cycle:
      max_duration: "00:45"   # cap per cycle (runoff threshold)
      min_duration: "00:10"   # floor; never run a cycle shorter than this
      min_soak: "01:00"       # guaranteed soak between a zone's own cycles
    zones:
      - zone_id: 1
        duration: "02:15"     # total; split into cycles automatically
      # ...

Behavior

  • Even-distribution splitnum_cycles = ceil(total / max_duration), then per_cycle = total / num_cycles. Avoids a tiny trailing cycle (e.g. 45 min → 3 × 15, never 15 + 15 + 15 + remainder).
  • Minimum cycle floornum_cycles is capped at floor(total / min_duration) so no cycle runs shorter than min_duration. If a zone’s total is below min_duration, it runs once.
  • Guaranteed minimum soak — between a zone’s consecutive cycles, at least min_soak is enforced.
  • Interleaving (most-time-remaining-first) — during a zone’s soak, the scheduler runs the pending zone with the most time remaining, keeping one-zone-at-a-time within the sequence. This fills soak windows productively and minimizes total run time.
  • Drop-out — zones with fewer cycles finish early and are skipped in later passes.
  • adjust_time integration — when a zone’s total is set via adjust_time, the cycle logic re-derives the split from the new total, so integrations that push a daily total per zone (e.g. Smart Irrigation) work without the caller dividing by a cycle count. This also sidesteps the repeat multiplication behavior reported in Smart Irrigation integration with Irrigation Unlimited not fully functional #203 / Despite different configuration the watering time is limited to one hour ("magically") #211.

Testing

  • Unit tests covering split math, the min_duration floor, min_soak enforcement, interleaving order (most-time-remaining-first), drop-out, and adjust_time re-derivation.
  • Validated end-to-end on a live Home Assistant instance (6 zones across 2 concurrent sequences). The run order was reconstructed from entity state history. With Front = 3 cycles, Side Left = 2, Side Right = 1, the observed order confirmed interleaving and selection logic:
    • Front c1 → (soak) → Side Left c1 — Side Left chosen over Side Right because it had more time remaining (2 min vs 1 min), confirming most-time-remaining-first rather than zone-label order.
    • Subsequent cycles continued interleaving, with each zone dropping out as it completed and min_soak respected between a given zone’s cycles.

Other findings

  • cancel during a cycled run — I noticed cancel does not always terminate all remaining cycles of an in-progress cycled run (a reload cleared it).

claude and others added 30 commits June 4, 2026 01:27
Cycle-and-soak already interleaves zones on the scheduled, adjust_time
and manual_run paths (all three share IUSequenceRun.build/_build_cycle).
Add explicit regression coverage for the manual_run path, which drives
the sequence through a manual run with no time override and asserts the
interleaved cycle order with drop-out.

Bump the version so HACS custom-repository installs pick up the build
that contains the interleaving scheduler.
Enhance pause/resume functionality
Allow the cycle block to be set on an individual sequence zone, with
each field falling back to the sequence-level cycle when not set on the
zone. This enables a different max_duration (runoff threshold) per zone
while inheriting min_duration and min_soak, and lets a sequence enable
cycle-and-soak from zone-level blocks alone.

Reuses the existing CYCLE_SCHEMA, IUSequenceCycle and calc_cycles. The
resolver mirrors the existing zone_delay_config/zone_duration_config
fall-through pattern. min_soak becomes per-zone via the resolved cycle.

Fully backwards compatible: a config with only a sequence-level cycle
resolves every zone to that cycle and behaves exactly as before.

Bump version to 2026.6.2.
Sync the fork with 22 upstream commits (schema rename rgc99#237, reworked
service handler/dispatcher rgc99#235, typing updates, DST fix for finish
anchor, pause/resume enhancements, HA 2026.5.0 / test plugin bump).

Merged cleanly with no conflicts; the native cycle-and-soak feature and
per-zone overrides are preserved. Full test suite passes (123).
export_config did not serialise the sequence/zone cycle block, so a
config using cycle-and-soak round-tripped without its max_duration,
min_duration and min_soak (and per-zone overrides). Add a _cycle helper
and emit it for both the sequence and each sequence zone when enabled.

Adds a dedicated export test covering the sequence-level cycle and a
per-zone max_duration override. Bump version to 2026.6.3.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants