Generic Presence Detection

Edit: A rewrite to take advantage of the Debounce rule template on the marketplace.

This is a tutorial to show a generic and sensor independent way to do presence detection with multiple sensors and implementing anti-flapping (i.e. all sensors must be OFF for a certain amount of time before the home goes to away).

Requirements

  • One or more Switches determines whether someone is present.
  • If one or more of these Switches is ON then someone is presumed to be present (more complex algorithms are possible but implementing them is an exercise left to the student)
  • All Switches must be off for a certain amount of time (5 minutes in this case) before turning off presence. If one returns before that amount of time then presence never turns OFF.

The Code

Items

In this example we will have two people. Each person will have two sensors and when any one of those sensors is ON the person is determined to be present. When both go to off for five minutes the person is determined to be absent.

Finally, there is one Item that represents the overall presence.

Because we are using the Debounce rule template we will also need some Proxy Items also.

Item Name Type Purpose Member of Debounce metadata
Presence Group:Switch using the “if one ON then ON” function Represents overall presence
Person1Presence Switch Represents the debounced presence for Person 1 Presence
Person1Presence_Raw Group:Switch using “if one ON then ON” function Represents the raw presence status of Person 1 Debounce debounce=Person1Presence[timeout="5m", states="OFF", command=true]
Person1Sensor1 Switch One of Person 1’s sensors Person1Presence_Raw
Person1Sensor2 Switch One of Person 1’s sensors Person1Presence_Raw
Person2Presence Switch Represents the debounced presence for Person 2 Presence
Person2Presence_Raw Group:Switch using “if one ON then ON” function Represents the raw presence status of Person 2 Debounce debounce=Person2Presence[timeout="5m", states="OFF", command=true]
Person2Sensor1 Switch One of Person 2’s sensors Person1Presence_Raw
Person2Sensor2 Switch One of Person 2’s sensors Person1Presence_Raw
PresenceOverride Switch If one wants to turn off presence detection flip to ON. Presence

Create these Items in MainUI with these parameters. To add the metadata click on “Add metadata” and “enter custom namespace”. Enter “debounce” as the namespace. Then create the debounce metadata to look like:

value: Person1Presence
config:
  states: OFF
  timeout: 2m
  command: "True"

In a text based .items file:

Group Debounce
Group:Switch:OR(ON,OFF) Presence "Presence"
Switch PresenceOverride (Presence) "Override Presence Detection"

Switch Person1Presence "Person1 Presence" (Presence)
Group:Switch:OR(ON,OFF) Person1Presence_Raw (Debounce) { debounce="Person1Presence[timeout='5m', states=OFF, command=true]" }
Switch Person1Sensor1 (Person1Presence_Raw)
Switch Person1Sensor2 (Person1Presence_Raw)
Switch Person2Presence "Person1 Presence" (Presence)

Group:Switch:OR(ON,OFF) Person2Presence_Raw (Debounce) { debounce="Person2Presence[timeout='5m', states=OFF, command=true]" }
Switch Person2Sensor1 (Person2Presence_Raw)
Switch Person2Sensor2 (Person2Presence_Raw)

Rule

In MainUI navigate to Settings->Automation and browse through the list of rule templates to find Debounce. You many need to click “show all”. Add it.

Navigate to Rules and click the + icon to create a new rule. Enter reasonable information for the Unique ID, Name, and Description. Select “Debounce” from the list of templates. For the “Debounce Group” configuration parameter select “Debounce” from the list of Items. Click save.

This will create the Debounce rule that drives the five minute delay when someone leaves. No coding on your part is required.

Theory of Operation

Each sensor is a member of a Group. If any one of the sensors is ON the Group’s state will be ON. When the Group changes state to ON that state is immediately passed to the proxy Item by the Debounce rule. When the Group changes to OFF that state is held for five minutes before being passed to the proxy Item.

The two proxy Items, one for each person, is in turn made a member of the overall presence group along with an overrride switch.

Any time a sensor changes to ON, that ripples through the Groups immediately and applies. Only when all the sensors for a person turn OFF and remain OFF for five minutes will that person be marked as OFF. Only if all people (and the override) are OFF will the overall presence be marked as OFF. So five minutes after the last person leaves will Presence go to OFF.

The code after this point is deprecated and will be removed at some point after OH 3.2 is released.

Python

See [Deprecated] Design Pattern: Debounce which uses presence detection as an example and uses the debounce Python library to implement this such that the user need only add metadata to the Sensor Group Items.

Rules DSL

rule "Reset Presence and sensors to OFF on startup"
when
    System started
then
    Presence.sendCommand(OFF)
    Presence_Sensors.sendCommand(OFF)
