Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- substitutions:
- friendly_name: Retic
- area_1_name: "North Lawn"
- area_2_name: "West Garden"
- area_3_name: "South Lawn"
- area_4_name: "South Bush"
- area_5_name: "North Bush"
- area_6_name: "Caves Road"
- count_of_valves: "6"
- winter_water_time: "2"
- water_tank_radius_m: "1.17"
- usable_water_head: "1.57" # Bottom of lower float switch activation to full water level is 1.57m. Measured 2/11/23.
- us_sensor_full_level: "0.461" # Value used to align 100% with full switch. Distance reported by U/S sensor when the water_tank_full_float_switch (float switch) sensor = true.
- bore_flow_rate_lpm: "14"
- retic_flow_rate_lpm: "40" # 48 estimated by refill time on full run. 40 calculated on new lawn only. Is a fitting blown on caves road or bund?
- phi: "3.14"
- # GPIO0, 2, 5, 12 and 15 are strapping pins.
- # GPIO0, 2 and 12 are critical as they can interfere with flashing.
- globals:
- - id: is_summertime
- type: bool
- initial_value: "false"
- - id: is_wintertime_water_day
- type: bool
- initial_value: "false"
- - id: excess_solar
- type: int
- initial_value: "0"
- - id: weather_factor
- type: float
- initial_value: "1"
- - id: total_watering_duration
- type: float
- initial_value: "0"
- - id: next_watering_timestamp
- type: int64_t
- - id: last_watered
- type: ESPTime
- restore_value: true
- - id: tank_full_after_sunset
- type: bool
- restore_value: true
- initial_value: "false"
- - id: tank_was_empty
- type: bool
- restore_value: true
- initial_value: "false"
- - id: odd_day_number
- type: bool
- - id: calculated_water_in_tank
- type: int
- initial_value: "0"
- restore_value: true
- esphome:
- name: esp-sprinkler-controller
- friendly_name: '${friendly_name}'
- esp32:
- board: esp32dev
- framework:
- type: arduino
- preferences:
- flash_write_interval: 10min
- # Enable logging
- logger:
- level: DEBUG
- # Enable Home Assistant API
- api:
- encryption:
- key: "QzppePufFQNLPzutZ74eoo2szcI8EeV5NpXn9e4nha8="
- reboot_timeout: 30 min
- ota:
- password: "edc41232fc16aba976d8bd2549293b24"
- wifi:
- networks:
- - ssid: !secret wifi_ssid
- password: !secret wifi_password
- - ssid: !secret ssid2
- password: !secret pw2
- - ssid: !secret ssid3
- password: !secret pw3
- id: wifi_id
- # Enable fallback hotspot (captive portal) in case wifi connection fails
- ap:
- ssid: "Esp-Sprinkler-Controller"
- password: "eAFllu9HjGvM"
- reboot_timeout: 30 min
- captive_portal:
- sprinkler:
- - id: lawn_sprinkler_ctrlr
- main_switch: "Main Switch"
- auto_advance_switch: "Auto Advance"
- reverse_switch: "Reverse order"
- standby_switch: "System standby"
- next_prev_ignore_disabled: true
- pump_stop_valve_delay: 1s
- pump_start_pump_delay: 1s
- valve_overlap: 1s
- valves:
- - valve_switch: "${area_2_name}"
- enable_switch:
- name: "Enable ${area_2_name}"
- id: valve_2_enable
- pump_switch_id: sprinkler_pump_sw
- run_duration_number:
- name: "${area_2_name} run"
- unit_of_measurement: min
- initial_value: 30
- step: 1
- id: valve_2_duration
- valve_switch_id: valve_sw2
- - valve_switch: "${area_3_name}"
- enable_switch:
- name: "Enable ${area_3_name}"
- id: valve_3_enable
- pump_switch_id: sprinkler_pump_sw
- run_duration_number:
- name: "${area_3_name} run"
- unit_of_measurement: min
- initial_value: 30
- step: 1
- id: valve_3_duration
- valve_switch_id: valve_sw3
- - valve_switch: "${area_1_name}"
- enable_switch:
- name: "Enable ${area_1_name}"
- id: valve_1_enable
- pump_switch_id: sprinkler_pump_sw
- run_duration_number:
- name: "${area_1_name} run"
- unit_of_measurement: min
- initial_value: 30
- step: 1
- id: valve_1_duration
- valve_switch_id: valve_sw1
- - valve_switch: "${area_5_name}"
- enable_switch:
- name: "Enable ${area_5_name}"
- id: valve_5_enable
- pump_switch_id: sprinkler_pump_sw
- run_duration_number:
- name: "${area_5_name} run"
- unit_of_measurement: min
- initial_value: 30
- step: 1
- id: valve_5_duration
- valve_switch_id: valve_sw5
- - valve_switch: "${area_4_name}"
- enable_switch:
- name: "Enable ${area_4_name}"
- id: valve_4_enable
- pump_switch_id: sprinkler_pump_sw
- run_duration_number:
- name: "${area_4_name} run"
- unit_of_measurement: min
- initial_value: 30
- step: 1
- id: valve_4_duration
- valve_switch_id: valve_sw4
- - valve_switch: "${area_6_name}"
- enable_switch:
- name: "Enable ${area_6_name}"
- id: valve_6_enable
- pump_switch_id: sprinkler_pump_sw
- run_duration_number:
- name: "${area_6_name} run"
- unit_of_measurement: min
- initial_value: 30
- step: 1
- id: valve_6_duration
- valve_switch_id: valve_sw6
- switch:
- - platform: gpio
- inverted: true
- id: sprinkler_pump_sw
- pin: 19
- - platform: gpio
- inverted: true
- id: valve_sw1
- pin: 18
- - platform: gpio
- inverted: true
- id: valve_sw2
- pin: 17
- - platform: gpio
- inverted: true
- id: valve_sw3
- pin: 16
- - platform: gpio
- inverted: true
- id: valve_sw4
- pin: 23
- - platform: gpio
- inverted: true
- id: valve_sw5
- pin: 22
- - platform: gpio
- inverted: true
- id: valve_sw6
- pin: 21
- - platform: gpio
- inverted: true
- name: "Bore pump"
- id: bore_pump_sw8
- pin: 15 # Strapping pin, consider changing to different pin
- - platform: template
- name: "New lawn watering"
- id: new_lawn_watering
- optimistic: true
- restore_mode: RESTORE_DEFAULT_OFF
- button:
- - platform: template
- name: "Start summer cycle"
- on_press:
- then:
- - sprinkler.clear_queued_valves:
- id: lawn_sprinkler_ctrlr
- - sprinkler.start_full_cycle: lawn_sprinkler_ctrlr
- - platform: template
- name: "Start winter cycle"
- on_press:
- then:
- - script.execute: run_winter_cycle
- - platform: template
- name: "Start new lawn cycle"
- on_press:
- then:
- - script.execute: run_new_lawn_cycle
- sun:
- latitude: -33.64562625475741
- longitude: 115.18726072719008
- id: sun_id
- on_sunset:
- lambda: 'id(tank_full_after_sunset) = (id(water_tank_full).state);'
- time:
- - platform: homeassistant
- id: ha_time
- on_time:
- # - seconds: 0
- # minutes: 0
- # hours: 11, 15
- # then:
- # - if:
- # condition:
- # switch.is_on: new_lawn_watering
- # then:
- # script.execute: run_new_lawn_cycle
- # Diag debug
- - seconds: 0
- minutes: 45
- hours: 10
- then:
- - script.execute: do_watering
- script:
- - id: do_watering
- then:
- - logger.log: "**** Do watering"
- - script.execute: write_diag_log
- - if:
- condition:
- lambda: 'return (is_summertime);'
- then:
- script.execute: run_summer_cycle
- - script.execute: write_diag_log
- - if:
- condition:
- lambda: 'return (is_wintertime_water_day);'
- then:
- script.execute: run_winter_cycle
- - script.execute: write_diag_log
- - logger.log: "**** Do watering - done"
- - id: run_new_lawn_cycle
- then:
- - logger.log: "**** Run new lawn cycle"
- - sprinkler.clear_queued_valves:
- id: lawn_sprinkler_ctrlr
- - sprinkler.set_multiplier:
- id: lawn_sprinkler_ctrlr
- multiplier: !lambda 'return id(weather_factor);'
- # Valves are numbered in the order they appear in the sprinkler controller’s configuration starting at zero (0).
- - sprinkler.queue_valve:
- id: lawn_sprinkler_ctrlr
- valve_number: 3
- - sprinkler.queue_valve:
- id: lawn_sprinkler_ctrlr
- valve_number: 2
- - sprinkler.queue_valve:
- id: lawn_sprinkler_ctrlr
- valve_number: 1
- - sprinkler.start_from_queue:
- id: lawn_sprinkler_ctrlr
- - logger.log: "**** Run new lawn cycle - done"
- - id: run_summer_cycle
- then:
- - logger.log: "**** Run summer cycle"
- - sprinkler.clear_queued_valves:
- id: lawn_sprinkler_ctrlr
- - sprinkler.set_multiplier:
- id: lawn_sprinkler_ctrlr
- multiplier: !lambda 'return id(weather_factor);'
- - if:
- condition:
- lambda: 'return id(odd_day_number);'
- then:
- - sprinkler.start_full_cycle: lawn_sprinkler_ctrlr
- else:
- - script.execute: run_new_lawn_cycle
- - logger.log: "**** Run summer cycle - done"
- - id: run_winter_cycle
- then:
- - logger.log: "**** Run winter cycle"
- # Reset multiplier to 1 so we get required winter_water_time duration
- - sprinkler.set_multiplier:
- id: lawn_sprinkler_ctrlr
- multiplier: 1.0
- # Clear the queue then queue winter_water_time for each
- - sprinkler.clear_queued_valves:
- id: lawn_sprinkler_ctrlr
- - sprinkler.queue_valve:
- # Valves are numbered in the order they appear in the sprinkler controller’s configuration starting at zero (0)
- id: lawn_sprinkler_ctrlr
- valve_number: 0
- run_duration: ${winter_water_time}min
- - sprinkler.queue_valve:
- id: lawn_sprinkler_ctrlr
- valve_number: 1
- run_duration: ${winter_water_time}min
- - sprinkler.queue_valve:
- id: lawn_sprinkler_ctrlr
- valve_number: 2
- run_duration: ${winter_water_time}min
- - sprinkler.queue_valve:
- id: lawn_sprinkler_ctrlr
- valve_number: 3
- run_duration: ${winter_water_time}min
- - sprinkler.queue_valve:
- id: lawn_sprinkler_ctrlr
- valve_number: 4
- run_duration: ${winter_water_time}min
- - sprinkler.queue_valve:
- id: lawn_sprinkler_ctrlr
- valve_number: 5
- run_duration: ${winter_water_time}min
- - sprinkler.start_from_queue:
- id: lawn_sprinkler_ctrlr
- - logger.log: "**** Run winter cycle - done"
- - id: write_diag_log
- then:
- - logger.log: "**** Diagnostic log start ********************************************************************"
- - logger.log:
- format: "is_summertime %d is_wintertime_water_day %d tank_full_after_sunset %d tank_was_empty %d odd_day_number %d"
- args: [ 'id(is_summertime)', 'id(is_wintertime_water_day)', 'id(tank_full_after_sunset)', 'id(tank_was_empty)', 'id(odd_day_number)' ]
- # - logger.log:
- # format: "excess_solar %x weather_factor %f total_watering_duration %f next_watering_timestamp %s last_watered %s calculated_water_in_tank %x"
- # args: [ 'id(excess_solar)', id(weather_factor), id(total_watering_duration), id(next_watering_timestamp), id(last_watered), id(calculated_water_in_tank) ]
- - logger.log: "**** Diagnostic log end **********************************************************************"
- binary_sensor:
- - platform: gpio
- name: "Water tank full float switch" # Diagnostic
- pin:
- number: 14
- mode:
- input: true
- pullup: true
- id: water_tank_full_float_switch
- publish_initial_state: true
- filters:
- - delayed_on_off: 3s
- - platform: template
- name: "Winter water day" # Diagnostic
- lambda: 'return id(is_wintertime_water_day);'
- - platform: template
- name: "Water tank full"
- id: water_tank_full # Tank full as measured by either the U/S sensor or the float switch
- lambda: 'return (id(water_tank_full_float_switch).state or id(ultrasonic_measure).state <= ${us_sensor_full_level});'
- on_state: # This is a "fail safe" in case the U/S sensor fails to clear the tank_was_empty state.
- lambda: 'if (x) {id(tank_was_empty) = false;}'
- - platform: gpio
- name: "Water tank empty"
- id: water_tank_empty_float_switch
- pin:
- number: 32
- mode:
- input: true
- pullup: true
- publish_initial_state: true
- filters:
- - invert:
- - delayed_on_off: 3s
- on_state:
- lambda: |-
- if ((x) and (id(lawn_sprinkler_ctrlr).active_valve().has_value())) {
- id(lawn_sprinkler_ctrlr).shutdown();
- }
- if (x) {id(tank_was_empty) = true;}
- sensor:
- - platform: wifi_signal
- name: "WiFi Signal Sensor"
- update_interval: 60s
- - platform: ultrasonic
- trigger_pin: 26
- echo_pin: 27
- name: "Ultrasonic Sensor"
- id: ultrasonic_measure
- timeout: 4.0 m
- update_interval: 60s
- accuracy_decimals: 3
- filters:
- - quantile:
- window_size: 7
- send_every: 4
- send_first_at: 1
- quantile: 0.9
- - lambda: !lambda |-
- if (x > 1.1) return {};
- return x;
- on_value_range:
- - below: 1.0
- then:
- - lambda: 'id(tank_was_empty) = false;'
- - platform: template
- name: "Water tank capacity"
- id: tank_capacity_l
- lambda: 'return ${phi} * ${water_tank_radius_m} * ${water_tank_radius_m} * (${usable_water_head}) * 1000;'
- unit_of_measurement: l
- accuracy_decimals: 0
- update_interval: 24h
- - platform: homeassistant
- name: Excess Solar
- entity_id: sensor.excess_solar
- id: ha_excess_solar
- - platform: homeassistant
- name: Weather factor
- entity_id: sensor.weather_factor
- id: ha_weather_factor
- accuracy_decimals: 2
- - platform: template
- name: Multiplier
- lambda: 'return id(lawn_sprinkler_ctrlr).multiplier();'
- - platform: template
- name: "Calculated water in tank"
- unit_of_measurement: "L"
- lambda: 'return id(calculated_water_in_tank);'
- text_sensor:
- - platform: wifi_info
- ip_address:
- name: '${friendly_name} - IP Address'
- icon: mdi:wifi
- ssid:
- name: '${friendly_name} - Connected SSID'
- icon: mdi:wifi-strength-2
- - platform: template
- name: "Calculated start time"
- lambda: |-
- char str[25];
- time_t currTime = id(next_watering_timestamp);
- strftime(str, sizeof(str), "%H:%M:%S", localtime(&currTime));
- return (std::string) str;
- interval:
- - interval: 1min
- then:
- - lambda: |-
- if(id(bore_pump_sw8).state) {id(calculated_water_in_tank) += ${bore_flow_rate_lpm};}
- if(id(sprinkler_pump_sw).state) {id(calculated_water_in_tank) -= ${retic_flow_rate_lpm};}
- if(id(water_tank_empty_float_switch).state) {id(calculated_water_in_tank) = 0;}
- if(id(water_tank_full).state) {id(calculated_water_in_tank) = id(tank_capacity_l).state;}
- - lambda: 'if(id(tank_was_empty)) {id(bore_pump_sw8).turn_on();}'
- - if:
- # get what we need from HA when we are connected. Reasonable defaults are set on global definitions.
- condition:
- and:
- - api.connected
- - wifi.connected
- then:
- - lambda: |-
- if(isfinite(id(ha_excess_solar).state)) {id(excess_solar) = id(ha_excess_solar).state;}
- if(isfinite(id(ha_weather_factor).state)) {id(weather_factor) = id(ha_weather_factor).state;}
- - if:
- # Determine winter or summer
- condition:
- time.has_time:
- then:
- # Watering restrictions for bores in Busselton.
- # Every day from 1 Sept to 31 May, once per day, before 9am or after 6pm. Summer = months 9, 10, 11, 12, 1, 2, 3, 4, 5.
- # Pressure testing, once a week, for 2 minutes per station. Pref before 9am or after 6pm. Winter = months 6, 7, 8.
- - lambda: |-
- auto time = id(ha_time).now();
- id(is_summertime) = bool(time.month > 8 or time.month < 6);
- id(is_wintertime_water_day) = ((time.month > 5 and time.month < 9) and (time.day_of_week = 3));
- id(odd_day_number) = (time.day_of_year % 2 != 0);
- - script.execute: write_diag_log
- # Calculate watering duration allowing for weather_factor and calculate start time to end at sunrise
- - lambda: |-
- id(total_watering_duration) = 0;
- if (id(is_wintertime_water_day)) {id(total_watering_duration) = $winter_water_time * $count_of_valves;}
- if (id(is_summertime)) {
- if (id(odd_day_number)) {
- if (id(valve_1_enable).state) { id(total_watering_duration) += id(valve_1_duration).state; }
- if (id(valve_2_enable).state) { id(total_watering_duration) += id(valve_2_duration).state; }
- if (id(valve_3_enable).state) { id(total_watering_duration) += id(valve_3_duration).state; }
- if (id(valve_4_enable).state) { id(total_watering_duration) += id(valve_4_duration).state; }
- if (id(valve_5_enable).state) { id(total_watering_duration) += id(valve_5_duration).state; }
- if (id(valve_6_enable).state) { id(total_watering_duration) += id(valve_6_duration).state; }
- } else {
- if (id(valve_1_enable).state) { id(total_watering_duration) += id(valve_1_duration).state; }
- if (id(valve_3_enable).state) { id(total_watering_duration) += id(valve_3_duration).state; }
- if (id(valve_5_enable).state) { id(total_watering_duration) += id(valve_5_duration).state; }
- }
- id(total_watering_duration) = id(total_watering_duration) * id(weather_factor);
- }
- auto total_watering_duration_sec = id(total_watering_duration) * 60;
- auto next_sunrise = id(sun_id).sunrise(-0.833).value();
- int64_t sunrise_timestamp = next_sunrise.timestamp;
- id(next_watering_timestamp) = sunrise_timestamp - total_watering_duration_sec;
- - if:
- # Determine if it's time to start watering
- condition:
- time.has_time:
- then:
- - if:
- condition:
- lambda: |-
- auto now_time = id(ha_time).now();
- return (((id(next_watering_timestamp) <= now_time.timestamp) and id(last_watered).day_of_year < now_time.day_of_year)
- and (id(is_wintertime_water_day) or id(is_summertime))
- and (id(weather_factor) > 0));
- then:
- - lambda: 'id(last_watered) = id(ha_time).now();'
- - script.execute: write_diag_log
- - script.execute: do_watering
- - interval: 10min
- then:
- # Bore water tank refill pump control
- if:
- condition:
- sun.is_above_horizon
- then:
- # Daytime - we might have solar power
- lambda: |-
- if ((id(excess_solar) >= 500) or id(tank_was_empty)) {
- if (id(water_tank_full).state == false) {
- id(bore_pump_sw8).turn_on();
- } else {
- id(bore_pump_sw8).turn_off();
- }
- // # Run the pump regardless of solar if water getting too low
- // #if ((id(water_tank_full_float_switch).state == false) and (id(tank_water_percent).state < 33)) {
- // # id(bore_pump_sw8).turn_on();
- // #} else {
- // # id(bore_pump_sw8).turn_off();
- // # }
- }
- else:
- # Nighttime - top up if we couldn't do it during the day
- lambda: |-
- if ((id(tank_full_after_sunset) == false) and (id(water_tank_full).state == false)) {
- id(bore_pump_sw8).turn_on();
- }
- if (id(water_tank_full).state) {
- id(bore_pump_sw8).turn_off();
- id(tank_full_after_sunset) = true;
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement