ESPHome: DIY Irrigation Controller With Internal Scheduler

Hey Everybody,

I actually have been lurking on this project for a good while now. I was a Tasmota/MQTT guy before but have now fully bought into ESPhome w/ HA as its exactly what I am looking for and getting better every day. This project has resulted in most of my ESPHome learning. My purpose is a little different though. I have a hobby farm and with 8 individual watering stations and 1,2,3 or 4 lines running off each station for gardens, 3 small orchards and several greenhouses. So I was trying to work with that by modularizing the project for cheap ESP-01 relay modules (IOTMCUs or HW-655s) As I have probably 2 dozen of them from various tasmota devices. So I took Mr. Sneezys code (great work, very readable) and cut out all but 1 relay and left everything else and moved the board back to esp01. At first it wouldn’t compile because of the std::string issue in the irrigation.h file. That’s now fixed. However afterwards one particular line used to determine next watering day and time would still fail with too many arguments. A couple of extra parenthesis will let it compile but its still not right. it will only take whatever is listed 2nd in the line. I now have most of it working in HA but this one thing is giving me issues. I try not to post gibberish because I am an absolute amateur with this and mostly do not know what I am doing but I can’t seem to get it and just need a little assistance.

Here is my compile fail

Here is my yaml that works

# 1 Zone Version
substitutions:
  devicename: irrigation-controller

esphome:
  name: $devicename
  platform: esp8266
  board: esp01_1m
  includes:
    - /config/esphome/irrigation.h

wifi:
  fast_connect: on
  ssid: !secret wifi_ssid
  password: !secret wifi_password
  

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "Esp8266-Irrigation"
    password: "password"

# Enable logging
logger:

# Enable Home Assistant API
api:
  reboot_timeout: 0s

ota:
  password: !secret ota_password

globals:
  # ============================================================================= #
  # Irrigation time remaining
  - id: remaining_time_1
    type: int
    restore_value: no
    initial_value: "300"
  
  # ============================================================================= #
  # Store previous values to verify change.
  - id: remaining_time_1_previous
    type: int
    restore_value: no
    initial_value: "0"
  
binary_sensor:
  # Connection status
  - platform: status
    name: ${devicename} Status
###########################################
  - platform: homeassistant
    id: ui_stop_irrigation_automation
    entity_id: input_boolean.stop_irrigation_automation
    


sensor:
###########################################
  - platform: uptime
    name: ${devicename} Uptime
###########################################

###########################################
  - platform: wifi_signal
    name:  ${devicename} WiFi Signal Sensor
    update_interval: 10s
###########################################

  # ============================================================================= #
  # Retrieve durations settings from the Home Assistant UI.
  - platform: homeassistant
    id: ui_zone_1_duration
    entity_id: input_number.irrigation_zone_1_duration
    on_value:
      #if:
      #  condition:
      #    api.connected:
      then:
        - sensor.template.publish:
            id: irrigation_zone_1_duration
            state: !lambda return id(ui_zone_1_duration).state;
  # ============================================================================= #
  # Store durations.
  - platform: template
    name: Irrigation duration - Zone 1
    id: irrigation_zone_1_duration
    accuracy_decimals: 0
    unit_of_measurement: min
    icon: mdi:camera-timer
  # ============================================================================= #
  # Countdown sensors.
  - platform: template
    name: Remaining Time - Zone 1
    id: irrigation_zone_1_remaining
    lambda: "return 0;"
    accuracy_decimals: 0
    unit_of_measurement: min
    icon: mdi:timer
    on_value:
      then:
        - if:
            condition:
              lambda: return id(remaining_time_1) == 0;
            then:
              - switch.turn_off: irrigation_channel_1
  
text_sensor:
###########################################
#
# Template Text Sensors
#
###########################################
  - platform: version
    name: ${devicename} ESPHome Version
