I’m relatively new to HASS automations. What are some tips for DRYing up HVAC automations? Or HASS automations in general. Ideally I could have one automation that covers different conditions below (time of day and day of week).
What’s preventing you from consolidating them into one automation?
Just my own lack of knowledge around how to handle multiple triggers, conditions, and actions within a single automation.
The best way to manage deduplication of code (which I believe you will start to see is really what you are talking about) is to not just lump it all into one huge monstrous hard to obtain piece. That piece of code will be called many times and when it is not finished running it might get called several more times, when you maybe want things done in a certain order. This will tend to slow down automations and cause unintended side effects which are hard to debug.
This will take you a little while to accomplish as you tweak your setup and learn how to use HA, but once you find yourself doing the same sort of steps in many different places - here is an example of how duplicated configuration (automations) can be simplified.
-
Keep each piece of code small. It is Ok to have a gazillion different scenarios, each of which might need an automation - but the key is how to handle it when they a large number of them need to do the same kind of steps.
-
This is not always possible but try to write automations that only call entities and services, or abstract objects representing the underlying hardware, not caling the physical devices directly. Why? Because when a piece of hardware dies and you might relace it with smething from a different manufacturer, you’ll have alot less configuration to do to swap it in!
-
When I say keep things simple with the short code - let’s say you have abunch of code that when one thing happens then a whole ton of other things are updated - in that same automation. Then later you add a different automation that indirectly involves one of the tiems in the huge pile of logic… sometimes you will have unintended consequences and it is very hard to figure out why something happened.
I can only give you one really good example of code deduplication and it was a struggle but I figured out how to do it, so hope this will help. First I will explain the scenario.
I have and liked to use the paradigm for automating lights coming on and going off when people come into or leave rooms, so I ended up writing the same kind opf code everywhere for this for different rooms. Then I figured out how to remove a huge amount of duplicate code. Ok here’s my scenario, use it to think through what you want to do but you will get the concept once you are done reading through this.
My lighting concept was buggy because if people did not move then the lights would go off as I am using inexpensive motion sensors. AND IT REALLY ANNOYS PEOPLE, ESPECIALLY THE WIFE! So - lets give the example of a single light in one room for a scenario I have pretty much perfected. I have:
Entities:
A. The light (actually it’s a wemo smart outlet for the lamp, not shown in the below code used to make my point.**** see my comments at the bottom about this under “addendum”
B. The Motion Sensor (in the code below, motion sensed in the room is represented by “binary_sensor.den_motion_sensor_motion”, coming from an integration with the motion sensor manufacturer, yolink)
C. A timer (in the code below, it is simply a timer entitiy in HA that I have called “timer.den_couch_lamp_by_closet_timer”)
D. An input variable (number) representing the duration of the timer when it is used (useful to have in the dashboard - in the below code mine is called “input_number.den_adjust_couch_lamp_by_closet_timer”, sorry bad naming convention, but I had the word “adjust” in all mine so I remembered it that way)
E. An input variable (could be a boolean but I have it as having two text values, “Enabled” and “Disabled” so my dashboard could have a drop-down) representing whether or not the automation is enabled or disabled (useful to have in the dashboard to control from the dashboard - in the code below my entitiy is called “input_select.den_automation_lamp_by_closet_is” - get it - "is… Enabled or is… Disabled - I know, not very imaginative)
Automations:
- When the timer is started, turn the light on. (NOTHING ELSE!)
- When the timer stops (is FINISHED, not CANCELED), turn the light off (NOTHING ELSE!)
- When motion is sensed, IF the automation is enabled (“Enabled” or “Disabled” variable) then timer is (re)started with the (number) duration specified. (NOTHING ELSE!)
The beauty of the above is, if the timer is already running which means the lights are already on, then if there is no motion in the room, the lights wil (EVENTUALLY) turn off. Every time there is motion, even if the lights aree already on, the timer is reset to the original duration and the lights stay on. So the only issue you have (not really) is if nobody moves for say your duration of 30 minutes - only then the lights would go off. (unlikely!) And even if the duration is set to 30 minutes if people are in the room for say 4 hours they lights would stay on the whole time - the timer would finish only 30 miutes after the last puerson leaves trhe room.
I did this in many rooms, and sometimes for separate timers I wanted for separate lights in the same room - so, I was stuck with having to write code for #3 a BUNCH of times! Here is how I got around that.
I took that code for #3 and put it in one place and it is called from a TON of different places - #3 I moved from an automation into one script that all of the automations call as needed (instead of them allhaving the same code).
So here it the automation code only for motion senser in my den to turn on a lamp (outlet):
alias: Den Motion Detected (Lamp By Closet Automation)
description: ""
trigger:
- type: motion
platform: device
device_id: 5ef8f43f23c1d09910584c8c6a3cb29c
entity_id: binary_sensor.den_motion_sensor_motion
domain: binary_sensor
condition: []
action:
- service: script.motion_detected
data:
enablement_input_selector_entity: input_select.den_automation_lamp_by_closet_is
timer_duration_entity: input_number.den_adjust_couch_lamp_by_closet_timer
timer_entity: timer.den_couch_lamp_by_closet_timer
mode: parallel
Note the “Script” section of the code at the bottom of the above automation - used to call a generic script
In the above automation, “enablement_input_selector_entity” (you could call it anything you want as long as it is the same spelling as what is in the script) is a variable used in the script below representing the actual entitiy I have (for Enabled/Disabled) which is named “input_select.den_automation_lamp_by_closet_is”.
“timer_duration_entity: input_number.den_adjust_couch_lamp_by_closet_timer” from above is the variable represneting the actual entitriy which is holding the duration for the time (that is used for the slider in the dasboard).
Finally, the “timer_entity: timer.den_couch_lamp_by_closet_timer” is the same idea - the actual timer is passed as a variable to the script from the automation.
Here is the source code for that script that is called that is using these entities passed by a large number of automations, sometimes running in parallel (hence the 1000 which might be overkill!). I don’t even pass the values to the script, I pass the entities that contain the values -
Remember, smaller code keeps everything simpler!
alias: Motion Detected
sequence:
- condition: template
value_template: "{{ is_state(enablement_input_selector_entity,'Enabled') }}"
- service: timer.start
data:
duration: "{{ (states(timer_duration_entity)|int*60) }}"
target:
entity_id: "{{ timer_entity }}"
mode: parallel
max: 1000
In the above case the - “enablement_input_selector_entity” is a generic name for the “Enabled”/“Disabled” variable passed to the script
The “timer_duration_entity” is a generic term for the duration for the timer. I have these durations as sliders on my dashboard, and the sliders represent MINUTES, but the duration for a timer is in seconds, hence the " * 60 "
Now it might seem like a lot of work and when I have an idea for an additional automation - it is - because I end up having to set up a gazilion different objects that are usied in each automation. But I think of it this way - the more granular it is - the more control you have to make it do absolutely anytihing you would want! And as you can see, the code is short (and it ALWAYS works)!
I hope the above helps. Te documentation for things like the above is not so easy to fins within the HA forums and documentation, so that is alkways a struggle. Maybe the effort needed to figure it out - helps us to really remember and learn how to do it… Thoughts?
****“Addendum”
As your HA journey becomes mature, you will add more and more and more for weird scenarios that do however happen). Let me explain what I mean, for just the above scenario.
-
If the automation is turned off for that lamp outlet, and the outlet is turned off, then your wife will yell at you “How come the lamp doesn’t turn on now? This home automation stuff is STUPID!”. Don’t try to explain it, that will just become more annoying to her! Lesson learned - add an automation that for any outlet when that “Enabled/Disabled” is changed to “Disabled”, - if the lamp is on, leave it on and stop the timer. If the lamp is off, TURN ON the outlet but don’t start any timer of course. Note, you hae to think everything through - for a smart outlet you need to leave it on in that case - but for a wall switch you don’t need to do anything.
-
in the same vein, if you have to work on or reboot or backup or whatever computer or raspberry pi you are using for home automation, then TURN ON ALL THE SMART OUTLETS FIRST so peoplpe in your family do not want to kill you lol… etc…
-
Then you would go back and for the all automations which do anything, (I know it’s a pain but you only need to do it once - take into account if the relevant flag is set to the automation being “Disabled” - then don’t do anything in the automation (you have to add it as a condition))
-
Have an automation that when automations are disabled, cancel the related timers.
-
I have other scenerios - if I change the duration slider on my dashboard for an automation say where a lamp is turned on already - I reset the timer to restart at the new (higher or lower) number - but if the item is not on and the timer is essentially dormant - then changing that value doesn’t have to have any automation assosicated with it…
-
Let me just add - whatever you do you will run into unexpected scenarios. The weirdest item I had was when one motion sensor sensed motion then lights in a different room would go on. Or nothing would happen. I checked and rechecked and rechecked my code and other than a couple unrelated items that were incorrect I coudn’t figure it out. I finally figured out evidently for Wemo smart outlets, their integration doesn’t rely upon the unique mac address of the physical smart outlet but instead on the IP address of that outlet. I have my router reboot every night and when it came back up, the ip addresses of some of them would change. So, for every single device that you add to your home for any home automation, go into your router and make it’s IP address atually assignbed to that device and permanent. That made my problem go away.
War and peace is finished… I look forward to your thoughts (you probably think I am nuts - I am, actually… )
I am left to conclude that you had some really bad experiences to arrive at these opinions. My own experience of using a consolidated automation is the exact opposite of yours.
No problem; I can help you with that. Post the automations you want to consolidate. I’ll examine them and let you know if it’s feasible to consolidate them, create a blueprint, or just leave them unchanged.
LOL. I used to write pretty sophisticated multithreaded code for trading systemns on Wall Street so I have seen it all and know from where I speak! Consolidating things into one lump i fine for something cmall but it will not scale. (Talking about best of breed practices here, to use the minimum CPU, etc.)
Fortunately for ocxo, a bunch of nearly-identical thermostat automations isn’t a trading system and can be either consolidated into a single automation or managed by a blueprint.
I have found NodeRed to be extremely well suited for DRYing automations.
It provide options to make dynamic use of entities and variables, but still handle them as a separate runs.
Please supply some more deatils so we can help
Thanks for the great explanation @KruseLuds! Amazing. Here’s two example automations that I think could be simplified. With some guidance on combining these two I can probably work out how to consolidate my other automations (i.e. Front Thermostat Night):
alias: Front Thermostat Day (M-F)
description: ""
trigger:
- platform: time
at: "09:00:00"
condition:
- condition: time
weekday:
- mon
- tue
- wed
- thu
- fri
after: "09:00:00"
before: "21:00:00"
action:
- service: climate.set_temperature
data:
temperature: 70
target_temp_high: 72
target_temp_low: 68
hvac_mode: heat
target:
device_id: 42db420da66e00f844f14171906491be
mode: single
alias: Front Thermostat Day (S-S)
description: ""
trigger:
- platform: time
at: "10:00:00"
condition:
- condition: time
weekday:
- sat
- sun
after: "10:00:00"
before: "21:00:00"
action:
- service: climate.set_temperature
data:
temperature: 70
target_temp_high: 72
target_temp_low: 68
hvac_mode: heat
target:
device_id: 42db420da66e00f844f14171906491be
mode: single
Have you looked at the new schedule integration? Looks like just what you want for these 2 automations. Also in this link @123 posted how to use it
It’s easy to combine those two automations into one. However, all they do is set the thermostat to the same setting (heat; 72) at two different times (09:00
on weekdays and 10:00
on weekends). Is there no need to set the thermostat to a different temperature, like at night?
Yes I have automations for night and a few other times during the day too. But for this help request I was trying to keep the examples simple as to not overcomplicate.
Post all of them. In all likelihood, it’s possible to consolidate them into a single automation.