# HearthNodes recipe 22 - soil moisture irrigation
alias: "Water - irrigation vetoed by wet soil"
id: hn_water_soil_moisture_irrigation
description: "Six probes veto their zone's scheduled run, duration scaled by deficit"
mode: single
max_exceeded: silent
trigger:
- platform: time
at: "05:10:00"
id: group_a
- platform: time
at: "05:40:00"
id: group_b
- platform: time
at: "06:10:00"
id: group_c
variables:
zones:
- { id: 1, valve: valve.zone_1, probe: sensor.soil_moisture_beds, threshold: 22, base_min: 9 }
- { id: 2, valve: valve.zone_2, probe: sensor.soil_moisture_hedge, threshold: 18, base_min: 14 }
- { id: 3, valve: valve.zone_3, probe: sensor.soil_moisture_pots, threshold: 25, base_min: 6 }
rain_recent: "{{ states('sensor.rain_last_6h_mm') | float(0) }}"
rain_forecast: "{{ states('sensor.rain_forecast_3h_mm') | float(0) }}"
condition:
- condition: numeric_state
entity_id: sensor.rain_last_6h_mm
below: 0.5
- condition: numeric_state
entity_id: sensor.rain_forecast_3h_mm
below: 0.5
- condition: numeric_state
entity_id: sensor.irrigation_pressure_bar
above: 1.5
- condition: state
entity_id: input_boolean.irrigation_suspend
state: "off"
action:
- repeat:
for_each: "{{ zones }}"
sequence:
- variables:
moist: "{{ states(repeat.item.probe) | float(99) }}"
deficit: "{{ [[repeat.item.threshold - moist, 0] | max / repeat.item.threshold, 0.3] | min }}"
minutes: "{{ [[repeat.item.base_min * (1 + deficit * 2), repeat.item.base_min * 0.4] | max, repeat.item.base_min * 1.3] | min | round(0) }}"
- choose:
- conditions:
- condition: template
value_template: "{{ moist < repeat.item.threshold }}"
sequence:
- service: valve.open_valve
target:
entity_id: "{{ repeat.item.valve }}"
- delay: "{{ '00:%02d:00' | format(minutes) }}"
- service: valve.close_valve
target:
entity_id: "{{ repeat.item.valve }}"
- conditions: []
sequence:
- service: logbook.log
data:
name: "irrigation skipped"
message: "zone {{ repeat.item.id }} soil {{ moist }} percent, wet"
- service: sensor.record
data:
entity_id: sensor.water_used_irrigation
note: "compare litres delivered against litres commanded"
# Probe unavailable beyond 48 hours -> that zone drops to 60 percent of nominal.
# Flow meter disagreeing with commanded volume by more than 25 percent skips the
# zone next cycle and names it: usually a split dripper line or a stuck valve.
触发器 · time
time
分区计划启动时间:05:10、05:40、06:10,每个分区组一个。
分步说明
- valve.zone_[n]按土壤低于阈值的程度缩放运行时长,限制在标称时长的 40 % 到 130 % 之间。
open · duration {{ base_minutes * moisture_factor }} - valve.zone_[n]跳过分区时记录探头读数,方便秋天回顾整个夏季的决策。
skip · log reason 'soil wet' - sensor.water_used_irrigation记录各分区每日用水量,使阀门内漏表现为「没有阀门开启却有流量」。
log · daily litres per zone - input_boolean.irrigation_suspend任一探头读数高于 35 % 时,整套计划暂停 48 小时;这样不必依赖降雨数据也能应对真正大雨后的那天。
on · 48 h after any probe shows > 35 %
让每个探头对着它自己的土壤标定
电容式探头在砂壤土和黏土中的读数完全不同。从该区抓一把土,烘干后记录探头的干燥原始值;再把土饱和后记录湿润值。两者之间就是你的标尺。跳过这一步,阈值就落在了没有意义的单位上。
清晨浇水,而且只在土壤允许时浇
05:10–06:10 能最大程度减少蒸发,并让叶面在傍晚前变干。在这个时间窗内,由探头决定该区到底浇不浇。忽略探头的计划会在雨后的日子浪费水,而那恰恰是过浇对根系伤害最大的时候。
看流量计,不看控制器
控制器报告「3 区运行 8 分钟」只代表它发出了命令。流量计才能告诉你 96 升水是否真的送出去了。这个区别能抓出破裂的管路、冬天过后忘记打开的手动阀,以及被当季第一波硬水堵住的滴头。
硬件
| 型号 | 协议 | 连接 | 在本链路中的作用 |
|---|---|---|---|
| Capacitive soil-moisture probes at 15 cm depth | zigbee |
无线 | 必须用电容式,不用电阻式:电阻式探头几周内就会腐蚀,且随着失效读数会单向漂高。 |
| Zigbee multi-zone irrigation controller | zigbee |
有线 | 六分区控制器,支持逐区时长命令;控制器自带的雨量传感器输入保持不接,让自动化掌握决策权。 |
| Inline flow meter on the irrigation main | zigbee |
有线 | 确认每个分区确实输出了指令规定的升数,据此可以发现滴灌管堵塞。 |
失效时怎么办
如果某个探头超过 48 小时不可用,该区切换到保守模式,按标称时长的 60 % 运行,既能保植物存活又不至于浇水过多。如果流量计与指令水量相差超过 25 %,下一次循环跳过该区,并在通知中点名——通常是滴灌管破裂或阀门卡滞。