ESPHome Config File
Referring page: Custom Sprinkler Controller
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.
This device is setup to run independently from Home Assistant. 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
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:
- text_sensor.template.publish:
id: controller_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: !secret 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
ssid: !secret wifi_ssid
password: !secret wifi_password
# Enable fallback hotspot (captive portal) in case wifi connection fails
ap:
ssid: "Nachio Fallback Hotspot"
password: !secret wifi_password
captive_portal:
# Enable Web server.
web_server:
port: 80
# 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
###############################################
# MAIN SPRINKLER CONTROLLER
###############################################
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
#this seems to cause error when activated
#on_turn_on:
# then:
# - text_sensor.template.publish:
# id: controller_status
# state: "Running"
#on_turn_off:
# then:
# - text_sensor.template.publish:
# id: controller_status
# state: "Idle"
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
id: sprinkler_pause
name: "Pause"
icon: "mdi:pause"
on_press:
then:
- text_sensor.template.publish:
id: controller_status
state: "Paused"
- sprinkler.pause: ${id_prefix}_sprinklers
- platform: template
id: sprinkler_resume
name: "Resume"
icon: "mdi:play"
on_press:
then:
- text_sensor.template.publish:
id: controller_status
state: "Running"
- sprinkler.resume: ${id_prefix}_sprinklers
- platform: template
id: sprinkler_stop
name: "Stop"
icon: "mdi:stop"
on_press:
then:
- text_sensor.template.publish:
id: controller_status
state: "Idle"
- sprinkler.shutdown: ${id_prefix}_sprinklers
#########################################################
# BEGIN SWITCHES
#########################################################
# Device 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:
- text_sensor.template.publish:
id: controller_status
state: "Running"
- lambda: |-
id(valve_1_active) = true;
on_turn_off:
then:
- text_sensor.template.publish:
id: controller_status
state: "Idle"
- 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:
- text_sensor.template.publish:
id: controller_status
state: "Running"
- lambda: |-
id(valve_2_active) = true;
on_turn_off:
then:
- text_sensor.template.publish:
id: controller_status
state: "Idle"
- 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:
- text_sensor.template.publish:
id: controller_status
state: "Running"
- lambda: |-
id(valve_3_active) = true;
on_turn_off:
then:
- text_sensor.template.publish:
id: controller_status
state: "Idle"
- 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:
- text_sensor.template.publish:
id: controller_status
state: "Running"
- lambda: |-
id(valve_4_active) = true;
on_turn_off:
then:
- text_sensor.template.publish:
id: controller_status
state: "Idle"
- 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:
- text_sensor.template.publish:
id: controller_status
state: "Running"
- lambda: |-
id(valve_5_active) = true;
on_turn_off:
then:
- text_sensor.template.publish:
id: controller_status
state: "Idle"
- 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:
- text_sensor.template.publish:
id: controller_status
state: "Running"
- lambda: |-
id(valve_6_active) = true;
on_turn_off:
then:
- text_sensor.template.publish:
id: controller_status
state: "Idle"
- 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:
- text_sensor.template.publish:
id: controller_status
state: "Running"
- lambda: |-
id(valve_7_active) = true;
on_turn_off:
then:
- text_sensor.template.publish:
id: controller_status
state: "Idle"
- 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:
- text_sensor.template.publish:
id: controller_status
state: "Running"
- lambda: |-
id(valve_8_active) = true;
on_turn_off:
then:
- text_sensor.template.publish:
id: controller_status
state: "Idle"
- 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
#########################################################
# Begin day of week switches per zone - Used to indicate the days of the week a zone should be run (7zones * 7days = 49 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
######################################
# BEGIN SWITCHES TO DISABLE ZONES - USED TO TEMPORARILY DISABLE A ZONE
######################################
- 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
- platform: restart
name: "Restart"
#RAIN SKIP Switch
# USE STANDBY IN PLACE OF RAIN_SKIP?
- platform: template
name: "Rain Skip Lock"
id: ${id_prefix}_rain_skip_lock
restore_mode: RESTORE_DEFAULT_OFF
optimistic: true
on_turn_on:
then:
- text_sensor.template.publish:
id: controller_status
state: "Rain Skip"
on_turn_off:
then:
- text_sensor.template.publish:
id: controller_status
state: "Idle"
#########################################################
# BEGIN DATETIMES - USED TO SPECIFY THE DAILY START TIMES
#########################################################
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 roundf(weighted_avg * 100.0)/100.0;
# return weighted_avg; #orig line 2025-0712
- 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;
#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 #####
###############################################################
# BEGIN SCRIPTS
###############################################################
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
id: controller_status
name: ${id_prefix} Status
update_interval: never
icon: "mdi:information-variant"
- platform: template
name: "Active Zone"
id: active_valve_name
update_interval: 10s
lambda: |-
if (id(nachio_valve01).state) return std::string("Front Lawn");
if (id(nachio_valve02).state) return std::string("Front Patio");
if (id(nachio_valve03).state) return std::string("Back Lawn1");
if (id(nachio_valve04).state) return std::string("Back Lawn2");
if (id(nachio_valve05).state) return std::string("Roses & Sideyard");
if (id(nachio_valve06).state) return std::string("Garden & Dahlias");
if (id(nachio_valve07).state) return std::string("Left Driveway");
if (id(nachio_valve08).state) return std::string("Unassigned");
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);