ESPHome Config File


This is the ESPHome based config file for my custom sprinkler controller. It is built around a modified implementation of the esphome sprinkler controller component.

I wanted more flexibility to select the days of the week to run certain zones so the code below is designed to primarily work with the sprinkler controller queue function.

While I have heavily integrated this device into my Home Assistant (HA) instance, it can also run independently from HA. Zone start & stop times are all executed directly from the device.

substitutions: # set up unique values for this hardware instance
  name_of_device: nachio
  id_prefix: nachio
  name_prefix: Nachio
  api_key: kHC70+TJJ4ZeYkstfj/KBYVLFUczMrb0PdacV9Khq1M=
  #ota_password: "paste_OTA_password_here"
  ap_password: 5050badass08
  sensor_update_frequency: 1s
  uom: Min # this overrides the uom in sprinkler -> run_duration 

esphome:
  name: $name_of_device
  friendly_name:  ${name_prefix}
  min_version: 2024.11.0
  name_add_mac_suffix: false
  on_boot:
    priority: -100
    then:
    #  # Set default state for Valve Status
    #  - text_sensor.template.publish:
    #      id: valve_status
    #      state: "Idle"
      # Set multiplier to 60, convert seconds to minutes
      - sprinkler.set_multiplier:
          id: ${name_of_device}_sprinklers
          multiplier: 60



esp32:
  board: esp32dev
  framework:
    type: esp-idf

# Enable logging
logger:
  level: DEBUG

# Enable Home Assistant API
api:
  encryption:
      key: $api_key
  reboot_timeout: 1min
  services:
    - service: start_valve
      then:
        - script.execute: start_valve_dynamic


# Allow Over-The-Air updates
ota:
  - platform: esphome

wifi:
  ssid: Aether
  password: $ap_password

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "Nachio Fallback Hotspot"
    password: $ap_password

captive_portal:


# Status LED
light:
  - platform: status_led
    name: "Nachio LED"
    restore_mode: ALWAYS_OFF
    pin:
      number: GPIO23


globals:
  - id: zone_index
    type: int
    restore_value: no
    initial_value: '0'

  - id: zone_duration
    type: float
    restore_value: no
    initial_value: '0'


  - id: sprinkler_queue_time_remaining
    type: int  # seconds
    initial_value: '0'


  #These are all used to determine the queue size
  - id: sprinkler_queue_size
    type: int
    initial_value: '0'

  - id: valve_1_active
    type: bool
    initial_value: 'false'

  - id: valve_2_active
    type: bool
    initial_value: 'false'

  - id: valve_3_active
    type: bool
    initial_value: 'false'

  - id: valve_4_active
    type: bool
    initial_value: 'false'

  - id: valve_5_active
    type: bool
    initial_value: 'false'

  - id: valve_6_active
    type: bool
    initial_value: 'false'

  - id: valve_7_active
    type: bool
    initial_value: 'false'

  - id: valve_8_active
    type: bool
    initial_value: 'false'


number:
  - platform: template
    id: var_run_duration_minutes
    name: "Var Run Duration Minutes"
    min_value: 1
    max_value: 20
    step: 1
    unit_of_measurement: "min"
    mode: box
    optimistic: true

  - platform: template
    name: "Sprinkler Valve Number"
    id: valve_number_input
    min_value: 0
    max_value: 7  # Adjust to match your number of valves minus 1
    step: 1
    mode: box
    optimistic: true






sprinkler:
  - id: ${id_prefix}_sprinklers
    main_switch: "Sprinkler Cycle Active"
    auto_advance_switch: "Auto Advance"
    queue_enable_switch: "Enable Queue"
    standby_switch: "System Standby" #To be used for rain skips
    valve_overlap: 3s
    valves:
      - valve_switch: "Zone 1 Active"
        #enable_switch: "Enable z1"
        run_duration_number: 
          id: zone_1_run_duration
          name: "Zone 1 Run Duration"
          icon: "mdi:timer-outline"
          initial_value: 1
          unit_of_measurement: min
        valve_switch_id: ${id_prefix}_valve01

      - valve_switch: "Zone 2 Active"
        #enable_switch: "Enable z2"
        run_duration_number: 
          id: zone_2_run_duration
          name: "Zone 2 Run Duration"
          icon: "mdi:timer-outline"
          initial_value: 1
          unit_of_measurement: min
        valve_switch_id: ${id_prefix}_valve02

      - valve_switch: "Zone 3 Active"
        #enable_switch: "Enable z3"
        run_duration_number: 
          id: zone_3_run_duration
          name: "Zone 3 Run Duration"
          icon: "mdi:timer-outline"
          initial_value: 1
          unit_of_measurement: min
        valve_switch_id: ${id_prefix}_valve03

      - valve_switch: "Zone 4 Active"
        #enable_switch: "Enable z4"
        run_duration_number: 
          id: zone_4_run_duration
          name: "Zone 4 Run Duration"
          icon: "mdi:timer-outline"
          initial_value: 1
          unit_of_measurement: min
        valve_switch_id: ${id_prefix}_valve04

      - valve_switch: "Zone 5 Active"
        #enable_switch: "Enable z5"
        run_duration_number: 
          id: zone_5_run_duration
          name: "Zone 5 Run Duration"
          icon: "mdi:timer-outline"
          initial_value: 1
          unit_of_measurement: min
        valve_switch_id: ${id_prefix}_valve05

      - valve_switch: "Zone 6 Active"
        #enable_switch: "Enable z6"
        run_duration_number: 
          id: zone_6_run_duration
          name: "Zone 6 Run Duration"
          icon: "mdi:timer-outline"
          initial_value: 1
          unit_of_measurement: min
        valve_switch_id: ${id_prefix}_valve06

      - valve_switch: "Zone 7 Active"
        #enable_switch: "Enable z7"
        run_duration_number: 
          id: zone_7_run_duration
          name: "Zone 7 Run Duration"
          icon: "mdi:timer-outline"
          initial_value: 1
          unit_of_measurement: min
        valve_switch_id: ${id_prefix}_valve07

      - valve_switch: "Zone 8 Active"
        #enable_switch: "Enable z8"
        run_duration_number: 
          id: zone_8_run_duration
          name: "Zone 8 Run Duration"
          icon: "mdi:timer-outline"
          initial_value: 1
          unit_of_measurement: min
        valve_switch_id: ${id_prefix}_valve08


button:
  - platform: template
    name: "Pause Sprinkler"
    on_press:
      - sprinkler.pause: ${id_prefix}_sprinklers

  - platform: template
    name: "Resume Sprinkler"
    on_press:
      - sprinkler.resume: ${id_prefix}_sprinklers