###########################################
  # ============================================================================= #
  # Retrieve list of times from the Home Assistant UI.
  - platform: homeassistant
    id: ui_zone_1_times
    entity_id: input_text.irrigation_zone_1_times
    on_value:
      #if:
      #  condition:
      #    api.connected:
      then:
        #- delay: 10sec
        - text_sensor.template.publish:
            id: irrigation_zone_1_times
            state: !lambda return id(ui_zone_1_times).state;
  # ============================================================================= #
  # Store time lists.
  - platform: template
    name: Schedule - Zone 1
    id: irrigation_zone_1_times
    on_value:
      then:
        # Update the next scheduled run time.
        - text_sensor.template.publish:
            id: irrigation_zone_1_next
            state: !lambda |-
              return update_next_runtime (id(irrigation_zone_1_times).state), (id(irrigation_zone_1_days).state);
# ============================================================================= #
  # Retrieve list of days from the Home Assistant UI.
  - platform: homeassistant
    id: ui_zone_1_days
    entity_id: input_text.irrigation_zone_1_days
    on_value:
      #if:
      #  condition:
      #    api.connected:
      then:
        #- delay: 10sec
        - text_sensor.template.publish:
            id: irrigation_zone_1_days
            state: !lambda return id(ui_zone_1_days).state;
  # ============================================================================= #
  # Store time lists.
  - platform: template
    name: Day - Zone 1
    id: irrigation_zone_1_days
    on_value:
      then:
        # Update the next scheduled run time.
        - text_sensor.template.publish:
            id: irrigation_zone_1_next
            state: !lambda |-
              return update_next_runtime (id(irrigation_zone_1_times).state), (id(irrigation_zone_1_days).state);
  # ============================================================================= #
  # Set the next scheduled time.
  - platform: template
    name: Next Irrigation - Zone 1
    id: irrigation_zone_1_next
    icon: mdi:calendar-clock

# Update the countdown timers every 5 seconds.
interval:
  - interval: 5s
    then:
      - lambda: |-
          if (id(remaining_time_1) > 0) {
            // Store the previous time.
            id(remaining_time_1_previous) = id(remaining_time_1);
            // When the relay is on.
            if (id(irrigation_channel_1).state) {
              // Decrement the timer.
              id(remaining_time_1) -= 5;
              // Turn off the relay when the time reaches zero... or the remaining time fails a sanity check!
              //if (id(remaining_time_1) <= 0 || id(irrigation_zone_1_remaining).state > id(irrigation_zone_1_duration).state){
              if (id(remaining_time_1) <= 0) {
                id(irrigation_channel_1).turn_off();
                id(remaining_time_1) = 0;
              }
            }
            // Update the remaining time display.
            if (id(remaining_time_1_previous) != id(remaining_time_1)) {
              id(irrigation_zone_1_remaining).publish_state( (id(remaining_time_1)/60) + 1 );
            }
          }
          
          
          
# Time based automations.
time:
  - platform: homeassistant
    id: homeassistant_time
    timezone: America/Chicago
    #timezone: CET-1CEST-2,M3.5.0/02:00:00,M10.5.0/03:00:00
    on_time:
      - seconds: 0
        minutes: /1
        then:
          - lambda: |-
              if (scheduled_runtime(id(irrigation_zone_1_next).state.c_str()))
                {
                  id(irrigation_zone_1).turn_on();
                }
switch:
###########################################
  - platform: restart
    name: ${devicename} Restart
