I’ve been testing various methods of room presence detection and while many do have limitations, they can give an accurate description of room presence in a home when they are all linked up and considered together. I wrote this guide on how I’ve been implementing my version of comprehensive room presence detection. I’ve made sure that all the processes are modular and scalable so it’s simple to add new users/sensors or tweak criteria for changes.
This guide is for implementing per-person home and room presence detection in Home Assistant, where the logic is processed via the Node-RED add-on.
Download – You can download a copy of all the flows below:
(note – I used a scrubber program to hopefully make it easier to import the flow. However, when I try to import it, I get an error message on all the Home Assistant nodes which are cleared when I double click on each one. If I figure out how to stop this, I’ll update the file.)
My flows use a combination of GPS tracking of phones, Bluetooth Low Energy (BLE) room presence, motion sensors and bed presence sensors to try to determine house and room presence up to the best level possible at any given time. I use the combined results of these methods to trigger many automations (lights, sleep, alarms etc.). Having reliable presence detection makes triggering all the other flows much more intuitive since you can drastically reduce the number of other conditions required for an automation to run.
Hardware:
In my setup, presence is tracked for two people across four rooms (the Bedroom, Living Room, Bathroom and Kitchen).
- Motion sensors: 2x Aqara Motion sensors above the door in the Bedroom and Living Room. These are implemented via Zigbee2MQTT and a CC2531 dongle.
- BLE Room Presence: 4x ESP32s flashed with ESP32-mqtt-room (1 in each room). These track BLE beacons on android phones generated by the Beacon Simulator app.
- Bed presence: 2x HC-SR04 ultrasonic sensors on the underside of bed slats on either side (not the best solution but worked more reliably than pressure sensors for me).
- Humidity Sensor: 1x Aqara temperature pressure humidity sensor tracks the humidity in the Bathroom to detect shower usage.
And that’s it. I do also use the HA app on the two android phones along with the Google Maps integration for home/away presence (discussed in the section below).
All the methods below can work fine with different methods of BLE room presence detection or brands of motion sensors. So long as the output of the entities is consistent, they can be swapped out easily. It’s also easy to start tracking further rooms and people by adding a motion sensor or even just an ESP32 (the Bathroom and Kitchen don’t have motion sensors in this example).
In Home Assistant:
Each person has a person.*person* entity (set automatically by the Google Maps integration or the device tracker component of the HA android app using the Person integration). In the flows below, I have just used an example person (Person 1). In this guide, I often abbreviate them to p1 in entity naming – so the entity for Person 1 would be person.p1. For actual people, I would suggest using initials for brevity so it helps if people in the home don’t share the same initials!
I use input_selects as a way to set all house/room presence and the individual locations of people:
- input_select.house_presence – keeps track of who is home
- input_select.*room*_presence – keeps track of individual room presence
- input_select.*person*_location – keeps track of where people are (and is more detailed than the home/away person.*person* entity)
In Node-RED:
Next up is a summary of my workflow to set the best possible room presence with whatever level of info is available at any given point. In my experience, BLE room presence is fantastic when it’s working (ie. the beacon is picked up and the person has their phone with them) but the overall process needs a helping hand to stay on track at times.
Group Frequently Used Nodes
In my Node-RED flows, all entities that are updated by many flows (such as input_select.p1_location) are terminated at a single node rather than lots of individual call service nodes at the end of each flow. This makes it much easier to find each flow that can update an input select. Different options are set in a function node prior to the message getting sent to be set to a terminus (this also shows what option each flow is setting). All the call service nodes that set house/room presence and people locations are grouped nearby with their own link in nodes right at the start.
Step 1: Home/Away Presence
1.1 – Set Home/Away on Startup:
I have each person’s input_select.*person*_location set as either Home or Away on startup. This is just in case changes were missed while Node-RED was offline. More detailed presence info will just overwrite this Home/Away status when available anyway. I have a flow that sends a message only when Home Assistant starts up. I use a sensor that monitors uptime and triggers when Home Assistant has been online for a few minutes. This triggers many flows that initialise various parameters and update them or do simpler tasks like setting the Home Assistant theme after a restart.
Setting Home or Away can be done using current state nodes or in a single function node for each person like my example below. (Note this flow relies on this add on that allows you to access Home Assistant states via a global variable – see this link for another example).
1.2 – Set Just Arrived/Home/Just Left/Away/Extended Away:
The next two flows are to set the overall house presence and keep it updated as people leave and arrive.
1.3 – Set House Presence:
The first flow is triggered by any person’s person.*person* changing to either Home/Away. The node with the question mark icon shows a place where the flow checks that this specific type of automation is actually turned on (that the user wants it to run). I have a series of input_boolean.automations_*type* that can pause various types of automation. The node here checks that input_boolean.automations_presence is on before continuing the flow.
Next, there is also a separator if there are guests. Currently, I have guest mode set to effectively override much of the presence detection. With guests, input_select.house_presence is set the Guests and remains unchanged. Without guests, a function node returns a string with the people at home and sends the value to a node that sets input_select.house_presence.
The function node searches all the person.*person* entities listed in group.person_home_away. It compiles a ‘+’ separated list of all the person.*person* entities which are Home. It takes the friendly name of all the person.*person* entities which are handily each person’s initials. Extra formatting sets the returned msg.payload so that the correct input_select.house_presence is set. So, if only Person 1 is home the node would set input_select.house_presence to P1 whereas, in a house with four people, it would return any combination of the four people who are home, in the order that they are set in the group.
This could also be achieved by checking each other person manually as someone else’s input_select.*person*_location changed, but that would get cumbersome for any more than two tracked people. Instead, this method is easily adapted to more people.
1.4 – Set House Presence to Extended Away if everyone is Extended Away:
Another short flow is triggered by any one person’s location input select going to Extended Away. The flow then sets input_select.house_presence to Extended Away if everyone else is too. If everyone goes on holiday, input_select.house_presence will be changed as soon as the last person’s input_select.*person*_location goes to Extended Away.
Step 2: Generic Room Presence Detection
The next step down from overall house presence are flows to set generic room presence with no reliance on any BLE tracking. Instead, motion sensors and other generic indications of room presence are used as triggers (eg. a humidity sensor in the Bathroom and bed presence in the Bedroom). My generic room presence flow determines whether a room is occupied and can set an input_select.*room*_presence to be either Someone or Empty (or a specific person if only they are home).
2.1 – Motion/Other Generice Room Presence Indicators:
This flow is triggered by generic indicators of room presence wherever they exist. Each room has two link nodes – the first is for detection of presence (such as a motion or bed presence sensor being activated) and the second for the indication of a room being empty (such as motion detectors going to off). Things like a bed presence sensor going to off are not necessarily indications of a room being empty (since the Bedroom can be occupied without bed presence being on) so they are not wired into the Bedroom Not Occupied link node.
In the Bathroom, the humidity rising to over 75% suggests occupation while it falling below 75% suggests that it is no longer occupied. Examples like this or TV usage in a room can trigger generic presence detection in the absence of motion sensors. Since the Kitchen has no generic indicator of room presence, it is simply not included in Generic Room Presence Detection. Adding a power monitored kettle etc. would be an example of how to add this to the Kitchen.
It is not a good idea to add a room to Generic Room Presence Detection without an indicator that suggests the room is Empty. Otherwise, in the absence of more precise BLE Room Presence Detection, the room can never be set to Empty by just Generic Room Presence Detection.
For each room, a boolean flow variable is also set. This keeps track of whether Generic Room Presence is detected in each room and is used later (in Step 4) when setting the presence in a room. The naming convention for these flow variables is important and is explained in the note in the flow.
2.2 – Set rooms to “Someone”:
When there is an indication of presence in a room by this generic method, the input_select.*room*_presence for that room is set to a specific person (ie. P1) if only one person is home or to Someone, if there are multiple people home since the people cannot be distinguished (in this generic room presence flow).
This flow also checks that presence detection is wanted by the user under the node with the ‘?’ icon. When setting the Bedroom to Someone by this method, there is also a check that the no-one is currently sleeping in the room (ie that input_select.bedroom_presence is not Going to Sleep, Sleeping, Waking Up or Woken Up. In these cases, Bedroom presence should not be changed. This is achieved by the node with the ‘moon’ icon. This check is also performed commonly to make sure that nothing changes the presence in the Bedroom away from any type of sleeping.
2.3 – Set rooms to “Empty”:
After motion sensor timeout or another generic indication of a room no longer being occupied, if the input_select.*room*_presence for that room is still Someone, there is another timeout before the room is set back to Empty. This timeout is important when BLE room presence is also in the mix (discussed later).
This timeout can be cancelled (no longer setting the room to Empty) if during the timer, the input_select.*room*_presence for that room changes to something other than Someone (most likely due to the input select being set to a specific person when their BLE detects them in the room too) or if new motion is detected in that room (the link node that sets the input select to Someone is also wired in to cancel any possible timer setting the room to Empty). In both these cases, there is confirmed occupation in that room so any timer looking to set the room to empty should be cancelled and no message passed.
There is also an option to add other conditions that must be met before a room is set to Empty by this method. In this example, the Bedroom can only be set to Empty by Generic Room Presence Detection is there is no presence detected on either side of the bed. If bed presence is detected before the Bedroom is to be set to Empty, the timer keeps getting restarted until there is no more bed presence detected or the timer is cancelled by something else.
Step 3: BLE Room Presence Detection
Next up is the ‘holy grail’ of presence tracking – the elusive room presence detection which distinguishes exactly who is in which room. In my flows, this works on top of the generic room presence detection based on motion and other faster-acting sensors. Motion sensors will always be faster than BLE which is the most common type of person-specific room presence detection that I’ve encountered. So, I use a combination of the two.
Using a combination is helpful if a person goes into a room but then stays seated/still. First, when someone walks into a room – the generic flow (2.2) would set an input_select.*room*_presence to be Someone straight away (this is used for lighting etc.). Second, when BLE tracking catches up and detects that a person has moved to this room, the Someone option is overwritten with the new person (based on BLE tracking). Now, when the motion detector is clear but the person is still sat there, the input_select.*room*_presence would still show the person in there (via BLE) so the room would not be set to Empty.
3.1 – BLE Inputs and Checking other States:
The BLE sensor for each person is fed into a flow that also saves other info that is later checked before setting a person’s input_select.*person*_location.
Similarly to the Home/Away flows, the input entity id of the sensor is stripped and a msg.person is set (for Person 1, msg.person would be p1 if their BLE sensor was named sensor.p1_esp32_mqtt_room etc.). Next, msg.ble_room is set as the state of the sensor that triggered the flow.
Then, the flow only continues if input_boolean.automations_presence_ble is on (this is an easy way to stop BLE detection overwriting generic room presence detection if required). If allowed to continue, a series of other states are saved too (whether Bedroom or Living Room motion has been detected) and also the current state of the person’s input_select.*person*_location. Since the msg.person was set, the current state node can just check on the state of input_select.{{person}}_location (where {{person}} retrieves msg.person).
Following this, a function node checks various properties about bed presence. If input_select.automations_bed_presence is on, and the person’s bed (eg. binary_sensor.bed_presence_p1) is also on (occupied), then the node sets msg.bed_presence to on. Otherwise, msg.bed_presence is set to off/not automated. Adding each of these properties to a msg property makes checking then much easier later.
3.2 – Set Room Presence:
Step 4: Update Room Presence
Conclusion
Thanks for reading this tutorial on my implementation of comprehensive room presence detection! It’s my first writeup of my projects so feel free to get in touch with any comments or suggestions. I’ve certainly noticed that having to write it all down made me think more critically and helped to improve the flows in general.
If you want to try out this way of room presence detection, you can download a file with all the flows discussed at the top of the page (in the download section, just after the first image). So long as you set up you own input select variables and groups etc. that were described in the ‘In Home Assistant’ section, it’s just a matter of changing the various rooms and people to suit your setup.
I mentioned that I use this room presence as a way to automate various things around the house (lighting, plant lighting, various aspects of a morning alarm clock, vacuum schedules and when to activate displays etc.). Over time I’ll try to do shorter writeups of these projects too – they will be linked under the Home Automation section of this website.