#########################################################
# BEGIN SWITCHES
#########################################################
# 8 relay outputs
#internal: true causes these switches to not show in HA
switch:
  - platform: gpio
    pin: GPIO32
    name: ${id_prefix}_valve01
    id: ${id_prefix}_valve01
    on_turn_on:
      then:
        - lambda: |-
            id(valve_1_active) = true; 
    on_turn_off:
      then:
        - lambda: |-
            if (id(valve_1_active)) {
              id(sprinkler_queue_size) -= 1;
              float runtime_min = id(zone_1_run_duration).state;
              int runtime_sec = (int)(runtime_min * 60.0);
              id(sprinkler_queue_time_remaining) -= runtime_sec;
              if (id(sprinkler_queue_time_remaining) < 0)
                id(sprinkler_queue_time_remaining) = 0;
              id(valve_1_active) = false;
            }  
    restore_mode: ALWAYS_OFF
    internal: true

  - platform: gpio
    pin: GPIO33
    name: ${id_prefix}_valve02
    id: ${id_prefix}_valve02
    on_turn_on:
      then:
        - lambda: |-
            id(valve_2_active) = true; 
    on_turn_off:
      then:
        - lambda: |-
            if (id(valve_2_active)) {
              id(sprinkler_queue_size) -= 1;
              float runtime_min = id(zone_2_run_duration).state;
              int runtime_sec = (int)(runtime_min * 60.0);
              id(sprinkler_queue_time_remaining) -= runtime_sec;
              if (id(sprinkler_queue_time_remaining) < 0)
                id(sprinkler_queue_time_remaining) = 0;
              id(valve_2_active) = false;
            }  
    restore_mode: ALWAYS_OFF
    internal: true

  - platform: gpio
    pin: GPIO25
    name: ${id_prefix}_valve03
    id: ${id_prefix}_valve03
    on_turn_on:
      then:
        - lambda: |-
            id(valve_3_active) = true; 
    on_turn_off:
      then:
        - lambda: |-
            if (id(valve_3_active)) {
              id(sprinkler_queue_size) -= 1;
              float runtime_min = id(zone_3_run_duration).state;
              int runtime_sec = (int)(runtime_min * 60.0);
              id(sprinkler_queue_time_remaining) -= runtime_sec;
              if (id(sprinkler_queue_time_remaining) < 0)
                id(sprinkler_queue_time_remaining) = 0;
              id(valve_3_active) = false;
            }    
    restore_mode: ALWAYS_OFF
    internal: true

  - platform: gpio
    pin: GPIO26
    name: ${id_prefix}_valve04
    id: ${id_prefix}_valve04
    on_turn_on:
      then:
        - lambda: |-
            id(valve_4_active) = true; 
    on_turn_off:
      then:
        - lambda: |-
            if (id(valve_4_active)) {
              id(sprinkler_queue_size) -= 1;
              float runtime_min = id(zone_4_run_duration).state;
              int runtime_sec = (int)(runtime_min * 60.0);
              id(sprinkler_queue_time_remaining) -= runtime_sec;
              if (id(sprinkler_queue_time_remaining) < 0)
                id(sprinkler_queue_time_remaining) = 0;
              id(valve_4_active) = false;
            }  
    restore_mode: ALWAYS_OFF
    internal: true

  - platform: gpio
    pin: GPIO27
    name: ${id_prefix}_valve05
    id: ${id_prefix}_valve05
    on_turn_on:
      then:
        - lambda: |-
            id(valve_5_active) = true; 
    on_turn_off:
      then:
        - lambda: |-
            if (id(valve_5_active)) {
              id(sprinkler_queue_size) -= 1;
              float runtime_min = id(zone_5_run_duration).state;
              int runtime_sec = (int)(runtime_min * 60.0);
              id(sprinkler_queue_time_remaining) -= runtime_sec;
              if (id(sprinkler_queue_time_remaining) < 0)
                id(sprinkler_queue_time_remaining) = 0;
              id(valve_5_active) = false;
            }      
    restore_mode: ALWAYS_OFF
    internal: true

  - platform: gpio
    pin: GPIO14
    name: ${id_prefix}_valve06
    id: ${id_prefix}_valve06
    on_turn_on:
      then:
        - lambda: |-
            id(valve_6_active) = true; 
    on_turn_off:
      then:
        - lambda: |-
            if (id(valve_6_active)) {
              id(sprinkler_queue_size) -= 1;
              float runtime_min = id(zone_6_run_duration).state;
              int runtime_sec = (int)(runtime_min * 60.0);
              id(sprinkler_queue_time_remaining) -= runtime_sec;
              if (id(sprinkler_queue_time_remaining) < 0)
                id(sprinkler_queue_time_remaining) = 0;
              id(valve_6_active) = false;
            }  
    restore_mode: ALWAYS_OFF
    internal: true

  - platform: gpio
    pin: GPIO12
    name: ${id_prefix}_valve07
    id: ${id_prefix}_valve07
    on_turn_on:
      then:
        - lambda: |-
            id(valve_7_active) = true; 
    on_turn_off:
      then:
        - lambda: |-
            if (id(valve_7_active)) {
              id(sprinkler_queue_size) -= 1;
              float runtime_min = id(zone_7_run_duration).state;
              int runtime_sec = (int)(runtime_min * 60.0);
              id(sprinkler_queue_time_remaining) -= runtime_sec;
              if (id(sprinkler_queue_time_remaining) < 0)
                id(sprinkler_queue_time_remaining) = 0;
              id(valve_7_active) = false;
            }  
    restore_mode: ALWAYS_OFF
    internal: true

  - platform: gpio
    pin: GPIO13
    name: ${id_prefix}_valve08
    id: ${id_prefix}_valve08
    on_turn_on:
      then:
        - lambda: |-
            id(valve_8_active) = true; 
    on_turn_off:
      then:
        - lambda: |-
            if (id(valve_8_active)) {
              id(sprinkler_queue_size) -= 1;
              float runtime_min = id(zone_8_run_duration).state;
              int runtime_sec = (int)(runtime_min * 60.0);
              id(sprinkler_queue_time_remaining) -= runtime_sec;
              if (id(sprinkler_queue_time_remaining) < 0)
                id(sprinkler_queue_time_remaining) = 0;
              id(valve_8_active) = false;
            }  
    restore_mode: ALWAYS_OFF
    internal: true

  - platform: shutdown
    name: "Nachio Shutdown"
    entity_category: config


  #RAIN SKIP Switch 
  - platform: template
    name: "Rain Skip Lock"
    id: ${id_prefix}_rain_skip_lock
    restore_mode: RESTORE_DEFAULT_OFF
    optimistic: true

 