###########################################
  # ============================================================================= #
  # Relays which trigger solenoids
  - platform: gpio
    id: irrigation_channel_1
    pin: 00
    restore_mode: ALWAYS_OFF
    inverted: no
    on_turn_on:
      then:
        # Start the countdown timer.
        - globals.set:
            id: remaining_time_1
            value: !lambda return id(irrigation_zone_1_duration).state * 60;
        # Show the remaining time.
        - sensor.template.publish:
            id: irrigation_zone_1_remaining
            state: !lambda return id(irrigation_zone_1_duration).state;
        # Show the "Next Time" as "now".
        - text_sensor.template.publish:
            id: irrigation_zone_1_next
            state: "now"
            # state NOW on original code, change to your preferred language
    on_turn_off:
      then:
        - sensor.template.publish:
            id: irrigation_zone_1_remaining
            state: "0"
        # Update the next scheduled run time.
        - text_sensor.template.publish:
            id: irrigation_zone_1_next
            state: !lambda |-
              return update_next_runtime (id(irrigation_zone_1_times).state), (id(irrigation_zone_1_days).state);
  
  # ============================================================================= #
  # Virtual Zone Switches which toggle the relay, and store the current state.
  - platform: template
    name: Stop All Zones
    id: irrigation_stop_zones
    icon: mdi:sprinkler-variant
    optimistic: true
    turn_on_action:
      - switch.turn_off: irrigation_channel_1
      - delay: 10s
      - switch.turn_off: irrigation_stop_zones

  - platform: template
    name: Irrigation Zone 1
    id: irrigation_zone_1
    icon: mdi:sprinkler-variant
    lambda: return id(irrigation_channel_1).state;
    optimistic: true
    turn_on_action:
      # Turn on if not disabled.
      if:
        condition:
          and:
            - lambda: return id(irrigation_zone_1_duration) > 0;
            - binary_sensor.is_off: ui_stop_irrigation_automation
        then:
          - switch.turn_on: irrigation_channel_1
    turn_off_action:
      - switch.turn_off: irrigation_channel_1

Here is the dashboard results of the code that will compile
image

It looks like your irrigation.h file has a different version of the update_next_runtime() function. My original version assumes watering every day, so that function only needs a list of times passed to it. You need to find a version of that function which accepts a second parameter with a list of days.

Oh!, Very cool. Thank you. Though maybe I can set a “water everyday” button to switch between both functions.

So this is now working correctly. I set it up with substitutions throughout so its relatively easy create new ones rapidly and integrate them into HA based on what they do. These are for single relay esp-01s. Thank you very much everyone.

substitutions:
  devicename: water_station_1 ##this will be the device name like "water_station_1" "outdoor_light_5"
  purpose: irrigation  ##this will be the purpose of the switch like recirculation, illumination, or irrigation
  subject: garden  ## this is what its interface is acting on like "garden", "water heater", or "zone_1"

This works great! and results in cleanly labeled entities
image

However I still need to make the helpers for the 3 inputs, 1number input for duration and 2 text inputs for the day of week and Time. Is there any way we can advertise the needed inputs to Home Assistant to make it auto-discoverable and easier to add?

1 Like

Hi, this is my code, but I always get an error for “irrigation_zone1_next”

globals:
  # Irrigation time remaining
  - id: remaining_time1
    type: int
    restore_value: no
    initial_value: "300"

  # Store previous values to verify change.
  - id: remaining_time1_previous
    type: int
    restore_value: no
    initial_value: "0"

sensor:
  # Countdown sensors.
  - platform: template
    name: Irrigation Zone1 Remaining
    id: irrigation_zone1_remaining
    lambda: "return 0;"
    accuracy_decimals: 0
    unit_of_measurement: minutes
    icon: mdi:timer
    on_value:
      then:
        - if:
            condition:
              lambda: return id(remaining_time1) == 0;
            then:
              - switch.turn_off: relay1
              
  # Retrieve durations settings from the Home Assistant UI.
  - platform: homeassistant
    id: ui_zone1_duration
    entity_id: input_number.irrigation_zone1_duration
    on_value:
      then:
        - sensor.template.publish:
            id: irrigation_zone1_duration
            state: !lambda return id(ui_zone1_duration).state;

  # Store durations.
  - platform: template
    name: Irrigation Zone1 Duration
    id: irrigation_zone1_duration

switch:
  # Relays which trigger solenoids
  - platform: gpio
    id: relay1
    pin: 
      number: GPIO14
    on_turn_on:
      then:
        # Start the countdown timer.
        - globals.set:
            id: remaining_time1
            value: !lambda return id(irrigation_zone1_duration).state * 60;

        # Show the remaining time.
        - sensor.template.publish:
            id: irrigation_zone1_remaining
            state: !lambda return id(irrigation_zone1_duration).state;

        # Show the "Next Time" as "now".
        - text_sensor.template.publish:
            id: irrigation_zone1_next
            state: "now"
    on_turn_off:
      then:
        - sensor.template.publish:
            id: irrigation_zone1_remaining
            state: "0"

        # Update the next scheduled run time.
        - text_sensor.template.publish:
            id: irrigation_zone1_next
            state: !lambda |-
              return update_next_runtime(id(irrigation_zone1_times).state);

# Update the countdown timers every 5 seconds.
interval:
  - interval: 5s
    then:
      - lambda: |-
          if (id(remaining_time1) > 0) {
            // Store the previous time.
            id(remaining_time1_previous) = id(remaining_time1);

            // When the relay is on.
            if (id(relay1).state) {
              // Decrement the timer.
              id(remaining_time1) -= 5;

              // Turn off the relay when the time reaches zero.
              if (id(remaining_time1) <= 0) {
                id(relay1).turn_off();
                id(remaining_time1) = 0;
              }
            }

            // Update the remaining time display.
            if (id(remaining_time1_previous) != id(remaining_time1)) {
              id(irrigation_zone1_remaining).publish_state( (id(remaining_time1)/60) + 1 );
            }
          }

text_sensor:
  # Retrieve list of times from the Home Assistant UI.
  - platform: homeassistant
    id: ui_zone1_times
    entity_id: input_text.irrigation_zone1_times
    on_value:
      then:
        - text_sensor.template.publish:
            id: irrigation_zone1_times
            state: !lambda return id(ui_zone1_times).state;

  # Store time lists.
  - platform: template
    name: Irrigation Zone1 Times
    id: irrigation_zone1_times
    on_value:
      then:
        # Update the next scheduled run time.
        - text_sensor.template.publish:
            id: irrigation_zone1_next
            state: !lambda |-
              return update_next_runtime(id(irrigation_zone1_times).state);
          
# Time based automations.
time:
  - platform: homeassistant
    id: homeassistant_time
    on_time:
      - seconds: 0
        minutes: /1
        then:
          - lambda: |-
              if (scheduled_runtime(id(irrigation_zone1_next).state.c_str())) {
                id(irrigation_zone1).turn_on();
              }

This is the error:

You need to define a template text sensor with the id of irrigation_zone1_next before you can assign a value to it.

Does it have to be created in Home Assistant or Esp Home?
Can you show me the part you created in Home Assistant? thank you

It needs to be defined in your ESPHome code. I’m away from my computer. Search for irrigation_zone1_next in this topic and you should find the relevant code.

This is my complete modified code for 1 zone:

globals:
  # ============================================================================= #
  # Irrigation time remaining
  - id: remaining_time1
    type: int
    restore_value: no
    initial_value: "300"


  # ============================================================================= #
  # Store previous values to verify change.
  - id: remaining_time1_previous
    type: int
    restore_value: no
    initial_value: "0"

number:
  - platform: template
    name: $zone1_name duration
    id: irrigation_zone1_duration
    icon: mdi:timer-sand
    #mode: slider #auto, box, slider
    #unit_of_measurement: minutes
    optimistic: true
    initial_value: 0
    restore_value: true
    min_value: 0
    max_value: 60
    step: 1
    # set_action:
    #   then:
    #     - globals.set:
    #         id: irrigation_zone1_duration
    #         value: !lambda return id(ui_zone1_duration).state;


