This post is part of my “Home Assistant Automation in Depth” series. Here is the series index:
- Part 1: Making my mornings a little easier
- Part 2: Fusing Sensors Together for Stateful Automations
- Part 3: Multi-function NFC Tags
One of the most powerful things about Home Automation is being able to undertake many actions simultaneously, based upon a single input. For example, almost every automation system has the concept of scenes which allow you to set the state of multiple devices.
Just as powerful, if not more, is the ability to perform an action based on multiple inputs. We see this in the Home Assistant automation language which introduces the concepts of triggers and conditions. Triggers are the initial event that that the automation reacts to. Conditions are extra inputs which should be in the correct state to proceed.
With these simple concepts, we can easily create automation routines which operate only at certain times, are dependent on the state of the sun or even the moon (if you happen to be a werewolf). However, how do we track the state of something that happened, given that it’s current state may have changed?
State and Events
As a brief aside, we’ll discuss the difference between the state of something and events which occur. I’ll discuss this in general computing terms as well as within HASS.
State is data which describes the current situation. For example if the temperature is 24°C, then the state of the temperature entity should have a numeric value of 24 and a unit of Celsius. In Home Assistant states come in two varieties, the primary state and attributes. The primary state is generally just referred to as the state of the entity. Attributes are more like ancillary data, or metadata, but can be just as useful as the main state.
Events are things that happen. As such they are transient and only exist at the exact point in time at which they occur. Home Assistant automation events can be triggered when something changes state, when a particular time occurs, when a message is received and many others. Even an entity being in a particular state for a certain length of time can be considered an event. See the HASS Triggers documentation for more details. It should be noted that all the items on which an automation can trigger are events in the general computing sense not just those which trigger the ‘event’ trigger.
A system is ‘event driven’ if it primarily reacts to events occurring in the environment rather then polling the state of the environment. In this way, a Home Assistant automation can be seen as an event driven system, since they are primarily triggered by events.
Just get on with it!
Enough with the computer science lesson. How is this relevant to triggering an automation when multiple things happen? Well let me describe a situation:
You get up in the middle of the night, open the bedroom door and walk down the hall. You go into the bathroom, where the motion detector senses your presence and helpfully turns the light on. At 100% brightness. Temporarily blind you then proceeds to do whatever it was that got you out of bed.
Now I can guess what you are saying. Why not just adjust the brightness based on time of day. Well we could, but what happens when we have multiple people in the house, some of whom are awake and some asleep?
[For those that are wondering, the light pictured above is a Mi-Light RGB-CCT downlight. I think that link is to the correct one, but I can’t be sure as I bought mine from LimitlessLED before they closed down. Being an NZ company they were able to provide the documentation for my electrician to install these. I had several of these installed when the house was built, but I actually wish I’d had the whole house done with them.]
Sensor Fusion
Hopefully, we have more than just the time of day and the motion event to go on. With the help of another sensor (or set of sensors) we can integrate the data together and not blind anyone. It should be noted that this isn’t really sensor fusion in the strict mathematical sense! However, the definition fits quite well and I like the name!
In my case the second source of data are my zigbee door sensors. One of these is sensing the state of the bedroom door and the other is sensing the state of the kitchen door to the main living space. The logic is simple, if the bedroom door is opened the light comes on dim when motion is detected. If the kitchen door is opened, the light comes on bright when motion is detected.
Here’s where the difference between state and events becomes important. If I close the door behind me, the system cannot determine the correct door that should drive the lighting. This is because either neither is open, or the wrong door is open (if it was left open). It turns out the event of the door opening was the important part. Not the actual state at the time of the motion event.
We need to convert the door opening event into a state which we can store somewhere else. Luckily, we can do this easily in Home Assistant using an input_select
entity.
Finally, some YAML
input_select:
last_door:
name: "Last Opened Door"
options:
- "Kitchen"
- "Bedroom"
Here, we create an input_select
entity which will store the last opened door. Note, you can extend this to store more states. For my purposes I only need to differentiate between bedrooms and kitchen.
An automation is used to catch the door open events and translate that to the correct state of the input_select
:
automation:
- alias: Set Last Opened Door
trigger:
- platform: state
entity_id: binary_sensor.kitchen_hall_door_contact
from: "off"
to: "on"
- platform: state
entity_id: binary_sensor.bedroom_door_contact
from: "off"
to: "on"
# further triggers for other bedroom doors can go here
action:
service: input_select.select_option
entity_id: input_select.last_door
data_template:
option: "{% if trigger.entity_id == 'binary_sensor.kitchen_hall_door_contact' %}Kitchen{% else %}Bedroom{% endif %}"
I’ll walk through this automation step by step:
- First we have our triggers, one for each door contact sensor we have. Obviously you can add as may of these as you want. For now I just have two.
- There are no conditions, the automation just runs whenever triggered.
- We call a single action which calls the
select_option
service of ourinput_select
entity. - A simple template is used to compare the triggering entity ID (i.e. which contact sensor triggered the automation) with the entity ID of the kitchen door sensor. In that case we set the option to ‘Kitchen’ otherwise we set it to ‘Bedroom’. In this way, this automation will scale to multiple bedrooms, but can only have one kitchen. If you have multiple kitchens (!) or other relevant rooms, you’ll need a more complex template.
Stateful Home Assistant Automation
Next comes our automation to control the light. This automation is stateful, in that it executes differently depending on the state of the input_select
we just set:
automation:
- alias: Bathroom Motion Light
trigger:
platform: state
entity_id: binary_sensor.bathroom_motion
from: "off"
to: "on"
condition:
condition: or
conditions:
- condition: sun
after: sunset
after_offset: "-00:20:00"
- condition: sun
before: sunrise
before_offset: "00:20:00"
action:
- service: timer.start
entity_id: timer.bathroom_light_timeout
- condition: template
value_template: "{{ states.light.bathroom.state == 'off' }}"
- service: light.turn_on
entity_id: light.bathroom
data_template:
brightness_pct: >
{% if now().hour >= 20 or now().hour < 7 %}
{% if states.input_select.last_door.state == 'Bedroom' %}
1
{% else %}
100
{% endif %}
{% else %}
100
{% endif %}
Breaking it down
Again, I’ll go through the automation step by step:
- First we have our trigger, in this case from our motion sensor.
- Next we have a couple of conditions to only run the automation when it’s dark enough. In this case 20 minutes before sunset and 20 minutes after sunrise. We wrap these in an or block so that only one has to match.
- Now we get to the actions, the first of these is to start a timer. This will be used later for turning off the light. For completeness the YAML for the timer looks like this:
timer:
bathroom_light_timeout:
duration: "00:02:00"
- The next action is actually another condition, which basically matches only if the light is already off. If this is not matched the action block will terminate here. This condition ensures that the brightness of the light does not get changed if the door state changes. Placing this after the timer start also ensures that the timer is restarted by motion events in the bathroom.
- The final action is obviously to turn the light on. The interesting part is the template logic. This contains a nested if block, the first part of which checks against some pre-defined times where we only want the light bright (before 8pm and after 7am). This is mostly useful in winter, since the sunset and sunrise rules will prevent the automation from running before/after these times in summer.
- The inner if statement is where we check our door state. Here we check whether the last door was to a bedroom. In that case we set the brightness to 1% (plenty bright enough for night time use on these lights). Otherwise we go to full brightness.
Finishing It Off
The final piece of the puzzle is turning off the light when the timer expires:
automation:
- alias: Bathroom Light Timeout
trigger:
platform: event
event_type: timer.finished
event_data:
entity_id: timer.bathroom_light_timeout
action:
- service: light.turn_off
entity_id: light.bathroom
This is reasonably self explanatory. The only odd looking bit is in the trigger where we must use the event platform to directly catch the timer.finished
event, since the timer doesn’t have it’s own trigger type.
Conclusion
Phew! Hopefully you got this far and didn’t get lost in the weeds of the states vs events stuff!
The automations shown here have been running for several months pretty much flawlessly. I’ve given them some minor tweaks such as introducing the outer if statement to check the hours, when winter came around and it became apparent that it was needed.
The usual complaint is that motion sensing lights tend to turn off when people are still in the room but not moving. Since the motion sensor in this case is so sensitive we’ve not had much of an issue with this, it will trigger even on the slightest motion. Interestingly it also seems not to false trigger, so I think the balance is just right.
Thanks for following along! I don’t have any more plans for another Home Assistant Automation in Depth article, but I’m sure there will be another installment in the future. I just need to write some more interesting automations.
Leave a Reply