#########################################################
# Begin day of week switches per zone
#########################################################
  # Per-zone day-of-week switches
  # Zone 1 - Front Lawn
  - platform: template
    id: ${id_prefix}_z1_0_sun
    name: z1_0 run sun
    icon: mdi:calendar-range
    entity_category: config
    restore_mode: RESTORE_DEFAULT_OFF
    optimistic: true

  - platform: template
    id: ${id_prefix}_z1_1_mon
    name: z1_1 run mon
    icon: mdi:calendar-range
    entity_category: config
    restore_mode: RESTORE_DEFAULT_OFF
    optimistic: true

  - platform: template
    id: ${id_prefix}_z1_2_tue
    name: z1_2 run tue
    icon: mdi:calendar-range
    entity_category: config
    restore_mode: RESTORE_DEFAULT_OFF
    optimistic: true

  - platform: template
    id: ${id_prefix}_z1_3_wed
    name: z1_3 run wed
    icon: mdi:calendar-range
    entity_category: config
    restore_mode: RESTORE_DEFAULT_OFF
    optimistic: true

  - platform: template
    id: ${id_prefix}_z1_4_thu
    name: z1_4 run thu
    icon: mdi:calendar-range
    entity_category: config
    restore_mode: RESTORE_DEFAULT_OFF
    optimistic: true

  - platform: template
    id: ${id_prefix}_z1_5_fri
    name: z1_5 run fri
    icon: mdi:calendar-range
    entity_category: config
    restore_mode: RESTORE_DEFAULT_OFF
    optimistic: true

  - platform: template
    id: ${id_prefix}_z1_6_sat
    name: z1_6 run sat
    icon: mdi:calendar-range
    entity_category: config
    restore_mode: RESTORE_DEFAULT_OFF
    optimistic: true

  - platform: template
    id: ${id_prefix}_z1_adjust_duration
    name: z1 - Adjust Duration
    icon: mdi:calendar-range
    entity_category: config
    restore_mode: RESTORE_DEFAULT_OFF
    optimistic: true



  # Zone 2 - Front Patio
  - platform: template
    id: ${id_prefix}_z2_0_sun
    name: z2_0 run sun
    icon: mdi:calendar-range
    entity_category: config
    restore_mode: RESTORE_DEFAULT_OFF
    optimistic: true

  - platform: template
    id: ${id_prefix}_z2_1_mon
    name: z2_1 run mon
    icon: mdi:calendar-range
    entity_category: config
    restore_mode: RESTORE_DEFAULT_OFF
    optimistic: true

  - platform: template
    id: ${id_prefix}_z2_2_tue
    name: z2_2 run tue
    icon: mdi:calendar-range
    entity_category: config
    restore_mode: RESTORE_DEFAULT_OFF
    optimistic: true

  - platform: template
    id: ${id_prefix}_z2_3_wed
    name: z2_3 run wed
    icon: mdi:calendar-range
    entity_category: config
    restore_mode: RESTORE_DEFAULT_OFF
    optimistic: true

  - platform: template
    id: ${id_prefix}_z2_4_thu
    name: z2_4 run thu
    icon: mdi:calendar-range
    entity_category: config
    restore_mode: RESTORE_DEFAULT_OFF
    optimistic: true

  - platform: template
    id: ${id_prefix}_z2_5_fri
    name: z2_5 run fri
    icon: mdi:calendar-range
    entity_category: config
    restore_mode: RESTORE_DEFAULT_OFF
    optimistic: true

  - platform: template
    id: ${id_prefix}_z2_6_sat
    name: z2_6 run sat
    icon: mdi:calendar-range
    entity_category: config
    restore_mode: RESTORE_DEFAULT_OFF
    optimistic: true

  - platform: template
    id: ${id_prefix}_z2_adjust_duration
    name: z2 - Adjust Duration
    icon: mdi:calendar-range
    entity_category: config
    restore_mode: RESTORE_DEFAULT_OFF
    optimistic: true


  # Zone 3 - Back Lawn1
  - platform: template
    id: ${id_prefix}_z3_0_sun
    name: z3_0 run sun
    icon: mdi:calendar-range
    entity_category: config
    restore_mode: RESTORE_DEFAULT_OFF
    optimistic: true

  - platform: template
    id: ${id_prefix}_z3_1_mon
    name: z3_1 run mon
    icon: mdi:calendar-range
    entity_category: config
    restore_mode: RESTORE_DEFAULT_OFF
    optimistic: true

  - platform: template
    id: ${id_prefix}_z3_2_tue
    name: z3_2 run tue
    icon: mdi:calendar-range
    entity_category: config
    restore_mode: RESTORE_DEFAULT_OFF
    optimistic: true

  - platform: template
    id: ${id_prefix}_z3_3_wed
    name: z3_3 run wed
    icon: mdi:calendar-range
    entity_category: config
    restore_mode: RESTORE_DEFAULT_OFF
    optimistic: true

  - platform: template
    id: ${id_prefix}_z3_4_thu
    name: z3_4 run thu
    icon: mdi:calendar-range
    entity_category: config
    restore_mode: RESTORE_DEFAULT_OFF
    optimistic: true

  - platform: template
    id: ${id_prefix}_z3_5_fri
    name: z3_5 run fri
    icon: mdi:calendar-range
    entity_category: config
    restore_mode: RESTORE_DEFAULT_OFF
    optimistic: true

  - platform: template
    id: ${id_prefix}_z3_6_sat
    name: z3_6 run sat
    icon: mdi:calendar-range
    entity_category: config
    restore_mode: RESTORE_DEFAULT_OFF
    optimistic: true

  - platform: template
    id: ${id_prefix}_z3_adjust_duration
    name: z3 - Adjust Duration
    icon: mdi:calendar-range
    entity_category: config
    restore_mode: RESTORE_DEFAULT_OFF
    optimistic: true


  # Zone 4 - Back Lawn2
  - platform: template
    id: ${id_prefix}_z4_0_sun
    name: z4_0 run sun
    icon: mdi:calendar-range
    entity_category: config
    restore_mode: RESTORE_DEFAULT_OFF
    optimistic: true

  - platform: template
    id: ${id_prefix}_z4_1_mon
    name: z4_1 run mon
    icon: mdi:calendar-range
    entity_category: config
    restore_mode: RESTORE_DEFAULT_OFF
    optimistic: true

  - platform: template
    id: ${id_prefix}_z4_2_tue
    name: z4_2 run tue
    icon: mdi:calendar-range
    entity_category: config
    restore_mode: RESTORE_DEFAULT_OFF
    optimistic: true

  - platform: template
    id: ${id_prefix}_z4_3_wed
    name: z4_3 run wed
    icon: mdi:calendar-range
    entity_category: config
    restore_mode: RESTORE_DEFAULT_OFF
    optimistic: true

  - platform: template
    id: ${id_prefix}_z4_4_thu
    name: z4_4 run thu
    icon: mdi:calendar-range
    entity_category: config
    restore_mode: RESTORE_DEFAULT_OFF
    optimistic: true

  - platform: template
    id: ${id_prefix}_z4_5_fri
    name: z4_5 run fri
    icon: mdi:calendar-range
    entity_category: config
    restore_mode: RESTORE_DEFAULT_OFF
    optimistic: true

  - platform: template
    id: ${id_prefix}_z4_6_sat
    name: z4_6 run sat
    icon: mdi:calendar-range
    entity_category: config
    restore_mode: RESTORE_DEFAULT_OFF
    optimistic: true

  - platform: template
    id: ${id_prefix}_z4_adjust_duration
    name: z4 - Adjust Duration
    icon: mdi:calendar-range
    entity_category: config
    restore_mode: RESTORE_DEFAULT_OFF
    optimistic: true


  # Zone 5 - Roses & side Yard
  - platform: template
    id: ${id_prefix}_z5_0_sun
    name: z5_0 run sun
    icon: mdi:calendar-range
    entity_category: config
    restore_mode: RESTORE_DEFAULT_OFF
    optimistic: true

  - platform: template
    id: ${id_prefix}_z5_1_mon
    name: z5_1 run mon
    icon: mdi:calendar-range
    entity_category: config
    restore_mode: RESTORE_DEFAULT_OFF
    optimistic: true

  - platform: template
    id: ${id_prefix}_z5_2_tue
    name: z5_2 run tue
    icon: mdi:calendar-range
    entity_category: config
    restore_mode: RESTORE_DEFAULT_OFF
    optimistic: true

  - platform: template
    id: ${id_prefix}_z5_3_wed
    name: z5_3 run wed
    icon: mdi:calendar-range
    entity_category: config
    restore_mode: RESTORE_DEFAULT_OFF
    optimistic: true

  - platform: template
    id: ${id_prefix}_z5_4_thu
    name: z5_4 run thu
    icon: mdi:calendar-range
    entity_category: config
    restore_mode: RESTORE_DEFAULT_OFF
    optimistic: true

  - platform: template
    id: ${id_prefix}_z5_5_fri
    name: z5_5 run fri
    icon: mdi:calendar-range
    entity_category: config
    restore_mode: RESTORE_DEFAULT_OFF
    optimistic: true

  - platform: template
    id: ${id_prefix}_z5_6_sat
    name: z5_6 run sat
    icon: mdi:calendar-range
    entity_category: config
    restore_mode: RESTORE_DEFAULT_OFF
    optimistic: true

  - platform: template
    id: ${id_prefix}_z5_adjust_duration
    name: z5 - Adjust Duration
    icon: mdi:calendar-range
    entity_category: config
    restore_mode: RESTORE_DEFAULT_OFF
    optimistic: true


  # Zone 6 - Garden & back fence
  - platform: template
    id: ${id_prefix}_z6_0_sun
    name: z6_0 run sun
    icon: mdi:calendar-range
    entity_category: config
    restore_mode: RESTORE_DEFAULT_OFF
    optimistic: true

  - platform: template
    id: ${id_prefix}_z6_1_mon
    name: z6_1 run mon
    icon: mdi:calendar-range
    entity_category: config
    restore_mode: RESTORE_DEFAULT_OFF
    optimistic: true

  - platform: template
    id: ${id_prefix}_z6_2_tue
    name: z6_2 run tue
    icon: mdi:calendar-range
    entity_category: config
    restore_mode: RESTORE_DEFAULT_OFF
    optimistic: true

  - platform: template
    id: ${id_prefix}_z6_3_wed
    name: z6_3 run wed
    icon: mdi:calendar-range
    entity_category: config
    restore_mode: RESTORE_DEFAULT_OFF
    optimistic: true

  - platform: template
    id: ${id_prefix}_z6_4_thu
    name: z6_4 run thu
    icon: mdi:calendar-range
    entity_category: config
    restore_mode: RESTORE_DEFAULT_OFF
    optimistic: true

  - platform: template
    id: ${id_prefix}_z6_5_fri
    name: z6_5 run fri
    icon: mdi:calendar-range
    entity_category: config
    restore_mode: RESTORE_DEFAULT_OFF
    optimistic: true

  - platform: template
    id: ${id_prefix}_z6_6_sat
    name: z6_6 run sat
    icon: mdi:calendar-range
    entity_category: config
    restore_mode: RESTORE_DEFAULT_OFF
    optimistic: true

  - platform: template
    id: ${id_prefix}_z6_adjust_duration
    name: z6 - Adjust Duration
    icon: mdi:calendar-range
    entity_category: config
    restore_mode: RESTORE_DEFAULT_OFF
    optimistic: true



  # Zone 7 - Left Driveway
  - platform: template
    id: ${id_prefix}_z7_0_sun
    name: z7_0 run sun
    icon: mdi:calendar-range
    entity_category: config
    restore_mode: RESTORE_DEFAULT_OFF
    optimistic: true

  - platform: template
    id: ${id_prefix}_z7_1_mon
    name: z7_1 run mon
    icon: mdi:calendar-range
    entity_category: config
    restore_mode: RESTORE_DEFAULT_OFF
    optimistic: true

  - platform: template
    id: ${id_prefix}_z7_2_tue
    name: z7_2 run tue
    icon: mdi:calendar-range
    entity_category: config
    restore_mode: RESTORE_DEFAULT_OFF
    optimistic: true

  - platform: template
    id: ${id_prefix}_z7_3_wed
    name: z7_3 run wed
    icon: mdi:calendar-range
    entity_category: config
    restore_mode: RESTORE_DEFAULT_OFF
    optimistic: true

  - platform: template
    id: ${id_prefix}_z7_4_thu
    name: z7_4 run thu
    icon: mdi:calendar-range
    entity_category: config
    restore_mode: RESTORE_DEFAULT_OFF
    optimistic: true

  - platform: template
    id: ${id_prefix}_z7_5_fri
    name: z7_5 run fri
    icon: mdi:calendar-range
    entity_category: config
    restore_mode: RESTORE_DEFAULT_OFF
    optimistic: true

  - platform: template
    id: ${id_prefix}_z7_6_sat
    name: z7_6 run sat
    icon: mdi:calendar-range
    entity_category: config
    restore_mode: RESTORE_DEFAULT_OFF
    optimistic: true

  - platform: template
    id: ${id_prefix}_z7_adjust_duration
    name: z7 - Adjust Duration
    icon: mdi:calendar-range
    entity_category: config
    restore_mode: RESTORE_DEFAULT_OFF
    optimistic: true


  # Switches to disable zones
  - platform: template
    id: ${id_prefix}_z1_disable
    name: z1 - Disable
    icon: mdi:water-off
    entity_category: config
    restore_mode: RESTORE_DEFAULT_OFF
    optimistic: true

  - platform: template
    id: ${id_prefix}_z2_disable
    name: z2 - Disable
    icon: mdi:water-off
    entity_category: config
    restore_mode: RESTORE_DEFAULT_OFF
    optimistic: true

  - platform: template
    id: ${id_prefix}_z3_disable
    name: z3 - Disable
    icon: mdi:water-off
    entity_category: config
    restore_mode: RESTORE_DEFAULT_OFF
    optimistic: true

  - platform: template
    id: ${id_prefix}_z4_disable
    name: z4 - Disable
    icon: mdi:water-off
    entity_category: config
    restore_mode: RESTORE_DEFAULT_OFF
    optimistic: true

  - platform: template
    id: ${id_prefix}_z5_disable
    name: z5 - Disable
    icon: mdi:water-off
    entity_category: config
    restore_mode: RESTORE_DEFAULT_OFF
    optimistic: true

  - platform: template
    id: ${id_prefix}_z6_disable
    name: z6 - Disable
    icon: mdi:water-off
    entity_category: config
    restore_mode: RESTORE_DEFAULT_OFF
    optimistic: true

  - platform: template
    id: ${id_prefix}_z7_disable
    name: z7 - Disable
    icon: mdi:water-off
    entity_category: config
    restore_mode: RESTORE_DEFAULT_OFF
    optimistic: true

  - platform: template
    id: ${id_prefix}_z8_disable
    name: z8 - Disable
    icon: mdi:water-off
    entity_category: config
    restore_mode: RESTORE_DEFAULT_OFF
    optimistic: true

