Module M2 - Basic Irrigation
Files: packages/grow_irrigation.yaml (this module), packages/grow_core.yaml
(grow_plant_count / grow_dripper_count / grow_dripper_flow_lph -
Phase 1, and the sensor.grow_substrate_*_avg sensors used for the
failsafe and the historical chart), packages/grow_actuators.yaml
(switch.grow_actuator_irrigation_pump_1 / _2 - the pumps this module
drives).
What’s implemented (Phase 4)
Replaces the old Growzelt Bewässerung - P1/P2, Cannabis Bewässerung - Notabschaltung, and Failsafe Bewässerung - 35 % automations with:
- An unlimited, dynamically editable schedule per pump - see “Why
schedule:instead of a fixed number of slots” below. - Manual trigger - a “Run Now” button on Dashboard 2, using its own configurable duration/zone.
- User-configurable safety limits (R10): max runtime per event, and max events per day. Both have sensible defaults (60s, 8/day) but are yours to change.
- Substrate-moisture failsafe: if the mapped substrate sensors’ average VWC drops below a configurable threshold (default 35%, matching the automation this replaces), all irrigation - scheduled and manual - is blocked until VWC recovers or you clear it by hand.
- A pump watchdog safety net independent of the above, in case a script run is ever interrupted.
- A historical chart (VWC + EC + both pumps’ on/off activity overlaid) so you can see how substrate moisture actually responds to each watering event, not just the live number.
Why schedule: instead of a fixed number of slots
The first cut of this module (shipped 2026-08-24) used 6 fixed event slots,
each its own input_datetime/input_number/input_select. That’s a real
Home Assistant limitation, not a design choice: input_* helpers are
declared in YAML and their count is fixed at config time - there’s no way
to let you add a 7th one from the dashboard without editing a package file
and restarting, no matter how the dashboard around it looks. It also got
cluttered fast (6 cards × 4 fields each).
The redesign uses Home Assistant’s native schedule: integration
instead - the same “weekly time-block editor” used for things like
thermostat schedules. Each schedule.* entity holds as many time blocks
per day as you want, added/removed/resized with no YAML edit and no
restart once it exists.
Important caveat, discovered 2026-08-25 after the first version of this
redesign shipped: a schedule: entity defined in package YAML has no
editor at all - its more-info dialog shows only generic History/Activity,
nothing to click, confirmed from a screenshot on the live instance. Only a
schedule helper created through the Home Assistant UI (Settings ->
Devices & Services -> Helpers -> ”+ Create Helper” -> Schedule) gets the
real weekly block editor, because editing a schedule’s blocks goes through
a storage-backed config-entry API that a YAML-defined entity simply doesn’t
have - the same category of limitation as HA’s other input_*/counter/
timer helper domains, which normally hide this because their value can
still be set from a YAML-defined entity even though their definition
technically can’t - but for schedule, the block list effectively is the
definition, so there’s no value left to set from outside. This package
therefore does not define schedule: itself anymore; you create the
two schedule helpers once, by hand, and rename their Entity ID to match
what the automations below expect - see the rollout checklist. Still 100%
native Home Assistant (R1 unaffected, no pyscript/custom integration
involved) - just “created via the UI” instead of “created via YAML”, the
same one-time-manual-step category as pairing a Zigbee device or
installing the apexcharts-card HACS card elsewhere in this project.
There’s one schedule entity per pump
(schedule.grow_irrigation_pump_1_schedule /
schedule.grow_irrigation_pump_2_schedule) rather than one shared schedule,
because a schedule block doesn’t carry custom fields like “which zone” -
separate schedules per pump keeps that simple, and lets both pumps run at
once if their blocks happen to overlap (this replaces the old “Both” slot
option, which is still available for the manual-run button).
How a scheduled event actually fires
automation.grow_irrigation_schedule_pump_1 (and the _pump_2 twin)
triggers when that pump’s schedule entity turns on - i.e. a block just
started. It reads the block’s own length from the entity’s next_event
attribute (the time it will next turn back off) at that moment, so an
arbitrary block of any length becomes that event’s requested duration -
still passed through the same script.grow_irrigation_run_event used by
the manual button, which hard-clamps it to “Max Runtime / Event” no matter
how long the block itself was set to. Both the master switch
(grow_irrigation_control_enabled) and this shared clamp apply identically
regardless of how an event started.
P1 (ramp-up) / P2 (maintenance) shot sizing
Classic crop-steering terminology: right after lights-on, substrate is typically dry from the overnight dry-back, so you want bigger shots to bring VWC up to its daily max quickly (P1 / ramp-up); for the rest of the day you only want small maintenance shots to hold VWC in a band without overwatering (P2 / maintenance). This is a different axis than “Pump 1” vs “Pump 2” (which zone) - P1/P2 here is which part of the day it is, and applies to whichever pump’s schedule block just fired.
input_datetime.grow_irrigation_p1_window_start/grow_irrigation_p1_window_enddefine the ramp-up window (default 06:00-09:00); everything outside it is P2. Handles a window that wraps past midnight too.input_number.grow_irrigation_p1_max_duration_sec(default 90s, bigger) andgrow_irrigation_p2_max_duration_sec(default 20s, smaller) are the two shot-size caps.binary_sensor.grow_irrigation_p1_window_activeshows which one is currently in effect.- This still works alongside the schedule, not instead of it - you still draw blocks in each pump’s schedule for when to water; P1/P2 caps how big a shot gets to be based on when it actually runs (real time at execution, not when the block was drawn).
- This is not a live-VWC feedback loop (i.e. “keep shooting until VWC hits its max, then hold it there”) - that’s full crop-steering territory, deliberately deferred per the Phase 0 plan (Section 2’s R1 note on the “Crop-Steering-Zustandsmaschine” being more than Grundfunktion scope). This gives you manual control over shot size by time of day, which covers the “more water to reach max VWC, less to maintain it” need without that full complexity.
Safety limits (R10)
input_number.grow_irrigation_max_duration_sec- the absolute safety ceiling, applied on top of whichever P1/P2 cap applies (tightest of the three wins). Whatever duration a schedule block or the manual run requests, the actual run time ismin(requested, P1_or_P2_cap, max_duration_sec)- this clamp happens inside the script itself, so an oversized schedule block (or a misconfigured P1/P2 cap) can never run longer than this ceiling.input_number.grow_irrigation_max_events_per_day- enforced via a native HAcounter:entity (counter.grow_irrigation_events_today), which survives restarts (restore: true) and resets at midnight (automation.grow_irrigation_daily_counter_reset). Once the limit is hit, every further event (scheduled or manual, either pump) is blocked and logged until the reset.binary_sensor.grow_irrigation_daily_limit_reached- convenience sensor the dashboard uses to show a warning banner when the limit is hit.
Failsafe (substrate moisture)
input_number.grow_irrigation_failsafe_vwc_threshold- default 35%, matching the automation this module replaces.- Compared against
sensor.grow_substrate_vwc_average(Phase 1, averages whichever substrate sensors are mapped and included). Note: this sensor’s entity_id had a naming bug until 2026-08-25 (seedocs/docs/troubleshooting.md/ CHANGELOG 0.7.2) - if your failsafe never seems to trip or the Live Substrate Readings tiles say “entity not found”, you’re on the pre-fix version; re-deploypackages/grow_core.yamland rename the 3 live entities as described there. - If VWC is below threshold:
input_boolean.grow_irrigation_failsafe_trippedturns on, a singlepersistent_notificationfires, and all irrigation is blocked - scheduled and manual - until it clears. - Auto-recovery with hysteresis: clears automatically once VWC rises to threshold + 5 percentage points (avoids flapping right at the edge), logging and notifying once on recovery.
- Manual clear: a “Clear Failsafe” button on the dashboard
(
script.grow_irrigation_reset_failsafe) for when you’ve fixed the underlying issue yourself and don’t want to wait for the sensor to catch up, or when no substrate sensors are mapped at all (in which case the failsafe simply never trips - Phase 1 substrate sensors are optional, and irrigation isn’t blocked just because you don’t have them).
VWC Lock (upper-bound schedule gate, 2026-08-27)
The failsafe above stops watering when substrate is too dry. VWC Lock is the complementary opposite: it stops the schedule from watering when substrate is already wet enough, so you don’t keep dosing past your target and risk runoff/oxygen-starved roots.
input_boolean.grow_irrigation_vwc_lock_enabled- off by default; the feature does nothing until you turn it on.input_number.grow_irrigation_vwc_lock_threshold- default 65%, compared againstsensor.grow_substrate_vwc_average.binary_sensor.grow_irrigation_vwc_locked- on when VWC is at or above the threshold. Stateless, no hysteresis by design (matches the feature as requested) - it re-evaluates fresh every time, unlike the failsafe’s auto-recovery-with-hysteresis behavior. If the substrate average isunknown/unavailable, this reads as not locked (fails open, so a missing sensor doesn’t silently block watering).- Applies only to the two
schedule.grow_irrigation_pump_*automations. Manual Run is deliberately unaffected - if you tap “Run Now” you clearly want water right then regardless of what the schedule would decide. - When a scheduled block fires while locked, the pump does not run, and
a “SKIPPED: VWC Lock active” line is written to
input_text.grow_irrigation_last_decisionso you can see it happened rather than wondering why a block didn’t fire. - Surfaced as a 🔒 banner on Dashboard 2’s Irrigation & Substrate view whenever both the toggle is on and the lock is currently engaged.
Lifetime delivered-volume tracking (2026-08-27)
input_number.grow_irrigation_total_volume_l accumulates the estimated
volume (see “Water volume” below) of every event this module runs -
scheduled and manual alike, both pumps - starting from when you first deploy
this version. It’s automation-managed (the run script increments it as the
last step of every FINISHED event) - you shouldn’t need to edit it by hand
except to reset it. This is what feeds the Energy module’s nutrient-cost
tracking (docs/docs/module-energy.md): every liter delivered is treated as
nutrient solution dosed, per the confirmed scope for this feature.
Crop-Steering Presets (Phase 5, R12, 2026-08-28)
Per the Phase 0 plan, Phase 5 (“Substratüberwachung & Crop-Steering-Basis”) calls for VWC/EC/Temp trend visualization plus generative/vegetative preset templates (R12). The trend-graph half was already delivered as a byproduct of the 2026-08-25 redesign (see “Historical chart” below); this adds the remaining piece.
input_select.grow_irrigation_crop_steering_preset offers two named
presets - Vegetative (wetter, more frequent watering, promotes
vegetative growth) and Generative (drier, bigger dry-back between
waterings, promotes flowering/generative growth). Selecting a value in the
dropdown changes nothing by itself - tapping the “Apply Preset” button
(script.grow_irrigation_apply_crop_steering_preset) does a one-time bulk
write of 5 existing fields to that preset’s starting values:
| Field | Vegetative | Generative |
|---|---|---|
| VWC Lock Threshold | 70% | 55% |
| Failsafe VWC Threshold | 40% | 28% |
| P1 (Ramp-Up) Max Runtime | 90s | 60s |
| P2 (Maintenance) Max Runtime | 25s | 12s |
| Max Events / Day | 10 | 6 |
These are proposed starting values (confirmed with the user), not a fixed
recipe for your specific genetics/substrate - every field stays freely,
individually adjustable afterward, exactly like before applying a
preset. Applying does not touch the Absolute Max Runtime ceiling
(grow_irrigation_max_duration_sec) or the master Automatic Control
switch - a preset only affects “how wet to keep the substrate and how
often”, never the hard safety limits or whether automation is active at
all. Applying logs a line to input_text.grow_irrigation_last_decision
and fires a persistent_notification naming exactly what changed, same
visibility pattern as every other action in this module.
This is deliberately the “Crop-Steering-Basis”, not the full “Crop-Steering-Zustandsmaschine” (a live VWC-feedback state machine that waters until a target VWC is reached, then holds it there) - the Phase 0 plan’s own risk table calls that out as more complexity than native YAML-only automations (R1) should take on, and it remains deliberately deferred. Applying a preset is a single dashboard-button action, not a running automation - it never re-applies itself, never overrides a value you change afterward, and there is no “currently active preset” state tracked anywhere (the select just remembers your last choice as a convenience for next time).
Pump watchdog (independent safety net)
automation.grow_irrigation_pump_watchdog is deliberately not part of
the run script - it’s a separate automation that watches both irrigation
pump switches directly, unaffected by anything above. If either has been
on for longer than max_duration_sec + 30s, it force-turns the pump off,
logs a WATCHDOG entry to grow_irrigation_last_decision, and sends a
notification. This exists for the “Hard-Timeout je Event” risk from the
Phase 0 plan’s risk table (Section 6.4) - script interruption (HA restart
mid-event, power loss, etc.) should never be able to leave a pump running
unattended.
Historical chart (VWC / EC / pump activity)
Dashboard 2’s Irrigation & Substrate view has a time-range-selectable chart
(input_select.grow_irrigation_chart_range, same 1h/6h/24h/3d/7d pattern as
the Climate & VPD chart) plotting sensor.grow_substrate_vwc_average and
sensor.grow_substrate_ec_average on their own visible y-axes, with both
switch.grow_actuator_irrigation_pump_1 / _2 overlaid as filled step
areas (on a hidden 0-1 axis each) so you can see exactly when each pump ran
against how VWC/EC moved in response - the actual point of a substrate
chart for irrigation, not just a live number. EC is plotted unconditionally
since sensor.grow_substrate_ec_average already exists from Phase 1; it will
simply show flat/unavailable if you don’t have EC-capable substrate probes
mapped.
Water volume
sensor.grow_irrigation_manual_run_volume shows the estimated volume (in
liters) for the current manual-run duration, using
grow_dripper_count * grow_dripper_flow_lph * (duration_sec / 3600) from
the Phase 1 helpers in packages/grow_core.yaml. The same formula is used
internally for every event (scheduled or manual) and written into the
_last_decision log line for that run, so you always know how much water
each event used without needing a dedicated sensor per slot.
Dashboard layout note (2026-08-25)
The first cut of the redesigned view nested a 2-column type: grid inside
an already-narrow Lovelace masonry column, which truncated long row labels
(“Failsafe VWC Threshold” showing as “Fa…”) to the point of being
unreadable. Fixed by removing that inner grid - cards now stack vertically
in view order and let Lovelace’s own masonry layout handle column
placement across the page width, the same approach the other 3 views on
this dashboard already use. The only remaining grid card is the small
2-tile pump row, which is the right use case for it (small square tiles
benefit from an explicit grid; wide entities rows with real text don’t).
Rollout checklist
- Deploy
packages/grow_irrigation.yamlandpackages/grow_core.yaml, check config, restart (new helpers/counter entities need one the first time).packages/grow_irrigation.yamlno longer definesschedule:entities - see step 2. - Create the two schedule helpers manually (one-time, via the UI, not
YAML) - see “Why
schedule:instead of a fixed number of slots” above for why this step can’t be automated away:- Settings -> Devices & Services -> Helpers -> ”+ Create Helper” -> Schedule -> name it anything, save.
- Open it -> gear icon -> set Entity ID to exactly
schedule.grow_irrigation_pump_1_schedule. - Repeat for Pump 2 with entity_id
schedule.grow_irrigation_pump_2_schedule. - If you already created a
schedule:entity via the earlier YAML-based version of this package, delete it first (Settings -> Helpers -> it will show there once no longer defined by the package) so the two entity_ids above are free to use.
- Re-import
dashboards/dashboard_2_control_panel.yamlto get the redesigned “Irrigation & Substrate” view. - One-time cleanup if you deployed the original 6-slot version: the
old
input_boolean/input_datetime/input_number/input_selectevent-slot helpers (grow_irrigation_event_1..6_*) are no longer defined by this package and will show as orphaned/unavailable in Settings -> Helpers - safe to delete. - If the “Live Substrate Readings” tiles or the failsafe never seem to
work, confirm the 3
sensor.grow_substrate_*_averageentities exist with exactly that entity_id (Settings -> Entities, search “grow_substrate”) - rename any that don’t match via that entity’s gear icon -> Entity ID field (same fix pattern as the actuator entity_id issue from Phase 3 - seedocs/docs/module-climate.md“Known issue”). - Confirm
grow_dripper_countandgrow_dripper_flow_lph(Dashboard 1, Phase 1) match your real hardware - the volume estimates depend on them. - Set your safety limits (max runtime/event, max events/day), the P1/P2
window and shot-size caps, and the failsafe VWC threshold to values
you’re comfortable with.
7a. If you want the schedule to stop watering once substrate is wet enough,
set
input_number.grow_irrigation_vwc_lock_thresholdand turn oninput_boolean.grow_irrigation_vwc_lock_enabled. Leave it off if you don’t want this behavior - Manual Run is never affected either way. - Disable any old
Growzelt Bewässerung - P1/P2,Cannabis Bewässerung - Notabschaltung, andFailsafe Bewässerung - 35 %automations, so they don’t fight this module over the same pumps. - Configure your schedules by tapping each
schedule.grow_irrigation_pump_*entity on the dashboard (now that it’s a UI-created helper, this opens the real block editor) and adding blocks, withgrow_irrigation_control_enabledstill off, and test with the “Run Now” manual button first. - Once you’re confident, turn
grow_irrigation_control_enabledon. - Watch Dashboard 3’s “Irrigation Configuration & Automation Status” and “Device Activity” logbook cards, plus the new VWC/EC chart, for the first day or two to confirm events run when/how expected, the right P1/P2 phase is applying, and substrate moisture actually responds.
- Optional - Crop-Steering Presets: on the Irrigation & Substrate view, pick “Vegetative” or “Generative” from the new preset dropdown and tap “Apply Preset” whenever you want a one-tap starting point for VWC Lock Threshold, Failsafe VWC Threshold, P1/P2 shot sizes, and Max Events/Day - see “Crop-Steering Presets” above for the exact values. Nothing changes until you tap Apply, and every field stays adjustable afterward - skip this entirely if you’d rather keep tuning those 5 fields by hand.