end

rule "A presence sensor updated"
when
        Item Presence_Sensors changed
then

    if(Presence_Timer.state == ON && Presence_Sensors.state == Presence.state) {
        logInfo(logName, "Timer is running but group and proxy are the same, cancelling timer")
        Presence_Timer.postUpdate(OFF)
    }
    else if(Presence_Sensors.state == Presence.state) {
        logInfo(logName, "No timer and both group and proxy are the same, nothing to do")
        return;
    }

    if(Presence_Sensors.state == OFF) {
        logInfo(logName, "Everyone is away, setting anti-flapping timer")
        Presence_Timer.sendCommand(ON)
    }
    else if(Presence_Sensors.state == ON) {
        logInfo(logName, "Someone came home, setting presence to ON")
        Presence.sendCommand(ON)
    }

end

rule "Presence timer expired, no one is home"
when
    Item Presence_Timer received command OFF
then
    logInfo(logName, "Everyone is still away, setting presence to OFF")
    Presence.sendCommand(OFF)
end

Theory of Operation

There are three Items that work together to make this work.

  • Presence_Sensors: A Group of Switches configured so if all the Switches are OFF the Group’s state is OFF and if one or more is ON the Group’s state is ON.

  • Presence_Timer: A Switch that will receive a command OFF 5 minutes after changing to ON. If it gets updated to OFF, nothing happens, effectively cancelling the timer.

  • Presence: A proxy Switch that gets set to ON immediately when Presence_Sensors changes to ON and set to OFF only if Presence_Sensors remains OFF for 5 minutes or longer.

These three Items drive two Rules. The first Rule triggers on any change to Presence_Sensors.

First we check to see if there is a Timer running (i.e. Presence_Timer.state == ON) and whether Presence_Sensors and Presence have the same state. If they are the same (i.e. both OFF or both ON) then there is no longer a need for the Timer so we can cancel it. And since they are already the same state there is nothing else to do.

If there is no Timer and both are the same state then there is nothing at all to do so just return.

If we get to this point in the Rule we know that there is no Timer and that Presence_Sensors and Presence have are different states.

If Presence_Sensors is OFF then we know that Presence is not OFF so we need to create a Timer to turn it OFF in 5 minutes.

If Presence_Sensors is ON then we know we need to send a command ON to Presence immediately.

If there is a Timer but Presence_Sensors and Presence are different, do nothing and let the Timer take care of bringing them to the same state.

44 Likes

Off-topic: Is this binding working with OH 1.8? or is there an update for OH1.8 to OH1.9?
The most confusing bit about this forum is the mixing of OH1 and OH2.

You are right. The problem is, that OH2 is getting more and more popular and most postings nowadays are for OH2 but that’s not always stated. It is planned to rework the forum structure to be 2.0 by default in a few weeks to months. I hope the problem will not get worse in that time.

1 Like

tl;dr: It is a 1.9 binding which means it works in OH 1.8 and OH 2.0. The above code is running in OH 2.0 but should work in 1.8 unchanged.

I agree, it is confusing. We are in a state of transition right now but OH 2 is rapidly maturing to the point where it will soon become the default assumed version talked about.

In this particular case, there will never be an update of the core of OH 1.8 to version 1.9. However, most of the bindings and add-ons have continued development. These bindings and add-ons are referred to as version 1.9. You can (and probably should in some cases) run 1.9 version bindings in OH 1.8.3.

In OH 2 there is a new architecture and a new way for bindings and add-ons to work. There is also a compatibility layer which allows one to run older 1.x style bindings inside OH 2. As it stands right now as of this writing, there are more 1.9 bindings that are part of the official distribution than 2.0 bindings, including some really essential bindings like MQTT and HTTP. Though that is going to change soon.

In the Migration Tutorial I actually used an approach that lets one first swap out the core to OH 2 but remain on 1.9 version bindings. And then gradually transition to the 2.0 version of the bindings.

So in short, if it says version 1.9 it will work in 1.8.3 and 2.0.

So here are a few rules of thumb I would use to determine if something posted is applicable to OH 1.8.3 if the:

  • binding/add-on is documented on the wiki as opposed to http://docs.openhab.org
  • version of the binding or add-on starts with a 1, e.g. 1.9
  • example rule imports things from org.openhab it was written for 1.x

It only applies to OH 2.0 if it:

  • talks about PaperUI
  • references Things and/or Channels
  • references BasicUI or ClassicUI
  • references Karaf or the Karaf Console

I hope this helps.

2 Likes

Thank you very much for this clarification… I do appreciate it, and it makes the whole thing much clearer.

