Version 2.3.1 | 6-Zone Coco Coir | Athena Nutrients | GroundWork Probes | April 2026
This system automates irrigation and climate control for a 6-table cannabis grow room running coco coir with Athena nutrients and GroundWork substrate probes. Two layers run simultaneously:
Time-based irrigation scheduler (should be OFF when crop steering is active), environment control (CO2, dehumidifiers, humidifier), and safety watchdogs. Runs in HA via YAML packages at /config/packages/irrigation/.
Sensor-driven 4-phase irrigation (P0–P3) with per-zone control, EC ratio logic, ML prediction, dryback detection, and emergency management. Runs as AppDaemon app + HA custom component. This is the active irrigation brain.
input_boolean.irrigation_irrigation_enabled to OFF when crop steering is handling irrigation. GW environment controls remain active independently.| Component | GW Entity | Raw Entity | Physical |
|---|---|---|---|
| Main Line | switch.irrigation_mainline | switch.mainline | Mainline solenoid (KC868) |
| Zone 1–6 Valves | switch.irrigation_table_N_valve | switch.table_N | Table solenoids (KC868) |
| Pump | None — auto-start | Constant pressure switch | |
| Mains Water | switch.irrigation_mains_water | switch.mains_water | Tank fill only (NOT irrigation) |
| Manifold | switch.irrigation_manifold | switch.manifold | Tank mixing only (NOT irrigation) |
| Zone | VWC | EC |
|---|---|---|
| 1–6 | sensor.substrate_N_substrate_N_vwc_coco_coir | sensor.substrate_N_substrate_N_pwec |
GW mapping layer creates aliases: sensor.irrigation_table_N_vwc / _ec / _substrate_temp. Crop steering reads the raw entities directly.
| Sensor | Entity | Source |
|---|---|---|
| Room temp | sensor.irrigation_room_1_temp | Avg of 4 calibrated sensors |
| Room RH | sensor.irrigation_room_1_rh | Avg of 4 sensors |
| Room CO2 | sensor.irrigation_room_1_co2 | Avg of 3–4 CO2 sensors |
| Room VPD | sensor.irrigation_room_1_vpd | Avg from CO2 sensors |
| Device | Entity | Notes |
|---|---|---|
| CO2 solenoid | switch.irrigation_co2 | Solid state relay |
| Humidifier | switch.irrigation_humidifier | Solid state relay |
| Dehumidifier (plug) | switch.irrigation_dehumidifier_1 | TP-Link Kasa WiFi plug |
| Dehumidifier relays 1–4 | switch.irrigation_dehumidifier_relay_1–4 | Staggered 10s apart |
| AC 1–4 | climate.ac_* | IR via ESPHome. Mapped, NOT automated. |
| Lights | light.grow_room_all_lights | ESPHome group |
binary_sensor.irrigation_leak — hardcoded false. Leak abort won't fire.binary_sensor.irrigation_tank_low — hardcoded false. Tank-low abort won't fire.switch.irrigation_waste_valve, switch.irrigation_ceiling_vent — TODO placeholdersPhysical probes (ESPHome)
|
Raw entities (sensor.substrate_N_substrate_N_vwc_coco_coir)
| [10_mapping.yaml]
GW contract entities (sensor.irrigation_table_N_vwc)
|
+-- crop_steering.env
|
Crop Steering custom component (crop_steering_ prefixed entities)
|
Home Assistant (state + events)
|
AppDaemon master app (listen_state + REST API fallback)
|
Hardware (valve switches via HA services)
crop_steering_ prefix (e.g. switch.crop_steering_system_enabled). AppDaemon tries both prefixed and unprefixed names. Sensors created by AppDaemon use exact IDs shown.| File | Purpose |
|---|---|
00_core.yaml | Emergency stop script, alert helper |
10_mapping.yaml | Raw ESPHome → irrigation_* contract aliases |
20_model.yaml | Input helpers (schedules, targets, timers) |
30_irrigation.yaml | Time-based scheduler + abort automations |
40_environment.yaml | Dehumidifier, humidifier, CO2 automations |
50_alerts_watchdogs.yaml | Valve watchdogs, sensor alerts, safety interlocks |
| File | Purpose |
|---|---|
master_crop_steering_app.py | ~5900 lines. Decision loop, phases, safety, analytics |
phase_state_machine.py | Per-zone state machine |
intelligent_sensor_fusion.py | Outlier detection, Kalman filtering |
advanced_dryback_detection.py | Peak detection, rate prediction |
intelligent_crop_profiles.py | Strain profiles, adaptive learning |
ml_irrigation_predictor.py | Pure-math ML predictor |
Each zone runs independently. Phases advance on sensor readings.
When: Lights on (08:00). No irrigation. Records peak VWC.
Exits: Dryback target met (50%) or min drop (15%) or max wait (120 min) or rate too slow. Min wait 30 min.
Exception: EC > 2.5x target → flush shot (10%).
When: P0 ends. Progressive shots: 2% + 0.5% × count, max 10%, × zone multiplier.
Exits: VWC ≥ 65% + min 3 shots, or max 6 shots. 15 min cooldown. EC-adjusted.
When: P1 target met. 5% shot when VWC < 60%.
EC > 1.2 → 1.5x shot. EC < 0.8 → 0.7x shot.
Exits: ML predicts insufficient overnight dryback time.
When: Dryback prediction. Emergency only: VWC < 40% or EC > 2.0x.
Exits: Lights on next morning → P0.
Phase state machine — what actually triggers each transition
flowchart TD
LightsOn(["☀️ Lights ON · 08:00"])
P0["🟡 P0 — Morning Dryback\nNo watering at all.\nSystem records the peak VWC reading\nand waits for the substrate to dry out\nbefore starting the first watering."]
P1["🟢 P1 — Ramp Up\nFirst waterings of the day.\nStarts with a tiny shot and grows\nslightly bigger each time.\nFires every 15 minutes."]
P2["🔵 P2 — Day Maintenance\nKeeps substrate at a steady level.\nFires a 5% shot whenever VWC\ndrops below the lower threshold.\nRuns for most of the day."]
P3["🔴 P3 — Lights-Off Wind-Down\nStops planned irrigation.\nLets substrate dry overnight.\nOnly emergency rescue shots allowed."]
LightsOff(["🌙 Lights OFF · 20:00"])
FlushShot["⚡ Flush Shot\n10% shot · emergency\nEC spiked above 2.5× target"]
EmgShot["🚨 Emergency Rescue\n2% shot fires\nVWC dropped below 40%"]
LightsOn --> P0
P0 -- "✓ VWC dropped 15%+ from peak\n✓ At least 30 min elapsed" --> P1
P0 -- "⏱ 120 min maximum\nAI forces transition" --> P1
P0 -. "EC too high — flush first" .-> FlushShot
FlushShot -. "returns to dryback wait" .-> P0
P1 -- "✓ VWC reached 65%\n✓ At least 3 shots fired" --> P2
P1 -- "⏱ 6 shots maximum\nforced transition" --> P2
P2 -- "ML predicts not enough\ndryback time left today" --> P3
P3 --> LightsOff
LightsOff -- "Next morning" --> LightsOn
P3 -. "VWC below 40% overnight" .-> EmgShot
EmgShot -. "returns to overnight wait" .-> P3
style LightsOn fill:#1c2128,stroke:#8b949e,color:#c9d1d9
style LightsOff fill:#1c2128,stroke:#8b949e,color:#c9d1d9
style P0 fill:#3a2600,stroke:#d29922,color:#e6edf3
style P1 fill:#0f2d1b,stroke:#3fb950,color:#e6edf3
style P2 fill:#0d1f3c,stroke:#58a6ff,color:#e6edf3
style P3 fill:#2d0f0f,stroke:#f85149,color:#e6edf3
style FlushShot fill:#2a1a2a,stroke:#bc8cff,color:#e6edf3
style EmgShot fill:#2d0f0f,stroke:#f85149,color:#f85149
Lights 08:00–20:00, Cannabis_Athena, vegetative:
| Time | Phase | What Happens |
|---|---|---|
| 08:00 | P0 | Lights on. Peak VWC recorded. No watering. |
| ~09:30 | P1 | Dryback met. Progressive shots every 15 min. |
| ~11:00 | P2 | VWC at 65%. Maintenance shots when < 60%. |
| ~17:00 | P3 | Pre-lights-off. Emergency only. |
| 20:00 | P3 | Lights off. P3 until morning. |
Max 1 concurrent zone. finally block ensures hardware off on error.
This is what your VWC sensor should look like on a healthy vegetative day. The line color shows which phase is active. The dashed horizontal lines are the key thresholds the system watches. If your actual readings look very different from this, something needs tuning.
VWC % over 24 hours — healthy vegetative zone · Cannabis_Athena profile · lights 08:00–20:00
input_boolean.irrigation_irrigation_enabled to OFF.Checks every 5 min: system enabled, irrigation enabled, not maintenance, no leak, no tank low, within time window, interval elapsed. Opens mainline, runs each enabled table for configured duration sequentially, closes mainline.
| Setting | Entity | Default |
|---|---|---|
| Window start/end | input_datetime.irrigation_irrigate_start/end_time | Set in HA |
| Interval | input_number.irrigation_irrigate_interval_min | 60 min |
| Duration | input_number.irrigation_irrigate_duration_sec | 60s |
| Table N | input_boolean.irrigation_table_N_enabled | OFF |
Gated by input_boolean.irrigation_environment_enabled.
4 relays stagger ON 10s apart. ON: RH > target + 5% for 3 min. OFF: RH < target − 2% for 2 min. TP-Link plug has separate UI automations at 75%/65%.
ON: RH < target − 5% for 3 min. OFF: RH > target + 2% for 2 min.
Pulsed: 1 min on / 4 min off. Inject: < 1100 ppm. Stop: ≥ 1400 ppm. Emergency off: ≥ 1800 ppm. Always off at lights-off. Only during lights-on.
4 units mapped. No automated control yet — manual or separate automations.
| Entity | Default | Range | Unit | What It Controls |
|---|---|---|---|---|
input_number.irrigation_temp_target_day | 26 | 18–32 | °C | Reference day temperature target (lights-on). AC units are mapped but not yet automated — this value is used for VPD reference calculations and future AC logic. Set it to match your AC manual setpoint so dashboards display correctly. |
input_number.irrigation_temp_target_night | 22 | 15–28 | °C | Reference night temperature target (lights-off). Cannabis typically runs 2–4°C cooler at night. Not currently driving AC automation. |
input_number.irrigation_rh_target_day | 55 | 40–75 | %RH | Target RH during lights-on. ON rule: RH > target + 5% for 3 min → all 4 dehumidifier relays stagger on (10s apart). OFF rule: RH < target − 2% for 2 min → relays off. Hysteresis band prevents rapid cycling. Typical veg: 55–65%, flower: 45–55%. |
input_number.irrigation_rh_target_night | 50 | 40–70 | %RH | Target RH during lights-off. Night targets are usually 5–10% lower than day to reduce mold risk — transpiration slows at night and moisture can accumulate on leaves. Dehumidifier and humidifier both switch to this target at lights_off_hour. |
input_number.irrigation_co2_target | 1200 | 800–1600 | ppm | Target CO2 concentration during lights-on. Inject: ppm < this value. Stop: ppm ≥ this value + 200 (e.g. 1400). Emergency off: ≥ 1800 ppm always. CO2 solenoid pulses 1 min on / 4 min off to avoid overshooting. Always off at lights-off. Ambient is ~420 ppm; enriched growing targets 1000–1400 ppm. Above 1600 ppm provides diminishing returns and wastes CO2. |
Every entity below is accessible from the dashboard or Developer Tools. Entities prefixed switch.crop_steering_, select.crop_steering_, number.crop_steering_, and input_* are user-configurable. Sensor entities are read-only and described in the lower half of this section.
| Entity | Default | What It Does | When to Change |
|---|---|---|---|
switch.crop_steering_system_enabled | ON | Master kill switch. When OFF, the AppDaemon loop stops all irrigation decisions immediately, closes all valves, and holds at safe-idle. No watering occurs under any condition — including emergencies — while this is OFF. | Turn OFF for extended maintenance, hardware work, or full manual control. Do not leave OFF if plants are unattended. |
switch.crop_steering_auto_irrigation_enabled | ON | Enables automatic phase-based irrigation decisions. When OFF, the phase logic still runs and sensor readings still update, but no valve opens automatically. Manual shots via button.crop_steering_zone_N_trigger_shot and emergency triggers still work when OFF. | Turn OFF to observe phase behavior without any watering, or to pause automatic decisions while keeping the system in monitoring mode. |
switch.crop_steering_ec_stacking_enabled | OFF | When ON, the system intentionally delivers smaller shots (or skips shots) when EC is below target, allowing nutrient concentration in the substrate to build up. This is a generative technique — high substrate EC creates mild stress that promotes flowering and resin production. When OFF, EC adjustments only go in the dilution direction (high EC = bigger flush shots). | Turn ON during late vegetative or early flower to drive generative steering. Leave OFF during veg when you want stable, consistent EC and vigorous growth. |
switch.crop_steering_zone_N_enabled | ON | Includes or excludes a zone from automatic scheduling and phase calculations. When OFF, the zone is skipped in all routine decisions. Emergency irrigation still fires for a disabled zone if VWC drops critically low — disabling does not override the safety net. | Turn OFF for unused tables, zones under transplant, or any table you don't want touched by the scheduler. |
switch.crop_steering_zone_N_manual_override | OFF | Absolute lockout. When ON, this zone receives zero water from any source — automatic, emergency, manual trigger, or ML-driven. This flag is checked at 4 separate code points: zone selection, phase evaluation, valve actuation, and the safety interlock. Nothing bypasses it. | Turn ON when a zone needs physical maintenance (hand-flushing, re-potting, dripper cleaning), when a probe is returning bad readings, or any time you need a hard guarantee of no-water for a zone. |
| Entity | Default | Options & What They Do |
|---|---|---|
select.crop_steering_crop_type | Cannabis_Athena | Sets the active crop profile, which defines EC target ranges, dryback percentage ranges, and shot size modifiers. Cannabis_Athena is tuned for Athena 2-part at higher EC (3–9 mS/cm). Cannabis_Indica: denser, shorter plants, slightly lower EC (2.8–8). Cannabis_Sativa: tall, stretchy, higher EC tolerance (3.2–9.5), deeper dryback. Cannabis_Hybrid: balanced defaults (3–8.5). Tomato / Lettuce: much lower EC, higher-frequency watering. |
select.crop_steering_growth_stage | Vegetative | Shifts EC targets and dryback aggressiveness across all zones simultaneously. Vegetative: EC target 3.0–3.2 mS/cm, dryback 50% of peak VWC. Frequent watering, wet substrate, maximum vegetative growth. Early_Flower: EC rises to ~3.5–5.0 mS/cm, dryback deepens to 40–45%. Initiates generative stress signal to promote flowering. Late_Flower: EC 5.0–6.0+ mS/cm, dryback 35–40%. Maximum generative pressure for resin, density, and ripening. |
select.crop_steering_zone_N_phase_override | Auto | Locks a zone into a specific phase regardless of sensor readings. Auto = normal sensor-driven progression (use this for normal operation). P0 = force morning dryback regardless of time. P1 = force ramp-up (useful after transplant or to manually trigger a wet-up). P2 = force maintenance mode. P3 = force pre-lights-off hold. Always return to Auto when done. |
select.crop_steering_zone_N_group | Ungrouped | Assigns a zone to a sync group (A/B/C/D). When ≥50% of zones in a group need water, all zones in the group irrigate together even if some don't technically need it. Keeps tables on identical schedules and simplifies management. Zones in the same physical row or on the same strain usually share a group. Only one zone irrigates at a time even within a group. |
select.crop_steering_zone_N_priority | Normal | Sets scheduling urgency. Score = Priority 40% + VWC Need 40% + Phase Urgency 20%. Priority weights: Critical = 4, High = 3, Normal = 2, Low = 1. Higher-scored zones get the next irrigation slot when multiple zones are waiting. Use Critical for large plants or high-value strains, Low for seedlings or plants needing minimal water. |
All prefixed number.crop_steering_. These apply to all zones unless a per-zone override is set.
| Entity (suffix) | Default | Range | Unit | What It Controls |
|---|---|---|---|---|
substrate_volume | 10 | 1–50 | L | Volume of substrate (coco) per pot. Used to convert a percentage shot into liters. Formula: Shot volume (L) = substrate_volume × shot_% ÷ 100. With 10 L substrate, a 5% shot = 0.5 L. A 25 L pot needs this set to 25 or the system will dramatically under-water. |
dripper_flow_rate | 1.2 | 0.5–10 | L/hr | Flow rate of one dripper. Combined with drippers_per_plant and plant_count this determines how many seconds a valve stays open to deliver the required volume. Formula: Duration (s) = Volume (L) ÷ (flow_rate × drippers_per_plant × plant_count) × 3600. Wrong flow rate = wrong valve-open time = wrong actual volume delivered. |
drippers_per_plant | 2 | 1–8 | count | Number of drippers per plant. Multiplied by flow_rate to get total delivery rate per plant. If you add a third dripper per pot, update this to 3 — otherwise the valve stays open too long and over-waters. |
field_capacity | 70 | 50–95 | %VWC | Maximum VWC the substrate should reach. This is a hard safety cap: if VWC ≥ field_capacity, the shot is skipped regardless of phase or EC. Fresh coco saturates around 70–80%. If your probes read higher due to sensor calibration, raise this value — but never above what your substrate physically holds or you'll mask over-watering. |
lights_on_hour | 8 | 0–23 | hour (24h) | Hour of day when lights turn on. Triggers the P0 morning dryback phase and activates CO2 injection. Must match your actual light schedule exactly. If set 1 hour too late, P3 persists and plants miss their morning dryback window. If too early, P0 starts before lights are on. |
lights_off_hour | 20 | 0–23 | hour (24h) | Hour of day when lights turn off. Triggers transition to P3, cuts CO2, and tells the ML predictor how many overnight hours remain for dryback. Must match your timer or smart plug schedule. Mismatch causes the ML to mis-predict whether there's enough dryback time before lights-on. |
p1_target_vwc | 65 | 50–85 | %VWC | VWC level that P1 ramp-up aims to reach before switching to P2 maintenance. P1 exits when VWC ≥ this value AND at least p1_min_shots (3) have been delivered. Higher = wetter substrate entering the day (more vegetative). Lower = drier entry to maintenance (more generative). Typical veg: 65–70%, early flower: 60–65%, late flower: 55–62%. |
p2_vwc_threshold | 60 | 40–80 | %VWC | VWC level that triggers a P2 maintenance shot. When VWC drops below this during P2, a 5% shot fires. Should sit 3–10% below p1_target_vwc to give natural swing room. If set too close to the P1 target, the system constantly fires shots trying to maintain it. If set too low, plants get dry spells mid-day. |
p3_emergency_vwc_threshold | 40 | 20–60 | %VWC | VWC level that triggers a rescue shot during P3 (lights-off period). Normally plants dry back overnight — this threshold should be low enough to allow normal dryback without triggering, but high enough to catch wilting risk. A zone hitting 40% VWC overnight usually indicates something is wrong (large plants, hot night, sensor issue). |
All prefixed number.crop_steering_zone_N_. Set to 0 to inherit the system-wide default above. Any value > 0 overrides just that zone. Use when one table has different pot sizes, dripper counts, plant sizes, or strain needs.
| Entity (suffix) | Default | Unit | What It Overrides |
|---|---|---|---|
zone_N_plant_count | 4 | plants | Plant count for this zone. Directly affects valve-open time calculation. A zone with 6 plants has more drippers flowing simultaneously, so the valve opens for less time to deliver the same volume. Always update when plant count changes. |
zone_N_max_daily_volume | 20 | L | Maximum total water for this zone per calendar day (resets at midnight). Once hit, no further shots are delivered that day regardless of VWC. Safety ceiling against sensor malfunction or runaway watering. Raise to 25–35 L for large plants in peak veg or flower with high transpiration demand. |
zone_N_shot_size_multiplier | 1.0 | × | Scales all shot sizes for this zone. 1.2 = all shots 20% larger. 0.8 = all shots 20% smaller. Use to compensate for a zone with faster-draining substrate, different dripper spacing, or a VWC sensor that reads differently from the others. Adjust in 0.1 increments and observe VWC response. |
zone_N_dryback_target | 0 (system) | %VWC | How far VWC must drop from its morning peak before P0 exits and watering begins. Deeper dryback = more root zone oxygen = more generative stress. 0 = use the growth-stage default. Set a positive value (e.g. 45) to give this zone a custom dryback independent of the global growth stage setting. |
zone_N_p1_target_vwc | 0 (system) | %VWC | VWC level the P1 ramp-up targets for this zone before switching to P2. 0 = use system p1_target_vwc. Override for zones with smaller pots that saturate faster, or strains needing a wetter/drier starting point. |
zone_N_p2_vwc_threshold | 0 (system) | %VWC | VWC trigger for P2 maintenance shots for this zone. 0 = use system p2_vwc_threshold. Override for zones on a different substrate or with sensors that read 5–10% off from others. |
zone_N_p3_emergency_vwc | 0 (system) | %VWC | Emergency rescue VWC threshold for this zone during lights-off. 0 = use system p3_emergency_vwc_threshold. |
button.crop_steering_zone_N_trigger_shot — Delivers one P2-sized shot (5% of substrate volume) immediately. Blocked only if zone_N_manual_override is ON. Works even when auto irrigation is disabled. Useful for manually topping off a zone, testing valve operation, or recovering after a missed irrigation window. The shot is logged to daily volume.
| Entity | Purpose & Detail |
|---|---|
input_boolean.irrigation_enable | GW package master switch. Disabling stops the GW time-based scheduler and all GW environment automations simultaneously. Does not affect Crop Steering. |
input_boolean.irrigation_irrigation_enabled | Must be OFF when Crop Steering is active. Enables the GW time-based irrigation scheduler. When ON, GW will open valves on its own timer regardless of what Crop Steering is doing, causing double-watering. |
input_boolean.irrigation_maintenance_mode | Emergency close. When toggled ON, immediately closes all valve switches and blocks any further GW irrigation. Use when you need to physically work on the water system and want a software-level guarantee nothing opens. Turn OFF to resume normal operation. |
input_boolean.irrigation_co2_enabled | Enables the CO2 injection automation. When OFF, the CO2 solenoid will not open regardless of room ppm. Use to disable CO2 without touching the irrigation_co2_target setting. |
input_boolean.irrigation_environment_enabled | Gates all GW climate automations: dehumidifiers, humidifier, and CO2 (CO2 also requires its own toggle). Turn OFF to fully disable climate control, e.g. during HVAC maintenance or when running the room manually. |
VWC (Volumetric Water Content) tells you how full of water your substrate is, as a percentage of its total volume. A reading of 60% means 6 out of every 10 litres of your coco coir is water. Your GroundWork probes measure this by reading how easily electricity passes through the medium — wet coco conducts much better than dry coco.
EC = Electrical Conductivity. Your probes measure pWEC (Pore Water EC) — the nutrient concentration in the actual water sitting in your substrate right now. Higher number = more concentrated nutrients. Units are mS/cm. Think of it like this: pure water = 0. Tap water = ~0.3–0.8. A proper nutrient solution = 2–6+. A salt flat = very high.
Nutrients being used faster than replenished. Plants may yellow or show deficiencies. Check feed concentration.
Healthy range for vegetative growth with Athena nutrients. System aims to hold EC here during P2 maintenance.
Higher concentration for flowering. Mild stress from elevated EC promotes resin production. Target rises with Late_Flower stage.
Salt buildup risk. System fires bigger shots to dilute. Plants may struggle to absorb water. Check drainage and feed ratio.
Safety ceiling. Irrigation may pause. Likely salt accumulation from poor drainage or over-feeding. Manual flush required.
Every time the system decides to water, it converts a percentage into an exact valve-open time. You need to set substrate_volume, dripper_flow_rate, and drippers_per_plant correctly or it will over/under-water even if the phase logic is perfect.
sensor.crop_steering_system_health_scoreA composite 0–100 score reflecting overall system health. 100 = all systems nominal, 0 = critical failure. Calculated from four 25-point components:
| Component | Points | What Reduces It |
|---|---|---|
| Sensor Health | 0–25 | Probes offline, stale (>10 min since last reading), or flagged faulty by the Kalman filter outlier check. Each degraded or offline sensor deducts proportionally. All 6 zones healthy = full 25 pts. |
| Phase Health | 0–25 | Zones stuck too long in a phase (P0 >4 h, P1 >3 h without VWC progress), phase mismatches vs. time of day, or zones that never advanced past P0 since lights-on. |
| Irrigation Health | 0–25 | VWC not responding to shots (possible clog or sensor issue), emergency shots firing, zones hitting their daily volume ceiling, or shot count exceeding 20/day per zone. |
| EC Health | 0–25 | EC ratio outside the 0.8–1.2 normal band, EC trending upward across multiple consecutive readings, or substrate EC approaching the maximum safe limit (9.0 mS/cm). |
| Score | Status | Suggested Action |
|---|---|---|
| 90–100 | Excellent | No action needed. |
| 75–89 | Good | Monitor. Minor degradation — check for trends. |
| 60–74 | Fair | Investigate. Check sensor status and EC ratios. |
| 40–59 | Poor | Active issues. Check for stuck phase, offline probe, or EC drift. |
| <40 | Critical | Immediate review. Hardware or sensor failure likely. |
Health Score Breakdown — 4 components, 25 pts each
sensor.crop_steering_zone_N_safety_status| Value | Meaning & Action |
|---|---|
safe | All 15 safety checks pass. Normal operation. |
approaching_saturation | VWC > 75% (nearing field capacity). Shots are skipped until VWC naturally drops. No action needed unless it persists for hours — if so, the sensor may be reading high. |
emergency | VWC dropped below the emergency threshold. A rescue shot has fired or is queued. Check why VWC dropped — large plant demand, missed P2 shot, or sensor spike are common causes. |
locked_out | Zone received 4+ consecutive emergency shots with <1% VWC lift each time. Permanent lockout activated — the system gave up trying because nothing is working. Likely cause: clogged dripper, no water pressure, or sensor malfunction. Fix the physical issue then fire crop_steering_reset_emergency event with {zone: N}. |
sensor_error | VWC or EC probe is offline, stale, or returning out-of-range values. Zone holds its current state. Check ESPHome device for the relevant probe. |
max_volume_reached | Zone hit its zone_N_max_daily_volume limit for today. No more shots until midnight. If this fires regularly, the daily volume limit needs to be raised or something is causing excessive shots. |
sensor.crop_steering_app_status| Value | Meaning |
|---|---|
safe_idle | AppDaemon running normally. No active irrigation. System is monitoring sensors and evaluating phase transitions every 60 s. |
irrigating_zone_N | Zone N valve is currently open. Only one zone at a time. Typical duration: 3–15 min per shot depending on shot size. |
starting | App just restarted. 120-second startup grace period — emergency irrigation is suppressed during this window to avoid false triggers on stale sensor data. |
error | AppDaemon encountered an unhandled exception. Hardware is safe — the finally block closes all valves on any error. Check AppDaemon logs at Settings → Add-ons → AppDaemon → Log. |
sensor.crop_steering_zone_N_phaseCurrent phase for this zone: P0, P1, P2, or P3. If this shows an unexpected phase (e.g. stuck in P3 mid-morning), verify that lights_on_hour matches the actual light schedule and that select.crop_steering_zone_N_phase_override is set to Auto.
sensor.crop_steering_zone_N_daily_water_appLiters delivered to this zone today (resets at midnight). Compare against zone_N_max_daily_volume to see headroom. Healthy reference: 4 large plants in veg typically consume 15–25 L/day. Consistently near the daily limit suggests you need to raise the cap. Consistently very low (<5 L) suggests the zone may be stuck in P0/P3 or the VWC threshold is set too low to trigger shots.
sensor.crop_steering_fused_vwc & _fused_ecKalman-filtered weighted averages across all active zone sensors. Each probe is weighted by its reliability score (0.0–1.0): excellent sensors get 1.2× weight, degraded sensors get 0.8×. These fused values are not used for per-zone irrigation decisions (each zone uses its own probe exclusively), but are used by the AI heartbeat for system-level anomaly detection and on the dashboard overview cards.
sensor.crop_steering_ai_heartbeatUpdates every 15 minutes. The sensor value is a summary string; the entity attributes contain a per-zone breakdown: current phase, VWC %, EC ratio, shot count, and any flagged anomalies. Anomalies listed in attributes: p0_stuck (P0 >4 h), sensor_stale (>15 min), no_vwc_response (VWC not rising after shots), ec_trending_up, excessive_shots (>20/day), override_long (>24 h). Zero anomalies = healthy.
| Event | Payload | What It Does |
|---|---|---|
crop_steering_trigger_zone_shot | {zone: N, source: "manual"} | Fires one immediate shot for the specified zone. Same as the dashboard trigger button. Blocked by manual_override. |
crop_steering_reset_emergency | {zone: N} or omit zone for all | Clears the emergency lockout counter and permanent lockout flag for the specified zone(s). Use after fixing the physical issue that caused 4+ failed emergency shots. |
crop_steering_manual_override | {zone: N, action: "enable"/"disable", timeout_minutes: 60} | Programmatically sets or clears a timed manual override. The override auto-releases after timeout_minutes. AI heartbeat flags overrides active longer than 24 h. |
ZONE_1_SWITCH=switch.irrigation_table_1_valve
ZONE_1_VWC_SENSORS=sensor.substrate_1_substrate_1_vwc_coco_coir
ZONE_1_EC_SENSORS=sensor.substrate_1_substrate_1_pwec
ZONE_1_PLANT_COUNT=4
ZONE_1_MAX_DAILY_VOLUME=20.0
ZONE_1_SHOT_MULTIPLIER=1.0
master_crop_steering:
module: crop_steering.master_crop_steering_app
class: MasterCropSteeringApp
hardware:
pump_master: null
main_line: switch.irrigation_mainline
zone_valves: {1: switch.irrigation_table_1_valve, ...}
timing: {phase_check_interval: 60}
thresholds: {emergency_vwc: 40.0}
Lights: lights_on_hour = 8, lights_off_hour = 20.
| Dashboard | Path | Purpose |
|---|---|---|
| GW Irrigation | gw-irrigation | Timer scheduler, table enables, hardware, safety |
| Crop Steering AI | crop-steering | Phases, VWC/EC, zone tuning, AI oversight, all parameters |
| CO2 & Environment | co2-environment | CO2, dehumidifier, temp/RH targets |
| zone_N_enabled | zone_N_manual_override | |
|---|---|---|
| Purpose | Include/exclude | Absolute lockout |
| Emergency? | Still gets emergency | No. Nothing waters. |
| Use for | Zone unused | Maintenance, flushing |
Groups: A/B/C/D. ≥50% needing water → all irrigate. Max 1 concurrent zone.
Priority: Critical(4) > High(3) > Normal(2) > Low(1). Score = Priority 40% + VWC Need 40% + Phase Urgency 20%.
These are the underlying numeric parameters that drive phase behavior. Most are accessible as number.crop_steering_* entities. Adjusting these allows fine-grained control of the irrigation strategy beyond just changing growth stage.
| Parameter | Default | Range | What It Does & Why You'd Change It |
|---|---|---|---|
veg_dryback_target | 50% | 20–80% | How far VWC must fall from its morning peak before P0 exits and P1 starts. Expressed as % of peak, not absolute VWC. If peak VWC is 70% and this is 50%, P0 exits when VWC reaches 35% (70 × 0.50 = 35). Higher value = deeper dryback = more generative stress. Lower = shallower, quicker exit to watering. Vegetative: 45–55%. Early flower: 40–50%. Late flower: 35–45%. |
gen_dryback_target | 40% | 20–70% | Same as above but used when growth stage is Early_Flower or Late_Flower. Allows the system to automatically apply a different (deeper) dryback in flowering without manual adjustment. The crop profile's generative setting kicks in automatically when you change the growth stage dropdown. |
p0_min_wait | 30 min | 10–180 min | Minimum time P0 must run before it can exit, even if the dryback target is already met. Prevents instant P0 exit if sensors are fluctuating or if VWC was already low at lights-on. Ensures at least some dryback window occurs every morning. |
p0_max_wait | 120 min | 30–360 min | Maximum time P0 is allowed to run before it is forced to exit to P1 even if the dryback target was never met. Safety valve against substrate that dried too slowly, miscalibrated sensors, or cool/humid conditions where evapotranspiration is very slow. The AI heartbeat also independently forces P1 at 4 h. |
p0_dryback_drop | 15% | 5–40% | Minimum absolute VWC drop (not relative to peak) required before P0 can exit. Even if the relative target is met, VWC must have dropped by at least this many percentage points from peak. Prevents P0 from exiting if the substrate started very dry and the relative target would be met after only a tiny drop. |
| Parameter | Default | Range | What It Does & Why You'd Change It |
|---|---|---|---|
p1_initial_shot | 2% | 1–10% | Size of the very first shot when P1 begins. Starting small avoids shocking a dry root zone with too much water at once, and gives the sensor time to confirm VWC is actually rising. Smaller initial shots are better for fine coco and sensitive strains. Larger if substrate drains very fast and needs more water to register a reading. |
p1_increment | 0.5% | 0.1–3% | How much each successive P1 shot grows beyond the last. Shot size = p1_initial_shot + (p1_increment × shot_count). Progressive ramping avoids over-saturating the substrate at the start of the day. With defaults: shot 1 = 2%, shot 2 = 2.5%, shot 3 = 3%, etc. Lower increment = more gradual ramp. Higher = faster wet-up. |
p1_max_shot | 10% | 3–20% | Cap on how large a single P1 shot can grow. Even as the progressive formula increases shot size, it cannot exceed this value. Prevents a runaway ramp from over-saturating late in P1 if VWC is slow to respond. |
p1_time_between | 15 min | 5–60 min | Cooldown between P1 shots. After a shot fires, the system waits this long before the next shot even if VWC hasn't reached target yet. Allows the substrate and sensor to equilibrate after each shot — coco can take 5–15 min to fully absorb a shot and for VWC readings to stabilize. Reduce if sensors stabilize quickly; increase if readings spike then drop. |
p1_min_shots | 3 | 1–10 | Minimum number of P1 shots that must be delivered before P1 can exit to P2, even if p1_target_vwc is already met. Ensures some ramp-up happens even if the substrate started near the target. Prevents instant P1-to-P2 transition on sensor noise. |
p1_max_shots | 6 | 3–20 | Maximum P1 shots before P1 is forced to exit to P2 even if target VWC was never reached. Safety cap. If P1 fires 6 shots and VWC still hasn't hit 65%, something is wrong (possible clog, or target is too high for this substrate). After 6 shots, the system moves on to P2 maintenance mode. |
P1 Shot Progression — shots get progressively larger (default settings: 10 L substrate, 4 plants, 2 drippers × 2 L/hr each)
| Parameter | Default | Range | What It Does & Why You'd Change It |
|---|---|---|---|
p2_vwc_threshold | 60% | 40–80% | VWC trigger for maintenance shots. When VWC drops below this level during P2, a shot fires. This is the "keep full" level for the day. Should be 3–10% below p1_target_vwc to allow natural VWC swing without constant triggering. Raising this makes the system water more frequently (wetter/vegetative). Lowering gives more dryback between shots during the day (drier/generative). |
p2_shot_size | 5% | 1–20% | Size of each P2 maintenance shot as a percent of substrate volume. Larger shots mean less-frequent irrigation with bigger swings in VWC. Smaller shots mean more frequent, gentler top-ups. For high-frequency irrigation (5–10 shots/day), smaller shots (3–5%) are typical. For low-frequency (2–4 shots/day), larger shots (7–10%) are used. |
p2_ec_high_ratio | 1.2 | 1.0–3.0 | EC ratio threshold above which shots are enlarged. If substrate_EC ÷ target_EC > 1.2, the shot is scaled up by 1.5× to flush excess salts. Lowering this threshold makes the system flush more aggressively at lower EC ratios. Raise it if EC naturally runs slightly above target and you don't want constant larger shots. |
p2_ec_low_ratio | 0.8 | 0.3–1.0 | EC ratio threshold below which shots are reduced. If substrate_EC ÷ target_EC < 0.8, the shot is scaled down by 0.7× to conserve nutrients and let EC build. Only active when ec_stacking_enabled is ON. If ec_stacking is OFF, low EC has no effect on shot size. |
| Parameter | Default | Range | What It Does & Why You'd Change It |
|---|---|---|---|
p3_emergency_vwc | 40% | 20–60% | VWC level that triggers an emergency rescue shot during P3. Normally plants dry overnight without any irrigation. If VWC drops below this threshold, it means the plant is at risk of wilting stress. The threshold must be low enough to allow a full overnight dryback without triggering, but high enough to catch genuine emergency depletion. Raise if you have large plants with high overnight demand; lower if your substrate holds water well overnight. |
p3_emergency_shot | 2% | 1–10% | Size of the rescue shot fired during P3 emergencies. Small by design — just enough to pull the plant back from the edge without disrupting the overnight dryback trajectory. Larger shot = more dryback interrupted. Keep this small (2–4%) and rely on the next morning's P1 for full wet-up. |
| Ratio | Response |
|---|---|
| < 0.8 | Reduce shot (conserve) |
| 0.8–1.2 | Normal |
| > 1.2 | Increase shot (flush) |
| > 2.0 (P3) | Emergency shot |
| > 2.5 (P0) | Flush during dryback |
| Phase | Veg | Gen |
|---|---|---|
| P0 | 3.0 | 4.0 |
| P1 | 3.0 | 5.0 |
| P2 | 3.2 | 6.0 |
| P3 | 3.0 | 4.5 |
| Flush | 0.8 | |
The system doesn't just react to the raw EC number — it divides your measured substrate EC by the phase target EC to get a ratio. A ratio of 1.0 means you're exactly on target. The ratio tells it whether to water more (flush salts) or less (let EC build). This is what the ratio means visually:
EC Ratio = Measured Substrate EC ÷ Phase Target EC
0 = use system default. > 0 overrides for that zone.
| Target | System | Per-Zone |
|---|---|---|
| Dryback | veg_dryback_target | zone_N_dryback_target |
| P1 VWC | p1_target_vwc | zone_N_p1_target_vwc |
| P2 threshold | p2_vwc_threshold | zone_N_p2_vwc_threshold |
| P3 emergency | p3_emergency_vwc_threshold | zone_N_p3_emergency_vwc |
VWC < 40% → 60s shot. 120s startup grace. 300s cooldown. Skips overridden zones.
| Count | Lockout |
|---|---|
| 1st | 2h |
| 2nd | 4h |
| 3rd | 8h |
| 4th+ | Permanent |
VWC lift < 1% over 3+ shots → sets ceiling. Reset: crop_steering_reset_emergency event.
Every 15 min. 6 checks:
| # | Anomaly | Action |
|---|---|---|
| 1 | P0 stuck >4h | Force P1 |
| 2 | Sensor stale >15m | Warning |
| 3 | VWC no response | Set ceiling |
| 4 | EC trending up | Warning |
| 5 | >20 shots/day | Warning |
| 6 | Override >24h | Warning |
Watchdog (60s): flag/hardware mismatch detection. Sensor fail-safe: all offline + hardware on → stop. Clean shutdown on terminate.
Safety Gate — every shot request passes through ALL of these checks in order. Any single NO = shot is blocked, no exceptions.
flowchart LR
REQ(["💧 Shot\nRequested"])
C1{"System\nEnabled?"}
C2{"Manual\nOverride\nOFF?"}
C3{"Auto\nIrrigation\nEnabled?"}
C4{"VWC below\nField Cap?"}
C5{"Daily Volume\nunder limit?"}
C6{"10+ min\nsince last\nshot?"}
C7{"EC under\nmax 9.0?"}
C8{"Under 50\nshots today?"}
FIRE(["✅ Valve\nOpens"])
STOP(["🚫 Shot\nBlocked"])
REQ --> C1
C1 -- "NO" --> STOP
C1 -- "YES" --> C2
C2 -- "Override ON\nabsolute block" --> STOP
C2 -- "OFF" --> C3
C3 -- "NO\n(manual shots\nstill allowed)" --> C4
C3 -- "YES" --> C4
C4 -- "Already full\nor over 90%" --> STOP
C4 -- "YES" --> C5
C5 -- "Daily limit\nhit" --> STOP
C5 -- "OK" --> C6
C6 -- "Too soon" --> STOP
C6 -- "OK" --> C7
C7 -- "Too high" --> STOP
C7 -- "OK" --> C8
C8 -- "Too many" --> STOP
C8 -- "OK" --> FIRE
style REQ fill:#1c2128,stroke:#58a6ff,color:#e6edf3
style STOP fill:#2d0f0f,stroke:#f85149,color:#f85149
style FIRE fill:#0f2d1b,stroke:#3fb950,color:#3fb950
zone_N_manual_override. 2) It already watered too recently — 10-minute cooldown. 3) Zone hit its daily volume limit — check zone_N_max_daily_volume. 4) VWC probe is offline or stuck — check ESPHome device status. 5) VWC is already above field capacity — it doesn't need water yet.| Protection | What |
|---|---|
| Valve watchdog | Open >180s → force close + alert |
| Mains watchdog | Open >24 min → kill |
| Leak abort | Emergency stop (sensor unmapped*) |
| Tank low | Close all (sensor unmapped*) |
| Maintenance mode | Close all immediately |
| Sensor offline | Alert after 10 min. CO2 offline → close solenoid. |
| Daily audit 3 AM | Force-close all + CO2 |
| CO2 lights-off | Always close at lights off |
* Leak and tank-low are hardcoded false. Won't fire until sensors installed.
Stale: 600s (10m). AI flags: 900s (15m).
| Profile | Dryback | EC | Best For |
|---|---|---|---|
| Cannabis_Athena | 15–25% | 3–9 | Athena nutrients |
| Cannabis_Indica | 12–22% | 2.8–8 | Dense, short |
| Cannabis_Sativa | 18–30% | 3.2–9.5 | Tall, stretchy |
| Cannabis_Hybrid | 15–25% | 3–8.5 | General |
| Tomato | 8–15% | 2–4.5 | High-freq |
| Lettuce | 5–10% | 1.2–2.5 | Very high-freq |
Stages: Vegetative → Early Flower → Late Flower. Adaptive: 0.1 rate, 0.8 momentum, min 10 samples.
Saved 5 min + on irrigation. Restores: phases (corrected for lights), P0/P1 tracking, water usage (same-day/week), overrides (with timeout), last irrigation. 10MB limit.
sensor.irrigation_table_1_vwc shows number. Check all 6.crop_steering.env entity IDsirrigation_irrigation_enabled OFFirrigation_co2_enabled and irrigation_environment_enabledcrop_steering_system_enabled ONapp_status = safe_idle| Symptom | Fix |
|---|---|
| Zone not watering | Check override OFF, enabled ON. Reset emergency if locked out. |
| Too many shots | Dripper blocked or sensor bad. Auto-lockout after 4 rapid shots. |
| Stuck in P3 | Check lights_on/off_hour match schedule. |
| Stuck in P0 | Lower dryback target. AI forces P1 after 4h. |
| Phase changes ignored | Zone phase override must be "Auto". |
| "Entity not found" | Entities have crop_steering_ prefix. |
| VWC "Unavailable" | ESPHome device offline. Check network. |
| Double watering | irrigation_irrigation_enabled must be OFF. |
| CO2 not injecting | Lights must be on + irrigation_co2_enabled ON. |
| Valves stuck | irrigation_maintenance_mode ON, or script.irrigation_emergency_stop, or power off relay. |
irrigation_maintenance_mode ONscript.irrigation_emergency_stopcrop_steering_system_enabled OFF| Item | Impact |
|---|---|
| Leak sensor | CRITICAL: Abort hardcoded false |
| Tank float switch | CRITICAL: Abort hardcoded false |
| Tank pH/EC | Unavailable on dashboard |
| Waste valve, ceiling vent | TODO placeholders |
| AC automation | Entities mapped, no logic |
crop_steering_ prefix. Dashboard/AppDaemon reference some without. Dual-naming fallback in place.10L substrate, 2 L/hr drippers, 2 per plant, 4 plants:
| Type | Size | Time | Volume |
|---|---|---|---|
| P1 initial | 2% | 3 min | 0.8L |
| P2 maintenance | 5% | 7.5 min | 2.0L |
| P0 flush | 10% | 15 min | 4.0L |
Notifications: notify.mobile_app_damians_iphone.
Crop Steering System Guide v2.3.1 — April 2026 — Verified from codebase audit