Home Assistant Automation in Depth: Fusing Sensors Together for Stateful Automations

This post is part of my “Home Assistant Automation in Depth” series. Here is the series index:

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!

Okay, okay.

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.

The light controlled by Home Assistant automation
A very bad picture of the light in question.

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 our input_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.

8 responses to “Home Assistant Automation in Depth: Fusing Sensors Together for Stateful Automations”

  1. sibrecht Avatar
    sibrecht

    Hi,
    I like your writing. I have also a motion sensor in my hallway, with similar functionalities. Only I set my house into night mode with a google home routine. In this night mode, the lights are also less bright.
    I was just wondering: you are using a timer to turn off the lights. Every time motion is detected, is the timer set again to 2 minutes? If there is motion for 3 minutes, do the lights stay on?
    I turn the lights off also after 1 minute, but there is an option for a state that it needs to be off for 1 minute. Why don’t you use that?

    An example (from the home assistant website):

    automation:
    trigger:
    platform: numeric_state
    entity_id: sensor.temperature
    # Optional
    value_template: ‘{{ state.attributes.battery }}’
    # At least one of the following required
    above: 17
    below: 25

    # If given, will trigger when condition has been for X time, can also use days and milliseconds.
    for:
    hours: 1
    minutes: 10
    seconds: 5

  2. Rob Connolly Avatar
    Rob Connolly

    Hi,

    Thanks for the feedback. I used the timer for just that – so that it can be reset when there are further motion events, which keeps the light on for longer. If I were to use the state trigger with the `for` condition, I’m not sure if I would be able to reset that (I don’t think so).

    In my case if there is motion for three minutes, the lights will stay on for three minutes plus two minutes after the last motion event. I also have a night mode, which I trigger via an NFC tag and Tasker (probably worth a write up), when I go to bed, since I’m usually the last person in the house to go to sleep.

    Does this clear things up for you? Feel free to ask me further questions, I’m always happy to answer.

  3. Matt Avatar
    Matt

    Have you seen the “for:” construct in Home Assistant triggers? Basically it’s a way of determining for how long an entity has been in a certain state.

    For example, the following will trigger if there’s been no motion for 5 minutes. In my experience it’s much less likely to shut the lights off when not intended than a timer.

    – alias: “Bedroom No Motion Evening”
    trigger:
    platform: state
    entity_id: binary_sensor.bedroom_sensor_motion
    to: ‘off’
    for:
    minutes: 5

  4. Rob Connolly Avatar
    Rob Connolly

    Yes, I have seen the “for:” construct, but I probably wasn’t aware of it at the time I wrote the automation. I’m not sure why it would be any less likely to shut off the lights incorrectly than my arrangement though, but it would be somewhat simpler.

  5. Matt Avatar
    Matt

    I think it’s less likely because with the timer, you’re only checking for motion at one instant in time. So while motion may have happened, 30 seconds ago, if it’s not registering motion when you check again your lights will turn off.

    However, reading above (I didn’t read the previous comment fully, obviously!), I think it depends a lot on your use case.

    Apart from that, this was a nice example of using template actions. I used the idea to implement a “time_of_day” system that lets me adjust the lighting scene that I turn on during specific parts of my day.

  6. Rob Connolly Avatar
    Rob Connolly

    Yes, that would be the case were I not resetting the timer constantly when a motion event is detected. From the YAML above:


    action:
    - service: timer.start
    entity_id: timer.bathroom_light_timeout
    - condition: template
    value_template: "{{ states.light.bathroom.state == 'off' }}"
    ...

    In this way the two approaches are equivalent for this use case.

  7. Matt Avatar
    Matt

    Ah, now I see. Nice way to solve that issue!

    I had problems with my NodeRed flows for motion activated lights because of the issue I mentioned, and went back to using yaml. The templated actions solved a major issue that I had with the HA approach — proliferation of automations each dealing with similar states.

    My next goal is to do in-house person tracking to turn on lights preemptively based on movement patterns. That will require some tricky sensor fusion and I’m not sure how best to approach the problem within the confines of HA.

  8. Rob Connolly Avatar
    Rob Connolly

    That sounds interesting, I’d really like to know how you get on. Feel free to get in contact with me via the contact page and let me know!

Leave a Reply

Your email address will not be published. Required fields are marked *

Bad Behavior has blocked 295 access attempts in the last 7 days.