Ok, I admit it: I generally excel at killing plants largely due to neglect. Sure, I could claim work, cooking, hobbies, etc. If I’m honest, I generally forget to water them. So, I wanted to implement an automated watering system linked to home automation. I wanted to do it on the cheap. This was also a test, so if needed a throw-away experiment. Turns out it works for few (3) herb-plants that I needed to keep alive.
- D1_Mini (Wemo, ESP8266).
- Kit link.
- 5v Relay
- 5v Pump
- Capacitive Soil Moisture Sensor
- We’ll install firmware via ESPHome plus HomeAssistant
- Outdoor Box
- 5V Micro-USB (leftover phone charger
- One momentary push-button (manual over-ride)
- Use cheap-as possible parts
- Software should:
- a) Water daily – but only if soil sensor says plant needs it
- b) Report back to HomeAssistant and notify that things are working correctly.
- c) Store last-triggered timings.
- While relays can support 5V, D1_Mini Wemos signal at 3V. So, wire all IN, VCC for 3V, and you will be happy. For other ESP32 or ESP8266 “your mileage may vary.”
- In ESPHome, ensure you live by either GPIO nomenclature for pin-mappings OR D1, D4, D syntax. This will save you getting lost when you think pins are mapping appropriately, but may not be. I’m the dummie who got lost. Don’t be a dummie.
James Callaghan (@jcallaghan) has been a value contributor to collaborate on this project. We effectively built this together. So, huge shout-out and virtual hug. One days we will raise a beer together (that I buy).
Otto Winter(@OttoWinter) also needs to be thanked. My mind was rocked when I learned how cool ESPHome is, and the enormous capability it has. The entire community is thankful for his contribution, but I’m trying to extend my gratitude here. Truly thankful!
Basically, here is the simple wiring. One addition that I added was the use of a manual button to toggle on/off for manual over-rides.
Again, picture shows NodeMCU board, however I used D1_Mini which signals at 3V, while pump operates at 5V. So, ensure you are wiring IN, VCC, GND to 3V on D1_Mini (see Don’t Be a Dummie).
This project is strongly coupled to ESPHome (thank you again Otto) which is an add-on to HomeAssistant. If you are not familiar with both, I highly recommend you invest the time on HomeAssistant (general home automation hub) first, learn pains of YAML (“damn you strict indenting!”), and then ESPHome (firmware compilation/management).
The core of this project is to:
- Have a ESP8266 (D1_Mini) “controller” plus sensor and pump
- Have the “controller” use some inputs (when to pump) and store some values back in HomeAssistant (Date and Time last pumped).
Understanding that the “controller” and HomeAssistant work together is key. The software for this project is driven primary by ESPHome. We’ll get to that in a minute. On HomeAssistant side, there are several Input_Boolean’s, and Input_DateTime values that are used.
The UI looks like this:
To achieve the UI we put in some Input_Booleans, Sensors, etc. I used “Packages” in HomeAssistant, so each of these becomes its own file. I have adopted Frenck’s technique on Packages, and separate things out in Input_Booleans folder, Input_Select, Sensors, etc. Check Frank’s GitHub config to see the approach. You may have these Input_Booleans elsewhere often found in your configuration.yaml.
zone_enabled.yaml
zone1_enabled:
name: Automatic Watering Enabled
icon: mdi:watering-can
zone1_switch.yaml
zone1_switch:
name: Pump
icon: mdi:pump
zone1_water_available.yaml
zone1_water_available:
name: Water Available
icon: mdi:water-off
Input_datetime esph_watering_d1_mini_triggered.yaml
esph_ watering_d1_mini_triggered:
has_date: true
has_time: true
zone1_watering_scheduled_time.yaml
zone1_watering_scheduled_time:
name: Schedule Time
icon: mdi:clock
has_date: false
has_time: true
Input_number zone1_pump_run_time.yaml
zone1_pump_run_time:
name: Pump Run Time
min: 0
max: 15
step: 1
unit_of_measurement: "seconds"
zone1_moisture.yaml
zone1_moisture:
name: Moisture Level
min: 0
max: 100
step: 1
unit_of_measurement: "%"
mode: box
zone_1_auto_watering.yaml
alias: 'Zone 1 Watering' – zone_1_auto_watering.yaml
id: 8fe93cfc-d8f4-4242-8679-94e22fc9d0ee
trigger:
- platform: template
value_template: "{{ states('sensor.time') == (state_attr('input_datetime.zone1_watering_scheduled_time','timestamp') | int | timestamp_custom('%H:%M', False)) }}"
condition:
condition: and
conditions:
# Only run once a day (86400 seconds in a day).
# //TODO Consider using an input_datetime helper to avoid issues with Home Assistant restarts.
# //TODO Move throttling time to a input_number help and allow the throttling to be changed in the UI [Done].
# //TODO This might also need to be considered as a trigger too.
#- condition: template
# value_template: "{{ (as_timestamp(states.sensor.date_time.last_changed) - (as_timestamp(state_attr('automation.zone_1_watering','last_triggered')))) > (states('fake_zone1_time_between_watering') | int * 60) }}"
# Watering must only occur whe the moisture is below the moisture low threshold.
- condition: template
value_template: "{{ states('sensor.esph_austria_watering_d1_mini_soil_moisture_percent') | int < states('input_number.zone1_moisture_threshold') | int }}"
# This automatic water enabled helper must be enabled for automatic watering to work.
- condition: state
entity_id: input_boolean.zone1_enabled
state: "on"
# The pump should not be activated if the water is low.
- condition: state
entity_id: input_boolean.zone1_water_available
state: "on"
action:
# Turn on the pump.
- service: homeassistant.turn_on
entity_id: switch.esph_austria_watering_d1_mini_pump
# Add a delay to allow the pump to work based on how long we want it to run for in the UI.
- delay:
seconds: "{{ states('input_number.zone1_pump_run_time') | int }}"
# Turn off the pump.
- service: homeassistant.turn_off
entity_id: switch.esph_austria_watering_d1_mini_pump
# //TODO check-in notification. Send notification to say the plants have been watered and their moisture level was 20% and is now 56%. Grow biggy grow! 🌻
# //TODO consider creating a secondary automation to send a notification/alert when the water is low.
# Tests
# 1. The automation should only work when "automatic watering enabled" and "Water Available" are both on.
# 1. The automation will only run after the duration "xxxx" has passed since it last ran.
# 1. The automation should trigger when the "Moisture Level" from the plant falls below the "Moisture Level Threshold" or the time matches "Water at".
# 1. The pump should only run for the duration set via "Pump Run Time".
See esphome.io for samples and getting images setup. This might take some learning. Also, ESPHome-Flasher-1.2.0-windows-x64 (1)
substitutions:
system_name: watering_d1_mini
friendly_name: Esph Watering D1
relay_gpio: D5 #D5=GPIO14
#dht22_gpio: D7
button_switch_gpio: D2
soil_moisture_adc: A0
esphome:
name: "esph_${system_name}"
platform: ESP8266
board: d1_mini
wifi:
ssid: !secret ssid
password: !secret ssid_password
power_save_mode: NONE
api:
ota:
time:
- platform: homeassistant
id: homeassistant_time
captive_portal:
logger:
#level: DEBUG #DEBUG, VERY_VERBOSE
text_sensor:
- platform: version
name: "esph_${system_name}_version"
on_value:
then:
- lambda: |-
ESP_LOGD("main", "The current version is %s", x.c_str());
- platform: wifi_info
ip_address:
name: esph_${system_name}_ip
ssid:
name: esph_${system_name}_ssid
bssid:
name: esph_${system_name}_bssid
switch:
## relay / pump - used by ESPHome internally, not visible from HomeAssistant
- platform: gpio
id: relay
restore_mode: ALWAYS_OFF
#name: "${system_name} Relay"
pin:
number: $relay_gpio
inverted: True
on_turn_on:
then:
# Do something
on_turn_off:
then:
- homeassistant.service:
service: input_datetime.set_datetime
data_template:
entity_id: "input_datetime.esph_${system_name}_triggered"
datetime: !lambda return id(homeassistant_time).now().strftime("%Y-%m-%d %H:%M:%S");
- homeassistant.service:
service: notify.html5_notification
data:
title: Plants Watered
data_template:
message: Plants were watered at {{ my_variable }}.
variables:
my_variable: |-
return id(homeassistant_time).now().strftime("%Y-%m-%d %H:%M:%S");
## Timed Pump
- platform: template
name: "esph_${system_name}_timed_pump"
id: timed_pump
icon: "mdi:siren"
lambda: !lambda |-
if (id(relay).state) {
return true;
} else {
return false;
}
turn_on_action:
- switch.turn_on: relay
- delay: !lambda return (id(zone1_pump_run_time).state * 1000);
- switch.turn_off: relay
- homeassistant.service:
service: input_datetime.set_datetime
data_template:
entity_id: "input_datetime.esph_${system_name}_triggered"
datetime: !lambda return id(homeassistant_time).now().strftime("%Y-%m-%d %H:%M:%S");
## Normal Pump
- platform: template
name: "esph_${system_name}_pump"
id: pump
icon: "mdi:siren"
lambda: !lambda |-
if (id(relay).state) {
return true;
} else {
return false;
}
turn_on_action:
- switch.turn_on: relay
turn_off_action:
- switch.turn_off: relay
- homeassistant.service:
service: input_datetime.set_datetime
data_template:
entity_id: "input_datetime.esph_${system_name}_triggered"
datetime: !lambda return id(homeassistant_time).now().strftime("%Y-%m-%d %H:%M:%S");
binary_sensor:
#Breadboard Button
- platform: gpio
id: button_switch
pin:
number: $button_switch_gpio
mode: INPUT_PULLUP
inverted: TRUE
filters:
- delayed_on_off: 200ms
on_press:
then:
- switch.toggle: relay
sensor:
- platform: homeassistant
name: "Pump Runtime"
entity_id: input_number.zone1_pump_run_time
id: "zone1_pump_run_time"
- platform: wifi_signal
name: "esph_${system_name}_wifi_signal"
update_interval: 300s
- platform: uptime
name: "esph_${system_name}_uptime"
update_interval: 300s
- platform: adc #Volts will flow to Template Sensor Below
pin: $soil_moisture_adc
name: "esph_${system_name}_soil_v"
id: soil_v
update_interval: 60s
unit_of_measurement: 'v'
accuracy_decimals: 2
- platform: template
name: "esph_${system_name}_soil_percent"
id: soil_percent
unit_of_measurement: '%'
icon: "mdi:water-percent"
update_interval: 60s
accuracy_decimals: 0
# Bryan Reading: 0.44 = in water, 0.84 = dry
lambda: |-
const float x = id(soil_v).state;
if (x > 0.8) {
return 0;
} else if (x < 0.4) {
return 100;
} else {
return (0.84 - x) / (0.84-0.44) * 100.0;
}
ESP_LOGD("main", "We hope soil value in volts is: %d", x);
Finally a pic:
Hello,
Thanks for the write up! What is the name of the orange things in the soil ? Do you have a link so I can buy the same ?
Thank you