#########################################################
# END ALL SWITCHES
#########################################################

#datetimes are used to specify the daily start time
datetime:
  - platform: template
    name: 01 mon Start Time
    entity_category: config
    id: ${id_prefix}_mon_st
    type: time
    optimistic: true
    restore_value: true

  - platform: template
    name: 02 tue Start Time
    entity_category: config
    id: ${id_prefix}_tue_st
    type: time
    optimistic: true
    restore_value: true

  - platform: template
    name: 03 wed Start Time
    entity_category: config
    id: ${id_prefix}_wed_st
    type: time
    optimistic: true
    restore_value: true

  - platform: template
    name: 04 thu Start Time
    entity_category: config
    id: ${id_prefix}_thu_st
    type: time
    optimistic: true
    restore_value: true

  - platform: template
    name: 05 fri Start Time
    entity_category: config
    id: ${id_prefix}_fri_st
    type: time
    optimistic: true
    restore_value: true

  - platform: template
    name: 06 sat Start Time
    entity_category: config
    id: ${id_prefix}_sat_st
    type: time
    optimistic: true
    restore_value: true

  - platform: template
    name: 00 sun Start Time
    entity_category: config
    id: ${id_prefix}_sun_st
    type: time
    optimistic: true
    restore_value: true


#########################################################
# BEGIN SENSORS
#########################################################
sensor:
  - platform: wifi_signal # Reports the WiFi signal strength/RSSI in dB
    name: "WiFi Signal dB"
    id: wifi_signal_db
    update_interval: 60s
    entity_category: "diagnostic"

  - platform: copy # Reports the WiFi signal strength in %
    source_id: wifi_signal_db
    name: "WiFi Signal Percent"
    filters:
      - lambda: return min(max(2 * (x + 100.0), 0.0), 100.0);
    unit_of_measurement: "%"
    entity_category: "diagnostic"

  - platform: uptime
    id: uptime_seconds
    name: Uptime
    entity_category: "diagnostic"
    

 
  #########################################################
  # BEGIN rain skip sensors
  #########################################################
  - platform: homeassistant
    id: forecasted_rain
    entity_id: sensor.rain_forecast_san_jose
    name: "${id_prefix} Rain Forecast San Jose"
    accuracy_decimals: 2
    internal: false   # This makes it visible in HA

  - platform: homeassistant
    id: actual_rain
    entity_id: sensor.rain_today_san_jose
    name: "${id_prefix} Rain Today San Jose" 
    accuracy_decimals: 2
    internal: false

  #########################################################
  # BEGIN Temperature Sensors
  #########################################################
  - platform: homeassistant
    id: hi_temp_today
    entity_id: sensor.max_temperature_san_jose_forecast0
    name: "${id_prefix} Max Temperature San Jose Forecast0" 
    accuracy_decimals: 0
    internal: false

  - platform: homeassistant
    id: hi_temp_tomorrow
    entity_id: sensor.max_temperature_san_jose_forecast1
    name: "${id_prefix} Max Temperature San Jose Forecast1"
    accuracy_decimals: 0
    internal: false

  - platform: homeassistant
    id: hi_temp_2days
    entity_id: sensor.max_temperature_san_jose_forecast2
    name: "${id_prefix} Max Temperature San Jose Forecast2"
    accuracy_decimals: 0
    internal: false

  - platform: homeassistant
    id: temp_now
    entity_id: sensor.outdoor_temperature_merged
    name: "${id_prefix} Outdoor Temperature Merged"
    accuracy_decimals: 1
    internal: false


  - platform: template
    name: "Weighted Avg Temp"
    id: weighted_avg_temp
    #unit_of_measurement: "min"
    update_interval: 15min
    lambda: |-
      float fcst_today = id(hi_temp_today).state;
      float fcst_t1 = id(hi_temp_tomorrow).state;
      float fcst_t2 = id(hi_temp_2days).state;

      float weighted_avg = (fcst_today * 0.5) + (fcst_t1 * 0.3) + (fcst_t2 * 0.2);

      return weighted_avg;


  - platform: template
    name: "Duration Adjustment"
    id: run_duration_adjustment
    unit_of_measurement: "min"
    update_interval: 15min
    lambda: |-
      float weighted_avg = id(weighted_avg_temp).state;
      float adjustment = 0.0;

      // Adjustment based on weighted avg of today & next two day's forecast
      if (weighted_avg > 82) {
        adjustment += floor((weighted_avg - 82) / 4.0);  // +1 min per 4°F over 82
      } else if (weighted_avg < 72) {
        adjustment -= floor((72 - weighted_avg) / 4.0) * 2;  // -2 min per 4°F under 72
      } else {
        adjustment += 0.0;  // No adjustment for temperatures between 72–82
      }

      return adjustment;



  #### END Temperature Sensors ####

#This needs a conditional to check if zone is using the adjustment
  - platform: template
    name: "Active Valve Total Runtime"
    id: active_valve_total_runtime
    unit_of_measurement: "min"
    update_interval: 10s
    lambda: |-
      float adj = id(run_duration_adjustment).state;

      if (id(nachio_valve01).state) return max(1.0f, min((id(zone_1_run_duration).state + adj), 30.0f));
      if (id(nachio_valve02).state) return max(1.0f, min((id(zone_2_run_duration).state + adj), 30.0f));
      if (id(nachio_valve03).state) return max(1.0f, min((id(zone_3_run_duration).state + adj), 30.0f));
      if (id(nachio_valve04).state) return max(1.0f, min((id(zone_4_run_duration).state + adj), 30.0f));
      if (id(nachio_valve05).state) return max(1.0f, min((id(zone_5_run_duration).state + adj), 30.0f));
      if (id(nachio_valve06).state) return max(1.0f, min((id(zone_6_run_duration).state + adj), 30.0f));
      if (id(nachio_valve07).state) return max(1.0f, min((id(zone_7_run_duration).state + adj), 30.0f));
      return 0.0f;



  # These manage queue info
  #- platform: template
  #  id: sprinkler_queue_count
  #  name: "Sprinkler Queue Count"
  #  unit_of_measurement: "zones"
  #  icon: "mdi:format-list-numbered"
  #  update_interval: 10s
  #  lambda: |-
  #    return id(sprinkler_queue_size);

  - platform: template
    name: "Sprinkler Queue Count"
    accuracy_decimals: 0
    lambda: |-
      return id(sprinkler_queue_size);
    update_interval: 10s

  - platform: template
    name: "Sprinkler Queue Time Remaining"
    unit_of_measurement: "min"
    accuracy_decimals: 1
    lambda: |-
      return id(sprinkler_queue_time_remaining) / 60.0;
    update_interval: 10s




binary_sensor:
  - platform: status
    name: "Connection Status"


# set up time and check every minute (or second) for scheduled actions
time:
  - platform: homeassistant
    id: ha_time
    timezone: America/Los_Angeles
    on_time:
      # Every 1 minute
      - seconds: 0
        minutes: /1
        then:
          - script.execute: script1_trigger
      #Rain Skip
      - seconds: 0
        minutes: /15  # Evaluate every 15 minutes
        then:
          - script.execute: check_rain_skip_conditions

    on_time_sync:
      then:
        - logger.log: "Synchronized system clock"




  ##### Begin RAIN SKIP ##### 
  #- platform: homeassistant
  #  id: ha_time
  #  on_time:
  #    - seconds: 0
  #      minutes: /15  # Evaluate every 15 minutes
  #      then:
  #        - script.execute: check_rain_skip_conditions

  ##### End RAIN SKIP  #####




script:

    # Step1: Check to see of the day today and the time now, match a predefined weekday start time 
  - id: script1_trigger
    then:
      lambda: |-
        int dow = id(ha_time).now().day_of_week;
        int hour = id(ha_time).now().hour;
        int minute = id(ha_time).now().minute;
        ESP_LOGD("custom", "beginning script1");
        
        bool run_now = false;

        if (dow == 1) run_now = (hour == id(${id_prefix}_sun_st).hour && minute == id(${id_prefix}_sun_st).minute);
        if (dow == 2) run_now = (hour == id(${id_prefix}_mon_st).hour && minute == id(${id_prefix}_mon_st).minute);
        if (dow == 3) run_now = (hour == id(${id_prefix}_tue_st).hour && minute == id(${id_prefix}_tue_st).minute);
        if (dow == 4) run_now = (hour == id(${id_prefix}_wed_st).hour && minute == id(${id_prefix}_wed_st).minute);
        if (dow == 5) run_now = (hour == id(${id_prefix}_thu_st).hour && minute == id(${id_prefix}_thu_st).minute);
        if (dow == 6) run_now = (hour == id(${id_prefix}_fri_st).hour && minute == id(${id_prefix}_fri_st).minute);
        if (dow == 7) run_now = (hour == id(${id_prefix}_sat_st).hour && minute == id(${id_prefix}_sat_st).minute);


        ESP_LOGD("custom", "Today is day %d (0=Sun, 1=Mon, ..., 6=Sat)", dow);
        ESP_LOGD("custom", "Current time: %02d:%02d:%02d", hour, minute, id(ha_time).now().second);
        ESP_LOGD("custom", "tue scheduled time: %02d:%02d", id(${id_prefix}_tue_st).hour, id(${id_prefix}_tue_st).minute);

        if (run_now) {
          ESP_LOGD("custom", "completed script1, moving to script2");
          id(script2_1_check_active_zones_today).execute();
        }

    #Step2: Identify zones that are set to run today
  - id: script2_1_check_active_zones_today
    then:
      - lambda: |-
          auto weekday = id(ha_time).now().day_of_week;  // 0=sun, 1=mon, ..., 6=sat

          bool should_run = false;

          if (weekday == 1) {  // sun
            should_run = (
              id(nachio_z1_0_sun).state || id(nachio_z2_0_sun).state ||
              id(nachio_z3_0_sun).state || id(nachio_z4_0_sun).state ||
              id(nachio_z5_0_sun).state || id(nachio_z6_0_sun).state ||
              id(nachio_z7_0_sun).state
            );
          } else if (weekday == 2) {  // tue
            should_run = (
              id(nachio_z1_1_mon).state || id(nachio_z2_1_mon).state ||
              id(nachio_z3_1_mon).state || id(nachio_z4_1_mon).state ||
              id(nachio_z5_1_mon).state || id(nachio_z6_1_mon).state ||
              id(nachio_z7_1_mon).state
            );
          } else if (weekday == 3) {  // tue
            should_run = (
              id(nachio_z1_2_tue).state || id(nachio_z2_2_tue).state ||
              id(nachio_z3_2_tue).state || id(nachio_z4_2_tue).state ||
              id(nachio_z5_2_tue).state || id(nachio_z6_2_tue).state ||
              id(nachio_z7_2_tue).state
            );
          } else if (weekday == 4) {  // wed
            should_run = (
              id(nachio_z1_3_wed).state || id(nachio_z2_3_wed).state ||
              id(nachio_z3_3_wed).state || id(nachio_z4_3_wed).state ||
              id(nachio_z5_3_wed).state || id(nachio_z6_3_wed).state ||
              id(nachio_z7_3_wed).state
            );
          } else if (weekday == 5) {  // thu
            should_run = (
              id(nachio_z1_4_thu).state || id(nachio_z2_4_thu).state ||
              id(nachio_z3_4_thu).state || id(nachio_z4_4_thu).state ||
              id(nachio_z5_4_thu).state || id(nachio_z6_4_thu).state ||
              id(nachio_z7_4_thu).state
            );
          } else if (weekday == 6) {  // fri
            should_run = (
              id(nachio_z1_5_fri).state || id(nachio_z2_5_fri).state ||
              id(nachio_z3_5_fri).state || id(nachio_z4_5_fri).state ||
              id(nachio_z5_5_fri).state || id(nachio_z6_5_fri).state ||
              id(nachio_z7_5_fri).state
            );
          } else if (weekday == 7) {  // sat
            should_run = (
              id(nachio_z1_6_sat).state || id(nachio_z2_6_sat).state ||
              id(nachio_z3_6_sat).state || id(nachio_z4_6_sat).state ||
              id(nachio_z5_6_sat).state || id(nachio_z6_6_sat).state ||
              id(nachio_z7_6_sat).state
            );
          }

          if (should_run) {
            ESP_LOGD("custom", "At least one zone is active for today. Executing script2_2_check_zone_day");
            id(script2_2_check_zone_day).execute();
            id(script3_start_queue).execute();
          } else {
            ESP_LOGD("custom", "No zones active for today.");
          }




  - id: script2_2_check_zone_day
    then:
      - lambda: |-
          int weekday = id(ha_time).now().day_of_week;
          switch (weekday) {
            case 1:
              id(script2_2_check_zone_sun).execute();
              break;
            case 2:
              id(script2_2_check_zone_mon).execute();
              break;
            case 3:
              id(script2_2_check_zone_tue).execute();
              break;
            case 4:
              id(script2_2_check_zone_wed).execute();
              break;
            case 5:
              id(script2_2_check_zone_thu).execute();
              break;
            case 6:
              id(script2_2_check_zone_fri).execute();
              break;
            case 7:
              id(script2_2_check_zone_sat).execute();
              break;
          }


  - id: script2_2_check_zone_sun
    then:
      - lambda: |-
          if (id(nachio_z1_0_sun).state && !id(nachio_z1_disable).state) {
            id(zone_index) = 0;
            if (id(nachio_z1_adjust_duration).state) {
              id(zone_duration) = (id(zone_1_run_duration).state + id(run_duration_adjustment).state)* 60;
            } else {
              id(zone_duration) = id(zone_1_run_duration).state * 60;
            }
            id(script2_3_queue_zone).execute();
          }
          if (id(nachio_z2_0_sun).state && !id(nachio_z2_disable).state) {
            id(zone_index) = 1;
            if (id(nachio_z2_adjust_duration).state) {
              id(zone_duration) = (id(zone_2_run_duration).state + id(run_duration_adjustment).state)* 60;
            } else {
              id(zone_duration) = id(zone_2_run_duration).state * 60;
            }
            id(script2_3_queue_zone).execute();
          }
          if (id(nachio_z3_0_sun).state && !id(nachio_z3_disable).state) {
            id(zone_index) = 2;
            if (id(nachio_z3_adjust_duration).state) {
              id(zone_duration) = (id(zone_3_run_duration).state + id(run_duration_adjustment).state)* 60;
            } else {
              id(zone_duration) = id(zone_3_run_duration).state * 60;
            }
            id(script2_3_queue_zone).execute();
          }
          if (id(nachio_z4_0_sun).state && !id(nachio_z4_disable).state) {
            id(zone_index) = 3;
            if (id(nachio_z4_adjust_duration).state) {
              id(zone_duration) = (id(zone_4_run_duration).state + id(run_duration_adjustment).state)* 60;
            } else {
              id(zone_duration) = id(zone_4_run_duration).state * 60;
            }
            id(script2_3_queue_zone).execute();
          }
          if (id(nachio_z5_0_sun).state && !id(nachio_z5_disable).state) {
            id(zone_index) = 4;
            if (id(nachio_z5_adjust_duration).state) {
              id(zone_duration) = (id(zone_5_run_duration).state + id(run_duration_adjustment).state)* 60;
            } else {
              id(zone_duration) = id(zone_5_run_duration).state * 60;
            }
            id(script2_3_queue_zone).execute();
          }
          if (id(nachio_z6_0_sun).state && !id(nachio_z6_disable).state) {
            id(zone_index) = 5;
            if (id(nachio_z6_adjust_duration).state) {
              id(zone_duration) = (id(zone_6_run_duration).state + id(run_duration_adjustment).state)* 60;
            } else {
              id(zone_duration) = id(zone_6_run_duration).state * 60;
            }
            id(script2_3_queue_zone).execute();
          }
          if (id(nachio_z7_0_sun).state && !id(nachio_z7_disable).state) {
            id(zone_index) = 6;
            if (id(nachio_z7_adjust_duration).state) {
              id(zone_duration) = (id(zone_7_run_duration).state + id(run_duration_adjustment).state)* 60;
            } else {
              id(zone_duration) = id(zone_7_run_duration).state * 60;
            }
            id(script2_3_queue_zone).execute();
          }
          //if (id(nachio_z8_0_sun).state) {
          //  id(zone_index) = 7;
          //  id(zone_duration) = id(zone_8_run_duration).state * 60;
          //  id(script2_3_queue_zone).execute();
          //}


  - id: script2_2_check_zone_mon
    then:
      - lambda: |-
          if (id(nachio_z1_1_mon).state && !id(nachio_z1_disable).state) {
            id(zone_index) = 0;
            if (id(nachio_z1_adjust_duration).state) {
              id(zone_duration) = (id(zone_1_run_duration).state + id(run_duration_adjustment).state)* 60;
            } else {
              id(zone_duration) = id(zone_1_run_duration).state * 60;
            }
            id(script2_3_queue_zone).execute();
          }
          if (id(nachio_z2_1_mon).state && !id(nachio_z2_disable).state) {
            id(zone_index) = 1;
            if (id(nachio_z2_adjust_duration).state) {
              id(zone_duration) = (id(zone_2_run_duration).state + id(run_duration_adjustment).state)* 60;
            } else {
              id(zone_duration) = id(zone_2_run_duration).state * 60;
            }
            id(script2_3_queue_zone).execute();
          }
          if (id(nachio_z3_1_mon).state && !id(nachio_z3_disable).state) {
            id(zone_index) = 2;
            if (id(nachio_z3_adjust_duration).state) {
              id(zone_duration) = (id(zone_3_run_duration).state + id(run_duration_adjustment).state)* 60;
            } else {
              id(zone_duration) = id(zone_3_run_duration).state * 60;
            }
            id(script2_3_queue_zone).execute();
          }
          if (id(nachio_z4_1_mon).state && !id(nachio_z4_disable).state) {
            id(zone_index) = 3;
            if (id(nachio_z4_adjust_duration).state) {
              id(zone_duration) = (id(zone_4_run_duration).state + id(run_duration_adjustment).state)* 60;
            } else {
              id(zone_duration) = id(zone_4_run_duration).state * 60;
            }
            id(script2_3_queue_zone).execute();
          }
          if (id(nachio_z5_1_mon).state && !id(nachio_z5_disable).state) {
            id(zone_index) = 4;
            if (id(nachio_z5_adjust_duration).state) {
              id(zone_duration) = (id(zone_5_run_duration).state + id(run_duration_adjustment).state)* 60;
            } else {
              id(zone_duration) = id(zone_5_run_duration).state * 60;
            }
            id(script2_3_queue_zone).execute();
          }
          if (id(nachio_z6_1_mon).state && !id(nachio_z6_disable).state) {
            id(zone_index) = 5;
            if (id(nachio_z6_adjust_duration).state) {
              id(zone_duration) = (id(zone_6_run_duration).state + id(run_duration_adjustment).state)* 60;
            } else {
              id(zone_duration) = id(zone_6_run_duration).state * 60;
            }
            id(script2_3_queue_zone).execute();
          }
          if (id(nachio_z7_1_mon).state && !id(nachio_z7_disable).state) {
            id(zone_index) = 6;
            if (id(nachio_z7_adjust_duration).state) {
              id(zone_duration) = (id(zone_7_run_duration).state + id(run_duration_adjustment).state)* 60;
            } else {
              id(zone_duration) = id(zone_7_run_duration).state * 60;
            }
            id(script2_3_queue_zone).execute();
          }
          //if (id(nachio_z8_1_mon).state && !id(nachio_z8_disable).state) {
          //  id(zone_index) = 7;
          //  if (id(nachio_z2_adjust_duration).state) {
          //    id(zone_duration) = (id(zone_2_run_duration).state + id(run_duration_adjustment).state)* 60;
          //} else {
          //    id(zone_duration) = id(zone_2_run_duration).state * 60;
          //}
          //  id(script2_3_queue_zone).execute();
          //}

  - id: script2_2_check_zone_tue
    then:
      - lambda: |-
          if (id(nachio_z1_2_tue).state && !id(nachio_z1_disable).state) {
            id(zone_index) = 0;
            if (id(nachio_z1_adjust_duration).state) {
              id(zone_duration) = (id(zone_1_run_duration).state + id(run_duration_adjustment).state)* 60;
            } else {
              id(zone_duration) = id(zone_1_run_duration).state * 60;
            }
            id(script2_3_queue_zone).execute();
          }
          if (id(nachio_z2_2_tue).state && !id(nachio_z2_disable).state) {
            id(zone_index) = 1;
            if (id(nachio_z2_adjust_duration).state) {
              id(zone_duration) = (id(zone_2_run_duration).state + id(run_duration_adjustment).state)* 60;
            } else {
              id(zone_duration) = id(zone_2_run_duration).state * 60;
            }
            id(script2_3_queue_zone).execute();
          }
          if (id(nachio_z3_2_tue).state && !id(nachio_z3_disable).state) {
            id(zone_index) = 2;
            if (id(nachio_z3_adjust_duration).state) {
              id(zone_duration) = (id(zone_3_run_duration).state + id(run_duration_adjustment).state)* 60;
            } else {
              id(zone_duration) = id(zone_3_run_duration).state * 60;
            }
            id(script2_3_queue_zone).execute();
          }
          if (id(nachio_z4_2_tue).state && !id(nachio_z4_disable).state) {
            id(zone_index) = 3;
            if (id(nachio_z4_adjust_duration).state) {
              id(zone_duration) = (id(zone_4_run_duration).state + id(run_duration_adjustment).state)* 60;
            } else {
              id(zone_duration) = id(zone_4_run_duration).state * 60;
            }
            id(script2_3_queue_zone).execute();
          }
          if (id(nachio_z5_2_tue).state && !id(nachio_z5_disable).state) {
            id(zone_index) = 4;
            if (id(nachio_z5_adjust_duration).state) {
              id(zone_duration) = (id(zone_5_run_duration).state + id(run_duration_adjustment).state)* 60;
            } else {
              id(zone_duration) = id(zone_5_run_duration).state * 60;
            }
            id(script2_3_queue_zone).execute();
          }
          if (id(nachio_z6_2_tue).state && !id(nachio_z6_disable).state) {
            id(zone_index) = 5;
            if (id(nachio_z6_adjust_duration).state) {
              id(zone_duration) = (id(zone_6_run_duration).state + id(run_duration_adjustment).state)* 60;
            } else {
              id(zone_duration) = id(zone_6_run_duration).state * 60;
            }
            id(script2_3_queue_zone).execute();
          }
          if (id(nachio_z7_2_tue).state && !id(nachio_z7_disable).state) {
            id(zone_index) = 6;
            if (id(nachio_z7_adjust_duration).state) {
              id(zone_duration) = (id(zone_7_run_duration).state + id(run_duration_adjustment).state)* 60;
            } else {
              id(zone_duration) = id(zone_7_run_duration).state * 60;
            }
            id(script2_3_queue_zone).execute();
          }
          //if (id(nachio_z8_2_tue).state) {
          //  id(zone_index) = 7;
          //  id(zone_duration) = id(zone_8_run_duration).state * 60;
          //  id(script2_3_queue_zone).execute();
          //}

  - id: script2_2_check_zone_wed
    then:
      - lambda: |-
          if (id(nachio_z1_3_wed).state && !id(nachio_z1_disable).state) {
            id(zone_index) = 0;
            if (id(nachio_z1_adjust_duration).state) {
              id(zone_duration) = (id(zone_1_run_duration).state + id(run_duration_adjustment).state)* 60;
            } else {
              id(zone_duration) = id(zone_1_run_duration).state * 60;
            }
            id(script2_3_queue_zone).execute();
          }
          if (id(nachio_z2_3_wed).state && !id(nachio_z2_disable).state) {
            id(zone_index) = 1;
            if (id(nachio_z2_adjust_duration).state) {
              id(zone_duration) = (id(zone_2_run_duration).state + id(run_duration_adjustment).state)* 60;
            } else {
              id(zone_duration) = id(zone_2_run_duration).state * 60;
            }
            id(script2_3_queue_zone).execute();
          }
          if (id(nachio_z3_3_wed).state && !id(nachio_z3_disable).state) {
            id(zone_index) = 2;
            if (id(nachio_z3_adjust_duration).state) {
              id(zone_duration) = (id(zone_3_run_duration).state + id(run_duration_adjustment).state)* 60;
            } else {
              id(zone_duration) = id(zone_3_run_duration).state * 60;
            }
            id(script2_3_queue_zone).execute();
          }
          if (id(nachio_z4_3_wed).state && !id(nachio_z4_disable).state) {
            id(zone_index) = 3;
            if (id(nachio_z4_adjust_duration).state) {
              id(zone_duration) = (id(zone_4_run_duration).state + id(run_duration_adjustment).state)* 60;
            } else {
              id(zone_duration) = id(zone_4_run_duration).state * 60;
            }
            id(script2_3_queue_zone).execute();
          }
          if (id(nachio_z5_3_wed).state && !id(nachio_z5_disable).state) {
            id(zone_index) = 4;
            if (id(nachio_z5_adjust_duration).state) {
              id(zone_duration) = (id(zone_5_run_duration).state + id(run_duration_adjustment).state)* 60;
            } else {
              id(zone_duration) = id(zone_5_run_duration).state * 60;
            }
            id(script2_3_queue_zone).execute();
          }
          if (id(nachio_z6_3_wed).state && !id(nachio_z6_disable).state) {
            id(zone_index) = 5;
            if (id(nachio_z6_adjust_duration).state) {
              id(zone_duration) = (id(zone_6_run_duration).state + id(run_duration_adjustment).state)* 60;
            } else {
              id(zone_duration) = id(zone_6_run_duration).state * 60;
            }
            id(script2_3_queue_zone).execute();
          }
          if (id(nachio_z7_3_wed).state && !id(nachio_z7_disable).state) {
            id(zone_index) = 6;
            if (id(nachio_z7_adjust_duration).state) {
              id(zone_duration) = (id(zone_7_run_duration).state + id(run_duration_adjustment).state)* 60;
            } else {
              id(zone_duration) = id(zone_7_run_duration).state * 60;
            }
            id(script2_3_queue_zone).execute();
          }
          //if (id(nachio_z8_3_wed).state) {
          //  id(zone_index) = 7;
          //  id(zone_duration) = id(zone_8_run_duration).state * 60;
          //  id(script2_3_queue_zone).execute();
          //}

  - id: script2_2_check_zone_thu
    then:
      - lambda: |-
          if (id(nachio_z1_4_thu).state && !id(nachio_z1_disable).state) {
            id(zone_index) = 0;
            if (id(nachio_z1_adjust_duration).state) {
              id(zone_duration) = (id(zone_1_run_duration).state + id(run_duration_adjustment).state)* 60;
            } else {
              id(zone_duration) = id(zone_1_run_duration).state * 60;
            }
            id(script2_3_queue_zone).execute();
          }
          if (id(nachio_z2_4_thu).state && !id(nachio_z2_disable).state) {
            id(zone_index) = 1;
            if (id(nachio_z2_adjust_duration).state) {
              id(zone_duration) = (id(zone_2_run_duration).state + id(run_duration_adjustment).state)* 60;
            } else {
              id(zone_duration) = id(zone_2_run_duration).state * 60;
            }
            id(script2_3_queue_zone).execute();
          }
          if (id(nachio_z3_4_thu).state && !id(nachio_z3_disable).state) {
            id(zone_index) = 2;
            if (id(nachio_z3_adjust_duration).state) {
              id(zone_duration) = (id(zone_3_run_duration).state + id(run_duration_adjustment).state)* 60;
            } else {
              id(zone_duration) = id(zone_3_run_duration).state * 60;
            }
            id(script2_3_queue_zone).execute();
          }
          if (id(nachio_z4_4_thu).state && !id(nachio_z4_disable).state) {
            id(zone_index) = 3;
            if (id(nachio_z4_adjust_duration).state) {
              id(zone_duration) = (id(zone_4_run_duration).state + id(run_duration_adjustment).state)* 60;
            } else {
              id(zone_duration) = id(zone_4_run_duration).state * 60;
            }
            id(script2_3_queue_zone).execute();
          }
          if (id(nachio_z5_4_thu).state && !id(nachio_z5_disable).state) {
            id(zone_index) = 4;
            if (id(nachio_z5_adjust_duration).state) {
              id(zone_duration) = (id(zone_5_run_duration).state + id(run_duration_adjustment).state)* 60;
            } else {
              id(zone_duration) = id(zone_5_run_duration).state * 60;
            }
            id(script2_3_queue_zone).execute();
          }
          if (id(nachio_z6_4_thu).state && !id(nachio_z6_disable).state) {
            id(zone_index) = 5;
            if (id(nachio_z6_adjust_duration).state) {
              id(zone_duration) = (id(zone_6_run_duration).state + id(run_duration_adjustment).state)* 60;
            } else {
              id(zone_duration) = id(zone_6_run_duration).state * 60;
            }
            id(script2_3_queue_zone).execute();
          }
          if (id(nachio_z7_4_thu).state && !id(nachio_z7_disable).state) {
            id(zone_index) = 6;
            if (id(nachio_z7_adjust_duration).state) {
              id(zone_duration) = (id(zone_7_run_duration).state + id(run_duration_adjustment).state)* 60;
            } else {
              id(zone_duration) = id(zone_7_run_duration).state * 60;
            }
            id(script2_3_queue_zone).execute();
          }
          //if (id(nachio_z8_4_thu).state) {
          //  id(zone_index) = 7;
          //  id(zone_duration) = id(zone_8_run_duration).state * 60;
          //  id(script2_3_queue_zone).execute();
          //}


  - id: script2_2_check_zone_fri
    then:
      - lambda: |-
          if (id(nachio_z1_5_fri).state && !id(nachio_z1_disable).state) {
            id(zone_index) = 0;
            if (id(nachio_z1_adjust_duration).state) {
              id(zone_duration) = (id(zone_1_run_duration).state + id(run_duration_adjustment).state)* 60;
            } else {
              id(zone_duration) = id(zone_1_run_duration).state * 60;
            }
            id(script2_3_queue_zone).execute();
          }
          if (id(nachio_z2_5_fri).state && !id(nachio_z2_disable).state) {
            id(zone_index) = 1;
            if (id(nachio_z2_adjust_duration).state) {
              id(zone_duration) = (id(zone_2_run_duration).state + id(run_duration_adjustment).state)* 60;
            } else {
              id(zone_duration) = id(zone_2_run_duration).state * 60;
            }
            id(script2_3_queue_zone).execute();
          }
          if (id(nachio_z3_5_fri).state && !id(nachio_z3_disable).state) {
            id(zone_index) = 2;
            if (id(nachio_z3_adjust_duration).state) {
              id(zone_duration) = (id(zone_3_run_duration).state + id(run_duration_adjustment).state)* 60;
            } else {
              id(zone_duration) = id(zone_3_run_duration).state * 60;
            }
            id(script2_3_queue_zone).execute();
          }
          if (id(nachio_z4_5_fri).state && !id(nachio_z4_disable).state) {
            id(zone_index) = 3;
            if (id(nachio_z4_adjust_duration).state) {
              id(zone_duration) = (id(zone_4_run_duration).state + id(run_duration_adjustment).state)* 60;
            } else {
              id(zone_duration) = id(zone_4_run_duration).state * 60;
            }
            id(script2_3_queue_zone).execute();
          }
          if (id(nachio_z5_5_fri).state && !id(nachio_z5_disable).state) {
            id(zone_index) = 4;
            if (id(nachio_z5_adjust_duration).state) {
              id(zone_duration) = (id(zone_5_run_duration).state + id(run_duration_adjustment).state)* 60;
            } else {
              id(zone_duration) = id(zone_5_run_duration).state * 60;
            }
            id(script2_3_queue_zone).execute();
          }
          if (id(nachio_z6_5_fri).state && !id(nachio_z6_disable).state) {
            id(zone_index) = 5;
            if (id(nachio_z6_adjust_duration).state) {
              id(zone_duration) = (id(zone_6_run_duration).state + id(run_duration_adjustment).state)* 60;
            } else {
              id(zone_duration) = id(zone_6_run_duration).state * 60;
            }
            id(script2_3_queue_zone).execute();
          }
          if (id(nachio_z7_5_fri).state && !id(nachio_z7_disable).state) {
            id(zone_index) = 6;
            if (id(nachio_z7_adjust_duration).state) {
              id(zone_duration) = (id(zone_7_run_duration).state + id(run_duration_adjustment).state)* 60;
            } else {
              id(zone_duration) = id(zone_7_run_duration).state * 60;
            }
            id(script2_3_queue_zone).execute();
          }
          //if (id(nachio_z8_5_fri).state) {
          //  id(zone_index) = 7;
          //  id(zone_duration) = id(zone_8_run_duration).state * 60;
          //  id(script2_3_queue_zone).execute();
          //}

  - id: script2_2_check_zone_sat
    then:
      - lambda: |-
          if (id(nachio_z1_6_sat).state && !id(nachio_z1_disable).state) {
            id(zone_index) = 0;
            if (id(nachio_z1_adjust_duration).state) {
              id(zone_duration) = (id(zone_1_run_duration).state + id(run_duration_adjustment).state)* 60;
            } else {
              id(zone_duration) = id(zone_1_run_duration).state * 60;
            }
            id(script2_3_queue_zone).execute();
          }
          if (id(nachio_z2_6_sat).state && !id(nachio_z2_disable).state) {
            id(zone_index) = 1;
            if (id(nachio_z2_adjust_duration).state) {
              id(zone_duration) = (id(zone_2_run_duration).state + id(run_duration_adjustment).state)* 60;
            } else {
              id(zone_duration) = id(zone_2_run_duration).state * 60;
            }
            id(script2_3_queue_zone).execute();
          }
          if (id(nachio_z3_6_sat).state && !id(nachio_z2_disable).state) {
            id(zone_index) = 2;
            if (id(nachio_z3_adjust_duration).state) {
              id(zone_duration) = (id(zone_3_run_duration).state + id(run_duration_adjustment).state)* 60;
            } else {
              id(zone_duration) = id(zone_3_run_duration).state * 60;
            }
            id(script2_3_queue_zone).execute();
          }
          if (id(nachio_z4_6_sat).state && !id(nachio_z4_disable).state) {
            id(zone_index) = 3;
            if (id(nachio_z4_adjust_duration).state) {
              id(zone_duration) = (id(zone_4_run_duration).state + id(run_duration_adjustment).state)* 60;
            } else {
              id(zone_duration) = id(zone_4_run_duration).state * 60;
            }
            id(script2_3_queue_zone).execute();
          }
          if (id(nachio_z5_6_sat).state && !id(nachio_z5_disable).state) {
            id(zone_index) = 4;
            if (id(nachio_z5_adjust_duration).state) {
              id(zone_duration) = (id(zone_5_run_duration).state + id(run_duration_adjustment).state)* 60;
            } else {
              id(zone_duration) = id(zone_5_run_duration).state * 60;
            }
            id(script2_3_queue_zone).execute();
          }
          if (id(nachio_z6_6_sat).state && !id(nachio_z6_disable).state) {
            id(zone_index) = 5;
            if (id(nachio_z6_adjust_duration).state) {
              id(zone_duration) = (id(zone_6_run_duration).state + id(run_duration_adjustment).state)* 60;
            } else {
              id(zone_duration) = id(zone_6_run_duration).state * 60;
            }
            id(script2_3_queue_zone).execute();
          }
          if (id(nachio_z7_6_sat).state && !id(nachio_z7_disable).state) {
            id(zone_index) = 6;
            if (id(nachio_z7_adjust_duration).state) {
              id(zone_duration) = (id(zone_7_run_duration).state + id(run_duration_adjustment).state)* 60;
            } else {
              id(zone_duration) = id(zone_7_run_duration).state * 60;
            }
            id(script2_3_queue_zone).execute();
          }
          //if (id(nachio_z8_6_sat).state) {
          //  id(zone_index) = 7;
          //  id(zone_duration) = id(zone_8_run_duration).state * 60;
          //  id(script2_3_queue_zone).execute();
          //}


  # Add zone to queue
  - id: script2_3_queue_zone
    mode: queued
    then:
      - lambda: |-
          id(sprinkler_queue_size) += 1;
          // Assuming the zone runtime is stored as minutes
          float runtime_min = id(zone_duration);
          int runtime_sec = (int)(runtime_min * 60.0);
          id(sprinkler_queue_time_remaining) += runtime_sec;
      - sprinkler.queue_valve:
          id: nachio_sprinklers
          valve_number: !lambda |-
            return id(zone_index);
          run_duration: !lambda |-
            return id(zone_duration);



  # start the sprinkler queue
  - id: script3_start_queue
    then:
      #- lambda: |-
      #    auto time = id(ha_time).now();
      #    char buffer[32];
      #    snprintf(buffer, sizeof(buffer), "%04d-%02d-%02d %02d:%02d:%02d",
      #        time.year, time.month, time.day_of_month,
      #        time.hour, time.minute, time.second);
      #    id(_last_run_str) = std::string(buffer);
      - sprinkler.start_from_queue: ${id_prefix}_sprinklers
      - lambda: |-
          ESP_LOGI("main", "Sprinkler queue has begun!");


  #Used to manually run a zone from Home Assistant
  - id: start_valve_dynamic
    then:
      - lambda: |-
          id(sprinkler_queue_size) += 1;
          auto sprinkler = id(${id_prefix}_sprinklers);
          int valve_num = (int) id(valve_number_input).state;
          int duration = (int) id(var_run_duration_minutes).state * 60;
          sprinkler->start_single_valve(valve_num, duration);





