Motion Tracking Lights (Lights on only in occupied room)

For this project I started out with the idea of having the lights turn on and off as I walked around my apartment. The idea was to only have lights on in the room that I was occupying to save electricity costs and the obvious “wow” factor.

Here is an explanation of how it all works.

Motion Sensor Selection:

For this project the key element is finding motion sensors that can quickly return to an “off” or “idle” state to ensure they can be ready to respond to motion again as the occupant moves from room to room. Ideally this value is as low as possible hopefully sub 2 seconds depending on the size of the room. The reason behind this is that if I were to walk from my living room into the bedroom and back into the living room quickly I want to ensure that Home Assistant receives a “on” or “motion triggered” event each time I cross the door way.

To this end I ended up going with the Fibaro Multi Sensor. It has configurable blind an active times that can be set as low a 0.5 seconds so they’re ideal for triggering frequently when transitioning between rooms.

Motion Sensor Placement:

Second was the placement of the motion sensors themselves, because in order to ensure that the sensors were not constantly being triggered and unprepared to trigger on when movement between rooms would occur. I found that the ideal placement was where the motion sensors could see the doorways, but not be triggered by movement in idea areas.

For example, if I am sitting on the couch and move around the motion sensor will not be triggered. The advantage to this setup is that motion triggering is focused around an event when movement between rooms will likely occur so false positives such as the uncertainty of what will happen within those 2 seconds of blind time is minimized and can be accommodated for in the home assistant automation.

A second benefit is that the motion sensors themselves can also maintain battery life for longer even with events being triggered substantially more often.

Home Assistant Configuration:

################################################################
## Packages / Motion Lights
################################################################

# @todo Handle non-realistic transitions gracefully

################################################
## Customize
################################################

homeassistant:
  customize:
    packages.motion_lights: &customize
      package: 'motion_lights'

    binary_sensor.aeotec_zw100_multisensor_6_sensor_3_0:
      <<: *customize
      friendly_name: Closet Motion

    binary_sensor.fibaro_system_fgms001zw5_motion_sensor_sensor_4_0:
      <<: *customize
      friendly_name: Bathroom Motion

    binary_sensor.fibaro_system_fgms001zw5_motion_sensor_sensor_5_0:
      <<: *customize
      friendly_name: Bedroom Motion

    binary_sensor.fibaro_system_fgms001zw5_motion_sensor_sensor_6_0:
      <<: *customize
      friendly_name: Livingroom Motion

    binary_sensor.fibaro_system_fgms001zw5_motion_sensor_sensor_7_0:
      <<: *customize
      friendly_name: Kitchen Motion

    input_select.last_motion_triggered:
      <<: *customize
      friendly_name: 'Last Motion Triggered In'

    group.livingroom_motion_off:
      <<: *customize
      hidden: true
    group.closet_motion_off:
      <<: *customize
      hidden: true
    group.bedroom_motion_off:
      <<: *customize
      hidden: true
    group.bathroom_motion_off:
      <<: *customize
      hidden: true
    group.kitchen_motion_off:
      <<: *customize
      hidden: true

    group.livingroom_motion_on:
      <<: *customize
      hidden: true
    group.closet_motion_on:
      <<: *customize
      hidden: true
    group.bedroom_motion_on:
      <<: *customize
      hidden: true
    group.bathroom_motion_on:
      <<: *customize
      hidden: true
    group.kitchen_motion_on:
      <<: *customize
      hidden: true

    input_boolean.dummy:
      <<: *customize
      hidden: true

   
################################################
## Groups
################################################

group:

  livingroom_motion_on:
    entities:
      - group.livingroom_lights
  bedroom_motion_on:
    entities:
      - light.bedroom_lamp
  bathroom_motion_on:
    entities:
      - group.bathroom_lights
  closet_motion_on:
    entities:
      - light.closet_light
  kitchen_motion_on:
    entities:
      - group.kitchen_lights

  kitchen_motion_off:
    entities:
      - group.livingroom_lights
      - group.bedroom_lights
      - group.bathroom_lights
      - light.closet_light
  livingroom_motion_off:
    entities:
      - group.kitchen_lights
      - group.bedroom_lights
      - group.bathroom_lights
      - light.closet_light
  bathroom_motion_off:
    entities:
      - group.livingroom_lights 
      - group.bedroom_lights
      - group.kitchen_lights
      - light.closet_light
  bedroom_motion_off:
    entities:
      - group.livingroom_lights 
      - group.bathroom_lights
      - group.kitchen_lights
      - light.closet_light
  closet_motion_off:
    entities:
      - group.livingroom_lights 
      - group.bathroom_lights
      - group.kitchen_lights     