Looks interesting! How about also removing the Present_Timer item and adding the expire to the main Present switch?

The problem with that is most of my sensors only report when there is a change. Furthermore the group’s state doesn’t change very frequently. Therefore there isn’t an event to keep rescheduling the timer. You could add a rule that triggers every few minutes to refresh the timer by sending an ON if the sensors are still on, but if you do that you may as well implement the timer in that rule.

In the end, you could use Present as the timer so long as ON keeps getting sent to it while you are present every <n minutes where n is the timer length. That isn’t the case with the code above.

1 Like

Hi rick,
Thanks for that great tutorial.
I was wondering what kind of presence sensors you are using.

I have been struggling to the iPhone as presence detector and using the network binding. But this did not work reliably.

Remember to use the search function first :wink:

Hi Tom,
Thanks for the hint. I am on Windows.

I use a combo of sensors none of which am I particularly happy about their performance for detecting iPhones.

I use the Network binding to ping devices (work like a champ for Android).

I’ve recently implemented the hping3 sensor @ThomDietrich linked to which I’m finding works no better than Network. I’m not sure if I need to adjust the timing or something else but so far I’ve never seen the hping3 detect the device present when Network did not and visa versa. Of course, the hping3 may be making Network work better.

I’ve a Pi 3 running reelyActive. So far it it detects BTLE devices like a champ (my fitbit, wife’s Dexcom and Pebble watch) but it detects the iPhone and my Android very poorly. But this may be caused more because I can’t figure out which device is which so I might not have identified the IDs of the devices I care about. I do notice that every time someone walks by the house reelyActive picks them up. I’m going to watch it some more but this might be a secondary way to detect when someone comes to the door (package delivery). What would be great is if I could find some sort of way to map the BT address of a device, which I can discover on my own, with the reelyActive deviceID.

I’ve also three Pis and my “server” (old laptop) each outfitted with BT and I run a custom script which does something similar to reelyActive, only at a more primitive level, to detect the phones. This works reasonably well though because it uses a sort of moving average to determine presence it isn’t super responsive. Sometimes it also gets stuck reporting a device as ON when it is no longer present,

I’ve not spent a lot of time trying to figure out the problems I have with each of these. I’ve recently had something major go wrong in my office which has really messed up my electronics in strange and inexplicable ways. My monitor died (probably a failed capacitor), the BT on my laptop server can no longer be found, I can no longer access port 443 on the laptop server despite there being no IP tables rules block that port and netstat clearly showing that nginx is listening on that port for all addresses. All my network gear had to be reset (thank goodness for dd-wrt’s backup feature) and I had to remount all of my drives. I don’t know if it was a power surge or an EMP but needless to say, I currently have some bigger problems to figure out right now so I’ve all but disabled my presence detection.

Anyway, as for being on Windows, hping is available on Windows. It will probably require some powershell scripting to make it work but it is not impossible.

4 Likes

@Rick. Thanks and wonderful as always.

1 Like

I hope you can recover fully from it all… sounds weird. Good luck.

@rlkoshak Thanks for the mega tutorial, however i have a question, my phones (android, two different makes) keep being marked as “not connected” by the network binding, despite still being on wifi (and visible on my router as connected), they do then come back but they drop in and out all the time. Does my Thing file look like a valid use case or do i need to tweak something.

Thing network:device:JamesPhone [ hostname=“192.168.1.19”, port=“0”, retry=“1”, timeout=“5000”, refresh_interval=“60000”, use_system_ping=“false”, dhcplisten=“true” ]

Or is this just normal?

I had the same problem with Android phones running Android 6 and above. I think they stop responding to pings when in doze mode.

I use the arp-scan tool combined with the exec binding for detecting phones. For a Nexus 6p, Nexus 5x and Galaxy S5 this works perfectly and always detects the phones (I had to up the number of retries to get the Galaxy S5 to respond reliably)

I think this is normal. Hence the anti-flapping logic and the use of alternative sensors in my setup.

Yes it’s normal (from a technical stand point) but there are of course ways around that. @MikeJMajor refered to the following solution I suppose. It’s reported to be quite reliable between many devices, including Androids and iPhones in doze/deepsleep mode:

Not convinced! … or beg to differ…

I run this set-up and it does not switch to ‘not present’, due to ARP retaining the IP.
… even (two) days later…

I’ve not done enough experiments to prove it, but I think hoping combined with Network binding might be a viable approach. I noticed when I was playing with it that periodically hpinging the iPhone made the Network Item linked to its ip work more reliably.

Also, if I remember correctly doesn’t the script first clear the arp table before doing the hping?

Very inspiring. Will try to imitate.
Thanks!