#### BEGIN Rain Skip related scripts ######

  - id: check_rain_skip_conditions
    mode: queued
    then:
      - lambda: |-
          float forecast = id(forecasted_rain).state;
          float actual = id(actual_rain).state;

          if (forecast >= 0.2 || actual >= 0.2) {
            id(${id_prefix}_rain_skip_lock).turn_on();

            if (actual >= 0.8) {
              id(rain_skip_duration_48h).execute();
            } else {
              id(rain_skip_duration_24h).execute();
            }
          }

  - id: rain_skip_duration_24h
    then:
      - delay: 24h
      - switch.turn_off: ${id_prefix}_rain_skip_lock

  - id: rain_skip_duration_48h
    then:
      - delay: 48h
      - switch.turn_off: ${id_prefix}_rain_skip_lock


##### END Rain Skip related scripts ######




###############################################
# Text sensors with general information.
###############################################
text_sensor:
#  - platform: version # Expose ESPHome version as sensor.
#    name: $name_of_device ESPHome Version
#    hide_timestamp: false
  - platform: wifi_info
    ip_address:
      name: "${name_of_device}_IP"
    ssid:
      name: "${name_of_device}_SSID"
    bssid:
      name: "${name_of_device}_BSSID"



  - platform: template
    name: "Uptime (Formatted)"
    lambda: |-
        int seconds = (int) id(uptime_seconds).state;
        int days = seconds / 86400;
        int hours = (seconds % 86400) / 3600;
        int minutes = (seconds % 3600) / 60;
        return to_string(days) + "d " + to_string(hours) + "h " + to_string(minutes) + "m";
    entity_category: "diagnostic"


  - platform: template
    name: "Active Sprinkler Zone"
    id: active_valve_name
    update_interval: 10s
    lambda: |-
      if (id(nachio_valve01).state) return std::string("Front Lawn (z1)");
      if (id(nachio_valve02).state) return std::string("Front Patio (z2)");
      if (id(nachio_valve03).state) return std::string("Back Lawn1 (z3)");
      if (id(nachio_valve04).state) return std::string("Back Lawn2 (z4)");
      if (id(nachio_valve05).state) return std::string("Roses Side Yard (z5)");
      if (id(nachio_valve06).state) return std::string("Garden Rear Fence (z6)");
      if (id(nachio_valve07).state) return std::string("Left Driveway (z7)");
      if (id(nachio_valve08).state) return std::string("Unassigned (z8)");

      return std::string("None");



  - platform: template
    id: time_remaining
    name: "Current Zone Time Remaining"
    update_interval: 10s
    icon: "mdi:timer-sand"
    lambda: |-
      int remaining = id(nachio_sprinklers).time_remaining_active_valve().value_or(0);

      int days = remaining / 86400;
      int hours = (remaining % 86400) / 3600;
      int minutes = (remaining % 3600) / 60;
      int seconds = remaining % 60;

      char buffer[32];
      snprintf(buffer, sizeof(buffer), "%s%s%dm %ds",
        (days > 0 ? (std::to_string(days) + "d ").c_str() : ""),
        (hours > 0 ? (std::to_string(hours) + "h ").c_str() : ""),
        minutes, seconds
      );
      return std::string(buffer);