sensor:

  # ============================================================================= #
  # Countdown sensors.
  - platform: template
    name: $zone1_name remaining
    id: irrigation_zone1_remaining
    lambda: "return 0;"
    accuracy_decimals: 0
    unit_of_measurement: minutes
    icon: mdi:timer-outline
    on_value:
      then:
        - if:
            condition:
              lambda: return id(remaining_time1) == 0;
            then:
              - switch.turn_off: relay1


  # ============================================================================= #
  # Retrieve durations settings from the Home Assistant UI.
  # - platform: homeassistant
  #   id: ui_zone1_duration
  #   entity_id: input_number.irrigation_zone1_duration
  #   on_value:
  #     then:
  #     - sensor.template.publish:
  #         id: irrigation_zone1_duration
  #         state: !lambda return id(ui_zone1_duration).state;



  # ============================================================================= #
  # Store durations.
  # - platform: template
  #   name: $zone1_name duration
  #   id: irrigation_zone1_duration



switch:

  # ============================================================================= #
  # Virtual zone Switches which toggle the relay, and store the current state.
  - platform: template
    name: $zone1_name
    id: irrigation_zone1
    lambda: return id(relay1).state;
    optimistic: true
    turn_on_action:
      # Turn on if not disabled.
      if:
        condition:
          lambda: return id(irrigation_zone1_duration) > 0;
        then:
          - switch.turn_on: relay1
    turn_off_action:
      - switch.turn_off: relay1

  - platform: template
    name: $zone2_name
    id: irrigation_zone2
    lambda: return id(relay2).state;
    optimistic: true
    turn_on_action:
      # Turn on if not disabled.
      if:
        condition:
          lambda: return id(irrigation_zone2_duration) > 0;
        then:
          - switch.turn_on: relay2
    turn_off_action:
      - switch.turn_off: relay2

  # ============================================================================= #
  # Relays which trigger solenoids
  - platform: gpio
    id: relay1
    pin: GPIO16
    on_turn_on:
      then:
        # Start the countdown timer.
        - globals.set:
            id: remaining_time1
            value: !lambda return id(irrigation_zone1_duration).state * 60;
        # Show the remaining time.
        - sensor.template.publish:
            id: irrigation_zone1_remaining
            state: !lambda return id(irrigation_zone1_duration).state;
        # Show the "Next Time" as "now".
        - text_sensor.template.publish:
            id: irrigation_zone1_next
            state: "now"
    on_turn_off:
      then:
        - sensor.template.publish:
            id: irrigation_zone1_remaining
            state: "0"
        # Update the next scheduled run time.
        - text_sensor.template.publish:
            id: irrigation_zone1_next
            state: !lambda |-
              return update_next_runtime(id(irrigation_zone1_times).state);

text_sensor:
  # ============================================================================= #
  # Retrieve list of times from the Home Assistant UI.
  - platform: homeassistant
    id: ui_zone1_times
    entity_id: $zone1_times_entity
    on_value:
      then:
        #- delay: 10sec
        - text_sensor.template.publish:
            id: irrigation_zone1_times
            state: !lambda return id(ui_zone1_times).state;

  # ============================================================================= #
  # Store time lists.
  - platform: template
    name: $zone1_name times
    id: irrigation_zone1_times
    on_value:
      then:
        # Update the next scheduled run time.
        - text_sensor.template.publish:
            id: irrigation_zone1_next
            state: !lambda |-
              return update_next_runtime(id(irrigation_zone1_times).state);

  # ============================================================================= #
  # Set the next scheduled time.
  - platform: template
    name: $zone1_name next
    id: irrigation_zone1_next


# Update the countdown timers every 5 seconds.
interval:
  - interval: 5s
    then:
      - lambda: |-
          if (id(remaining_time1) > 0) {
            // Store the previous time.
            id(remaining_time1_previous) = id(remaining_time1);
            // When the relay is on.
            if (id(relay1).state) {
              // Decrement the timer.
              id(remaining_time1) -= 5;
              // Turn off the relay when the time reaches zero... or the remaining time fails a sanity check!
              //if (id(remaining_time1) <= 0 || id(irrigation_zone1_remaining).state > id(irrigation_zone1_duration).state){
              if (id(remaining_time1) <= 0) {
                id(relay1).turn_off();
                id(remaining_time1) = 0;
              }
            }
            // Update the remaining time display.
            if (id(remaining_time1_previous) != id(remaining_time1)) {
              id(irrigation_zone1_remaining).publish_state( (id(remaining_time1)/60) + 1 );
            }
          }
          if (id(remaining_time2) > 0) {
            // Store the previous time.
            id(remaining_time2_previous) = id(remaining_time2);
            // When the relay is on.
            if (id(relay2).state) {
              // Decrement the timer.
              id(remaining_time2) -= 5;
              // Turn off the relay when the time reaches zero... or the remaining time fails a sanity check!
              //if (id(remaining_time2) <= 0 || id(irrigation_zone2_remaining).state > id(irrigation_zone2_duration).state){
              if (id(remaining_time2) <= 0) {
                id(relay2).turn_off();
                id(remaining_time2) = 0;
              }
            }
            // Update the remaining time display.
            if (id(remaining_time2_previous) != id(remaining_time2)) {
              id(irrigation_zone2_remaining).publish_state( (id(remaining_time2)/60) + 1 );
            }
          }
# Time based automations.
time:
  - platform: homeassistant
    id: homeassistant_time
    timezone: Europe
    on_time:
      - seconds: 0
        minutes: /1
        then:
          - lambda: |-
              if (scheduled_runtime(id(irrigation_zone1_next).state.c_str())) {
                id(irrigation_zone1).turn_on();
              }
              if (scheduled_runtime(id(irrigation_zone2_next).state.c_str())) {
                id(irrigation_zone2).turn_on();
              }


There now appears to be this error on line 183 for “entity_id: $zone1_times_entity” (ERROR: Entity ID must have exactly one dot in it.)

text_sensor:
  # ============================================================================= #
  # Retrieve list of times from the Home Assistant UI.
  - platform: homeassistant
    id: ui_zone1_times
    entity_id: $zone1_times_entity
    on_value:
      then:
        #- delay: 10sec
        - text_sensor.template.publish:
            id: irrigation_zone1_times
            state: !lambda return id(ui_zone1_times).state;

Please when you have free time can you check my code? I just can’t fix it …
Thanks!

Ho provato anche il tuo metodo, ho sempre errori con questi nomi:


Inoltre, cosa sarebbe il file xml?
Se puoi mi daresti qualche spiegazione? Grazie

Problem found!
Unable to find action with the name ‘text_sensor.template.publish’.

platform: homeassistant
  id: ui_zone_1_days
  entity_id: input_text.irrigation_zone_1_days
  on_value: 
    then: 
      - 
        Unable to find action with the name 'text_sensor.template.publish'.
        text_sensor.template.publish: 
          id: irrigation_zone_1_days
          state: !lambda |-
            return id(ui_zone_1_days).state;
sensor.template: [source /config/esphome/esp-irrigazione.yaml:215]
  platform: template
  name: Day - Zone 1
  id: irrigation_zone_1_days
  on_value: 
    then: 
      - 
        Unable to find action with the name 'text_sensor.template.publish'.
        text_sensor.template.publish: 
          id: irrigation_zone_1_next
          state: !lambda |-
            return update_next_runtime(id(irrigation_zone_1_times).state, id(irrigation_zone_1_days).state);

Wow, just became aware of this new component for ESPhome.

That is awesome !

:sweat_drops:

3 Likes

I did not understand how to create a slider to vary the activation duration of each single switch

Yeah, that is definitely worth looking into at some point. That would greatly simplify the code for this type of project!

I have setup this awesome irrigation controller based on @sreknob version and it is working perfectly, however since a recent ESPHome update I am unable to compile the esp32 device yaml. It validates fine. Below is the error I receive when trying to compile. Any guidance will be appreciated.

