HASS sprinkler

I do wish the configuration files for something could all be wrapped up in one. i.e. Boolean inputs, sliders, mqtt configs, etc. So you could literally package it up and share it. I guess that is more in the component category though…

That’s what custom components are for :wink:

Looks awesome! Do you have source for how you made the frontend look like this? Thanks for sharing

I’m on holiday and became grandfather a week ago so my response is a bit behind…
It is all based on MQTT… The status is just a calculation from timenow and when the switch was activated just a little template sensor.

The last run is when the switch was turned on last time however this is not always the right status as when HASS is restarted it is set to the restart tome so needs a bit of work…

Still have one week holiday but after that I will post some of my yaml templates…

How did you get the run schedule in there? Is that all yaml?

Yes the schedule is a input select and based on that the template sensor next run is determined. On this sensor the automation is triggered

OK below here is some of the yaml and scripts I use for this… now as I have my config split up in multiple files you cannot cut & past but you have to make sure it all goes in the right place in your config… and check the yaml as at some places there will be too many spaces :slight_smile:

Hope it helps to understand how I have done a few things… might be that I forgot something so let me know if it is not working.

Btw as this is very script heavy with template sensors I planning to move parts of it to the apps.

  - platform: template
sensors:      
  starttime_valve_1:
    friendly_name: 'Start Time'
    entity_id: 
      - input_slider.valve_1_hour
      - input_slider.valve_1_minutes
    value_template: '{% if states.input_slider.valve_1_hour.state|length < 4%}0{% endif %}{{states.input_slider.valve_1_hour.state | int }}:{% if states.input_slider.valve_1_minutes.state|length < 4%}0{% endif %}{{ states.input_slider.valve_1_minutes.state | int }}'

  duration_valve_1:
    friendly_name: 'Duration'
    entity_id: input_slider.valve_1_duration
    value_template: '00:{% if states.input_slider.valve_1_duration.state|length < 4%}0{% endif %}{{ states.input_slider.valve_1_duration.state | int }}'    
    
  status_valve_1:
    friendly_name: 'Status'
    value_template: "{% if states.switch.valve_1.state == 'on'%}Opened {{((as_timestamp(now) - as_timestamp(states.switch.valve_1.last_changed)) / 60) | round(0) }} minutes ago{% else %}Closed{% endif %}"
    
  last_run_valve_1:
     friendly_name: 'Last Run'
     entity_id: switch.valve_1
     value_template: "{{states.switch.valve_1.last_changed.strftime('%a')}}, {{states.switch.valve_1.last_changed.strftime('%d-%m-%Y')}}"

  schedule_valve_1:
    friendly_name: 'Schedule'
    entity_id: input_select.valve_1_schedule
    value_template: "{{ states.input_select.valve_1_schedule.state }}"
     
  next_run_valve_1:
     friendly_name: 'Next Run'
     entity_id:
       - input_slider.valve_1_hour
       - input_slider.valve_1_minutes
       - input_select.valve_1_schedule
       - switch.valve_1
     value_template: >
        {# runDay set to 9 = Not Set #}
        {% set runDay = 9 |int %}
        {# Set todays day #}
        {% set currentDay = now.weekday() |int %}
        {%- macro monthu() -%}
            {%- if currentDay == 0 -%}
                {{0}}  
            {%- elif  currentDay <= 3 -%}
                {{3}}
            {%- else -%}	
                {{0}}
            {%- endif -%}
        {%- endmacro -%}
        {%- macro monwedfrisun() -%}
            {%- if currentDay == 0 -%}
                {{0}}  
            {%- elif  currentDay <= 2 -%}
                {{2}}
            {%- elif  currentDay <= 4 -%}
                {{4}}
            {%- elif  currentDay <= 6 -%}
                {{6}}	
            {%- endif -%}
        {%- endmacro -%}
        {# set configured runDay from input_select #}
        {% if states.input_select.valve_1_schedule.state == "Monday" %}
            {% set runDay = 0 |int %}
        {% elif states.input_select.valve_1_schedule.state == "Tuesday" %}
            {% set runDay = 1 |int %}
        {% elif states.input_select.valve_1_schedule.state == "Wednesday" %}
            {% set runDay = 2 |int %}
        {% elif states.input_select.valve_1_schedule.state == "Thursday" %}
            {% set runDay = 3 |int %}
        {% elif states.input_select.valve_1_schedule.state == "Friday" %}
            {% set runDay = 4 |int %}
        {% elif states.input_select.valve_1_schedule.state == "Saturday" %}
            {% set runDay = 5 |int %}
        {% elif states.input_select.valve_1_schedule.state == "Sunday" %}
            {% set runDay = 6 |int %}
        {% elif states.input_select.valve_1_schedule.state == "Mon/Thu" %}
            {% set runDay = monthu() |int -%}
        {% elif states.input_select.valve_1_schedule.state == "Mon/Wed/Fri/Sun" %}
            {% set runDay = monwedfrisun() |int %}  
        {% elif states.input_select.valve_1_schedule.state == "Every Day" %}
            {% set runDay = currentDay |int %}     
        {% endif %} 
        {# determine the next runday #}
        {% if runDay == 9 %}
            {# schedule not active #}
            {% set runDate = "Not set" %}
        {% else %}
            {# schedule is active so determine next run #}
            {# first check if runDay = today #}
            {% if currentDay == runDay %}
                {# are we passed the scheduled time? #}
                {% if states.sensor.starttime_valve_1.state < now.time().strftime("%H:%M") %}
                    {# than we need to fake tomorrow #}
                    {% set currentDay = (currentDay + 1) |int %}
                    {# and calculate new runDay in case we have a list of multiple run days in in a week #}
                    {% if states.input_select.valve_1_schedule.state == "Mon/Thu" %}
                        {% set runDay = monthu() |int -%}
                    {% elif states.input_select.valve_1_schedule.state == "Mon/Wed/Fri/Sun" %}
                        {% set runDay = monwedfrisun() |int %} 
                    {% elif states.input_select.valve_1_schedule.state == "Every Day" %}
                        {% set runDay = currentDay |int %} 
                    {% endif %}
                {% endif %}
            {% endif %}
            {# Now we can determine next runDate base on now.weekday() and not currentDay as that can be set to fake tomorrow #}	
            {% if currentDay <= runDay %}
                {% set Days = runDay - now.weekday() |int %}
            {% else %}
                {% set Days = runDay + 7 - now.weekday() |int %}
            {% endif %}
            {% set runDate = ((as_timestamp(now) + (86400 * Days)) | timestamp_local) %}
            {# we also want to show the weekday of the next date #}
            {% set weekdayList = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"] %}
            {% if now.weekday()+ Days < 7 %}
                {% set weekday = weekdayList[(now.weekday()+ Days)] %}
            {% else %}
                {% set weekday = weekdayList[(now.weekday()+ Days)- 7] %}
            {% endif %} 
        {% endif %}
        {# all done #}
        {# set states.sensor.next_run_date_valve_1.state = runDate #}
        {% if runDay == 9 %}
            {# schedule not active #}
            {{runDate}}
        {% else %}  
            {{weekday}}, {{runDate[8:10]}}-{{runDate[5:7]}}-{{runDate[0:4]}} at {{states.sensor.starttime_valve_1.state}}
        {% endif %}

  #### input_sliders
  valve_1_hour:
	name: Hour
	icon: mdi:clock
	initial: 9.00
	min: 0
	max: 23
	step: 1
  
  valve_1_minutes:
	name: Minutes
	icon: mdi:clock
	initial: 0.00
	min: 0
	max: 55
	step: 5
  
  valve_1_duration:
	name: Duration
	icon: mdi:timer-sand
	initial: 30.00
	min: 0
	max: 55
	step: 5 
	
#### input_select
valve_1_schedule:
  name: Schedule
  options:
	- None - Close Valve
	- Run Now
	- Every Day
	- Mon/Wed/Fri/Sun
	- Mon/Thu
	- Monday
	- Tuesday
	- Wednesday
	- Thursday
	- Friday
	- Saturday
	- Sunday
  initial: None - Close Valve
  icon: mdi:calendar  
	
#### groups
  valve_1_status:
	name: Valve 1
	icon: mdi:water-pump
	entities:
	  - sensor.status_valve_1
	  - sensor.last_run_valve_1
	  - sensor.next_run_valve_1
	  - sensor.schedule_valve_1
	  - sensor.starttime_valve_1
	  - sensor.duration_valve_1
	  - group.valve_1_settings
	  
  valve_1_settings:
	name: Settings
	icon: mdi:settings
	entities:  
	  - input_select.valve_1_schedule
	  - input_slider.valve_1_hour
	  - input_slider.valve_1_minutes
	  - input_slider.valve_1_duration	

##### Switch
  - platform: mqtt
	name: "Valve 1"
	state_topic: "5ccf7f18d3c/valve/1/state"
	command_topic: "5ccf7f18d3c/valve/1/cmd"
	payload_on: "1"
	payload_off: "0"
	optimistic: false
	qos: 0
	retain: true
	
##### Automation
alias: "Turn on valve based on schedule"
trigger:
  platform: template
  value_template: '{{states.sensor.starttime_valve_1.state == now.time().strftime("%H:%M")}}'
	 
condition:
  - condition: state
	entity_id: switch.valve_1
	state: 'off'
	
  - condition: template
	value_template: "{{states.sensor.next_run_valve_1.state[5:15] == now.date().strftime('%d-%m-%Y')}}"
	  
action:
  - service: script.turn_on
	entity_id: script.open_valve_1

#####################	
alias: "Turn on valve"
trigger:
  - platform: state
	entity_id: input_select.valve_1_schedule
	state: "Run Now"

action:
  - service: script.turn_on
	entity_id: script.open_valve_1	

################
alias: "Turn off valve"
trigger:
  - platform: state
	entity_id: input_select.valve_1_schedule
	state: "None - Close Valve"

action:
  - service: script.turn_on
	entity_id: script.close_valve_1	
	
#### scripts
  open_valve_1:
	alias: "Open Valve 1 for duration below"
	sequence:
	  - service: script.turn_off
		data:
		   entity_id: script.close_valve_1_delayed

	  - service: switch.turn_on
		data:
		  entity_id: switch.valve_1

	  - service: script.turn_on
		data:
		  entity_id: script.close_valve_1_delayed
		  
##############
  close_valve_1_delayed:
	alias: "Close Valve 1 after duration"
	sequence:
	  - delay: '00:{% if states.input_slider.valve_1_duration.state|length < 4%}0{% endif %}{{ states.input_slider.valve_1_duration.state | int }}'
 
	  - service: switch.turn_off
		data:
		  entity_id: switch.valve_1	

################		  
  close_valve_1:
	alias: "Close Valve 1 immediately"
	sequence:
	  - service: script.turn_off
		data:
		   entity_id: script.close_valve_1_delayed
		   
	  - service: switch.turn_off
		data:
		  entity_id: switch.valve_1
4 Likes

and some customize yaml as well

 #### Sprinkler
 sensor.starttime_valve_1:
   icon: mdi:clock
 sensor.duration_valve_1:
   icon: mdi:timer-sand
 sensor.status_valve_1:
   icon: mdi:water-pump
 sensor.last_run_valve_1:
   icon: mdi:history   
 sensor.schedule_valve_1:
   icon: mdi:calendar
 sensor.next_run_valve_1:
   icon: mdi:calendar-clock
1 Like

this is how it should look like

One of the key opportunities automating the sprinkler brings is not sprinkling when there is rain. Generally the current plug ins provide data for looking forward (so for example expected rain today). But as we all know this is often off. So best would be to look back two days and forward 1 day. And only trigger sprinkler when rain < x.

Did you guys find a solution for this?

You are absolutely right as I’m been monitoring forecast against actual rain in the last months and came to the conclusion that this is not usable.
The heart of the sprinkler is an ESP8266 that opens the valves via relays and I will build another ESP8266 with moist sensors for each valve (http://www.banggood.com/Soil-Hygrometer-Humidity-Detection-Module-Moisture-Sensor-For-Arduino-p-79227.html).

That seems to be the best solution for the moment… now as we are going into Autumn I probably save that for the dark winter evenings to build :wink:

Btw you can also add a rain sensor but that doesn’t tell you how much rain is falling… so that doesn’t help

Nice work, i made it work in my environment, i had to change the function call now to now() in all value_template entries.

small question if i may, is the settings panel hidden or you can see it in the main window?

Wow, this is some pretty serious stuff. Very cool! This is way beyond what I had thought was possible and I’d like to try and implement what you’ve done. I have an Electrodragon 2-relay IOT board that drives two water valves and has two inputs; one I’m using for the rain sensor mounted on the roof (0/1). The device is controlled via MQTT like yours. I also have my configuration split with ‘scripts’ and ‘auto’ folders.

Is there any way you could share your files on GitHub as others have done? I’m planning to do they same once I have something useful and have implemented the secrets file.

Good to hear that you got it working and indeed after 29 you need to change the function into now().

Yes it is hidden normally only when you click on settings you see it. It is not perfect or very intuitive in the way HASS works at the moment as I would like to see Settings as a link (blue and underlined) … but he maybe in the future :slight_smile:

I did it this way as otherwise the GUI is very full with stuff you only need when changing the cycle of watering.

Sorry xbmcnut I will not find the time to create a working example without all other stuff I have in there… The C&P above should help you along the way … the template sensors are the most important parts as they calculate the next run based on how the sliders and input select are set.

Please note that the function now should be replaced by now() since 29

If you run into issues I’m happy to look at them

Sorry for the dumb question but how did you hide the settings?
I tried to customize the group with hidden: true but it didnt hide all the group items.

What am i missing???

Hi Ron. Of course, I completely understand and in hindsight, it would probably be above my capabilities at the moment (I’m a hardware guy) are an overkill for my requirements. All I’m attempting to do is the following:

  • Schedule my two water valves on at 0630 everyday for 20 minutes with an input slider for the timer value
  • Check the state of my rain gauge (wet/dry) as a condition to prevent firing if wet
  • Have the timer fire based on the state change of the valve so that the timer always works, even if the valves are manually turned on. This is how it is set up with PLEG in my Vera which I’m trying to replicate in HA.

I’m learning YAML but it is a steep learning curve for a hardware guy, especially when the formatting in all of the examples on the main webpage bare no reference to the required formatting when you split your config up (which is kind of mandatory if you have more that a half-dozen devices).

This is where I’ve got to which I’m happy with (about 6hrs latter!) but is a way from where I need to be.

## Timed Garden Watering ##
timed_garden_water:
  alias: "Garden Water Timer"
  sequence:
    - service: script.turn_off
      entity_id: script.garden_water_timer
    - service: switch.turn_on
      entity_id: switch.ed01_relay1
    - service: switch.turn_on
      entity_id: switch.ed01_relay2
    - service: script.turn_on
      entity_id: script.garden_water_timer

garden_water_timer:
  alias: "Turn off garden water after x minutes"
  sequence:
    - condition: state
      entity_id: input_boolean.water_timer
      state: 'on'
    - delay:
        seconds: 5
    - service: switch.turn_off
      entity_id: switch.ed01_relay1
    - service: switch.turn_off
      entity_id: switch.ed01_relay2

Which gives me this. Any advice to help me achieve my goal would be appreciated.

Well it is not very well described in the documentation but it is done in the groups :slight_smile: nothing with hidden:true :slight_smile:

Groups for the sprinkler look like this:

  #########################
  # Sprinkler group
  #########################
  Sprinkler:
    view: yes
    entities:
      - group.valve_1_status

  valve_1_status:
    name: Valve 1
    icon: mdi:water-pump
    entities:
      - sensor.status_valve_1
      - sensor.last_run_valve_1
      - sensor.next_run_valve_1
      - sensor.schedule_valve_1
      - sensor.starttime_valve_1
      - sensor.duration_valve_1
      - group.valve_1_settings
      
  valve_1_settings:
    name: Settings
    icon: mdi:settings
    entities:  
      - input_select.valve_1_schedule
      - input_slider.valve_1_hour
      - input_slider.valve_1_minutes
      - input_slider.valve_1_duration

Please see also the group documentation on how to use view: yes together with the default_view to create your own layout in the front end. If you got that than the trick is easy as the above basically creates an entry for the Sprinkler in the top menu of the frontend that shows the view for the group valve_1_status. Now the last line in the view valve_1_status is another group group.valve_1_settings.

The effect is that the group.valve_1_settings is only shown if you click on the Settings in the view of valve_1_status.

Not sure if that is clear…

Basically you need to create an automation rule that triggers on turning a valve on. This should include a condition based on “Use Timer for Sprinklers” if that is ON/TRUE than turn_on script that turns them off after a set delay.

This is how I have done that with scripts:

  open_valve_1:
    alias: "Open Valve 1 - timer based"
    sequence:
      - service: script.turn_off
        data:
           entity_id: script.close_valve_1_delayed

      - service: switch.turn_on
        data:
          entity_id: switch.valve_1

      - service: script.turn_on
        data:
          entity_id: script.close_valve_1_delayed

  close_valve_1_delayed:
    alias: "Close Valve 1 after duration"
    sequence:
      - delay: '00:{% if states.input_slider.valve_1_duration.state|length < 4%}0{% endif %}{{ states.input_slider.valve_1_duration.state | int }}'
 
      - service: switch.turn_off
        data:
          entity_id: switch.valve_1

  close_valve_1:
    alias: "Close Valve 1 immediately"
    sequence:
      - service: script.turn_off
        data:
           entity_id: script.close_valve_1_delayed
           
      - service: switch.turn_off
        data:
          entity_id: switch.valve_1
1 Like

see also https://community.home-assistant.io/t/hide-group-attributes-if-used-as-nested-groups/3592