################################################
## Input Boolean
################################################

input_boolean:
  dummy:
    name: dummy input for automations

################################################
## Input Select
################################################

input_select:
  last_motion_triggered:
    initial: Livingroom
    options:
      - Livingroom
      - Kitchen
      - Bedroom
      - Bathroom
      - Closet

################################################
## Automation
################################################

automation:

  - alias : Update last_motion_triggered from motion sensor last triggered.

    trigger:
      platform: state
      entity_id: 
        - binary_sensor.aeotec_zw100_multisensor_6_sensor_3_0
        - binary_sensor.fibaro_system_fgms001zw5_motion_sensor_sensor_4_0
        - binary_sensor.fibaro_system_fgms001zw5_motion_sensor_sensor_5_0
        - binary_sensor.fibaro_system_fgms001zw5_motion_sensor_sensor_6_0
        - binary_sensor.fibaro_system_fgms001zw5_motion_sensor_sensor_7_0
      to: 'on'

    action:
      - service: input_select.select_option
        data_template:
          entity_id: input_select.last_motion_triggered
          option: "{{ trigger.to_state.attributes.friendly_name | replace(' Motion', '') }}"

  - alias : Adjust lights based on last_motion_triggered selection.

    trigger:
      platform: state
      entity_id: input_select.last_motion_triggered

    action:
      - service: light.turn_on
        data_template:
          entity_id: "group.{{ trigger.to_state.state | lower }}_motion_on"

      - service: light.turn_off
        data_template:
          entity_id: >
              {% if (as_timestamp(now()) - as_timestamp(trigger.from_state.last_updated)) | int > 1 %}
                  group.{{ trigger.to_state.state | lower }}_motion_off
              {% else %}
                  input_boolean.dummy
              {% endif %}
          transition: 3

The code above is the home assistant automation that I am using to control the lights. There are two major groups. One that controls the lights that will be turned off when I enter a specific room and one that will turn the lights on when I enter a specific room. Secondly there is an input_select that shares the same naming scheme as the room so that the automation can concatenate the necessary commands to perform the action. Lastly there is a timestamp check that ensures that lights will not be turned off if a transition occurs to quickly, since the system cannot be sure in which direction the motion occurred.

Eventually I would like to simplify the timeframe code using statistics sensors to make the transition time is dynamic and more robust.

Conclusion:

Overall I am very happy with how this is working and you can see a demonstration here: https://www.youtube.com/watch?v=kRRXve7HTpE

If you have any questions please let me know!

21 Likes

Just as an update to this code I wanted to show some changes to how this is working. Specifically I’ve learned that looking at when the motion sensors go back to an “off” state collectively and only turning the lights off in the unoccupied rooms when motion has ceased in all rooms provides a significantly better experience. The modified code is in the automation section. This also entirely removes the need for an input_select sensor for tracking the current room I am located in, as the last motion sensor to return to the off state should indicate the currently occupied room.

  automation:
  - alias : Update last_motion_triggered from motion sensor last triggered.

    trigger:
      platform: state
      entity_id:
        - binary_sensor.fibaro_system_fgms001zw5_motion_sensor_sensor
        - binary_sensor.fibaro_system_fgms001zw5_motion_sensor_sensor_2
        - binary_sensor.fibaro_system_fgms001zw5_motion_sensor_sensor_3
        - binary_sensor.fibaro_system_fgms001zw5_motion_sensor_sensor_4
        - binary_sensor.fibaro_system_fgms001zw5_motion_sensor_sensor_5
        - binary_sensor.fibaro_system_fgms001zw5_motion_sensor_sensor_6
      to: 'on'

    action:
      - service: light.turn_on
        data_template:
          entity_id: "group.{{ trigger.to_state.attributes.friendly_name | replace(' Motion', '') | lower }}_motion_on"
          transition: 0

  - alias : Turn off lights based on last montion triggered

    trigger:
      platform: state
      entity_id:
        - binary_sensor.fibaro_system_fgms001zw5_motion_sensor_sensor
        - binary_sensor.fibaro_system_fgms001zw5_motion_sensor_sensor_2
        - binary_sensor.fibaro_system_fgms001zw5_motion_sensor_sensor_3
        - binary_sensor.fibaro_system_fgms001zw5_motion_sensor_sensor_4
        - binary_sensor.fibaro_system_fgms001zw5_motion_sensor_sensor_5
        - binary_sensor.fibaro_system_fgms001zw5_motion_sensor_sensor_6
      to: 'off'

    condition:
      condition: state
      entity_id: input_boolean.guest_mode
      state: 'off'

    action:
      - service: light.turn_off
        data_template:
          entity_id: >
              {% set roomname = trigger.to_state.attributes.friendly_name | replace(' Motion', '') | lower %}
              {% set lightgroup = 'group.' + roomname + '_motion_off'  %}
              {% if is_state('group.motion_sensors', 'off') and is_state(test3, 'on') %}
                  {{ lightgroup }}
              {% else %}
                  input_boolean.dummy
              {% endif %}