Compiling  /data/besproeing/.pioenvs/besproeing/FrameworkArduino/WString.cpp.o
/config/esphome/besproeing.yaml: In function 'void setup()':
/config/esphome/besproeing.yaml:652:41: error: 'lambda' was not declared in this scope
/config/esphome/besproeing.yaml:652:96: error: expected primary-expression before ')' token
/config/esphome/besproeing.yaml:306:50: error: expected ')' before 'return'
/config/esphome/besproeing.yaml:306:98: error: expected primary-expression before ')' token
/config/esphome/besproeing.yaml:343:50: error: expected ')' before 'return'
/config/esphome/besproeing.yaml:343:98: error: expected primary-expression before ')' token
/config/esphome/besproeing.yaml:379:50: error: expected ')' before 'return'
/config/esphome/besproeing.yaml:379:98: error: expected primary-expression before ')' token
Compiling /data/besproeing/.pioenvs/besproeing/FrameworkArduino/base64.cpp.o
Compiling /data/besproeing/.pioenvs/besproeing/FrameworkArduino/cbuf.cpp.o
Compiling /data/besproeing/.pioenvs/besproeing/FrameworkArduino/esp32-hal-adc.c.o
Compiling /data/besproeing/.pioenvs/besproeing/FrameworkArduino/esp32-hal-bt.c.o
Compiling /data/besproeing/.pioenvs/besproeing/FrameworkArduino/esp32-hal-cpu.c.o
Compiling /data/besproeing/.pioenvs/besproeing/FrameworkArduino/esp32-hal-dac.c.o
Compiling /data/besproeing/.pioenvs/besproeing/FrameworkArduino/esp32-hal-gpio.c.o
*** [/data/besproeing/.pioenvs/besproeing/src/main.cpp.o] Error 1
========================= [FAILED] Took 15.53 seconds =========================

I just complied using the latest version without issue - seems like it’s choking on the arduino framework. Do you have any build flags or a specific arduino version pinned in your yaml? Have you tried cleaning your build files?

Below is top section from the device yaml. Yes, I did clean the build files.

esphome:
  name: besproeing
  includes:
    - besproeing.h
esp32:
  board: nodemcu-32s
  framework:
    type: arduino
    
    
# Enable logging
logger:

# Enable Home Assistant API
api:
  encryption:

Just tried to compile again and now the error pops at a different point.

Compiling /data/irrigation/.pioenvs/irrigation/lib64d/WiFi/WiFiClient.cpp.o
Compiling /data/irrigation/.pioenvs/irrigation/lib64d/WiFi/WiFiGeneric.cpp.o
/config/esphome/irrigation1.yaml: In function 'void setup()':
/config/esphome/irrigation1.yaml:648:41: error: 'lambda' was not declared in this scope
/config/esphome/irrigation1.yaml:648:96: error: expected primary-expression before ')' token
/config/esphome/irrigation1.yaml:302:50: error: expected ')' before 'return'
/config/esphome/irrigation1.yaml:302:98: error: expected primary-expression before ')' token
/config/esphome/irrigation1.yaml:339:50: error: expected ')' before 'return'
/config/esphome/irrigation1.yaml:339:98: error: expected primary-expression before ')' token
/config/esphome/irrigation1.yaml:375:50: error: expected ')' before 'return'
/config/esphome/irrigation1.yaml:375:98: error: expected primary-expression before ')' token
Compiling /data/irrigation/.pioenvs/irrigation/lib64d/WiFi/WiFiMulti.cpp.o
Compiling /data/irrigation/.pioenvs/irrigation/lib64d/WiFi/WiFiSTA.cpp.o
Compiling /data/irrigation/.pioenvs/irrigation/lib64d/WiFi/WiFiScan.cpp.o
*** [/data/irrigation/.pioenvs/irrigation/src/main.cpp.o] Error 1