2 Likes

Thank you so much for sharing. I see your post from youtube since last year.

Great project! :slight_smile:
Have you figured out how to make this work with multiple people in the house? I’d love a setup like this, but it would need to work for me and my gf in different rooms.

not yet, but work in progress using Markov chains and statistics

As far as with multiple people. Can’t the system be modified to have a motion sensor on each side of the entry ( as long as the entry isn’t huge this should be possible ). Then using the state of the motion sensors you could build a counter of how many people are in the room depending on what direction the motion sensors were triggered in.

Fibaro actually did an article on this exact concept, though I haven’t personally tried it. I invested into a lot of Aeotec multi-sensors and though 15-20 seconds isn’t bad between off state detections it doesn’t allow me to do something cool like this. I may try to switch them out for Fibaro. I also live in a multi-person + pet house though so ideally I test this first. Let me know if anyone actually tries this out!

One downside with this is that it pretty much doubles the amount of PIR sensors you need though.

I’ve ordered 4 of them and I’m going to test this with my office. Both my girlfriend and I work from home and we go in and out of the office. Right now I have a long delay on turning off the lights ( 1 hour since last motion during the day and 15mins at night because we aren’t working at night but sometimes go in there to grab things).

If I setup the counter I should be able to tell how many people are in the office and keep the lights on or turn them off. I’ll report back here with any findings.

I have considered this approach and nearly have enough motion sensors to do this. My issue would be how to ensure that motion isn’t triggered inadvertently such as when I enter a room and turn around immediately because I forgot something.

The other issue I see is that the motion sensors have a fairly wide field of view. So positioning would be crucial to make sure this works well.

with regards to

15-20 seconds between off-state detections

You can change the holding time of a ‘detected’ signal back to HA within the z-wave settings. I have mine set to 1 or 2 seconds (cant remember exactly)

I think you have to have them plugged in to be able to do this? If they are on battery I think there is a lower limit that they wont go past.

So I’ve setup my two Fibaro multi sensors, one inside my office and one outside my office. As initially stated by @keatontaylor the issue is definitely getting the positioning correct, which I’m still struggling with.

Here’s the placement inside of my office:

Here’s the placement outside of my office:

Here’s an example of the occupancy counting, with a two person household. So as you can tell it’s obviously off :slight_smile:

The major issue in my setup that I found is that in these cases often the motion detection message from one last sensor to be triggered will be the first to be received by HA and vice versa. So a false trigger in the wrong direction.

I’m not sure if this is related to delay in the communication protocol or latency due to the python implantation of open-zwave on HA.

In your case I’d try playing around with the sensitivity settings. Adjust them to the point that they are just sensitive to detect a human body transitioning through the doorway.

I do this in several rooms with the Xiaomi motion sensors. I tried a bunch of zwave sensors including these Fibaro ones, and found the Xiaomi sensors to be much faster and more reliable. They are also waaay cheaper. They reset after about 1.5 minutes, but that has worked well for me.

1.5 minutes would defeat the purpose of my setup. I want the lights in the room I am exiting to turn off within seconds of me exiting that room. With my setup ~2.5 seconds.

Could I ask what your setup is, with code examples if possible? I have a bunch of the Xiaomi sensors too and I’m only using simple ‘on if motion detected, off if no motion for x minutes’ automations and I’d like to take it a bit further.

1 Like

Same here. I use a Xiaomi motion sensor for basic motion sensor lighting but I’m curious how you use them for tracking room occupancy.

hello guys
I just follow the instructions of keatontaylor
I created the file in my Packages
When I check the configuration is not error but keep running forever without error
keep running forever
someone help me please …
thank you

what checking is_state(test3, 'on') %} is this ?

can anyone give a tip to make a template switch off in 30 minutes ?
and some idea how to turn it manual so it wont turn off by timer

This is a very neat bit of code to achieve the motion tracking lights with minimal lines, nice one!

I wonder whether you find it annoying that the lights would turn off so aggressively in the room if you were sitting still though? Generally I find that giving a bit of a threshold to say that no motion was detected for X minutes (usually about 10 seems to work in my experience for rooms where we might be sitting to eat or watching TV) causes less need to wave arms around in the air to put the lights on again… unless I’m misunderstanding what this does, and it only turns off the lights after motion is detected in another zone?

I like the idea of occupancy tracking for rooms as well if it could be done reliably.