Fine tuning of wifi presence detection

Hi guys,

we are using two smartphones and I have build some rules to detect presence. A rule for each device and a separate rule for a general presence like “anybody home”. Now I want to optimize this general presence rule to consider a short absence (some seconds when I bring the trash outside or stuff like that).

I thought of testing the presence, then waiting a couple of seconds (maybe 60) and then testing the presence again. And only if the second test is “positive”, the general switch is turned to OFF.

This is my current rule (without optimization):

rule "Niemand ist mehr zuhause" when Item Handy1 changed to OFF or Item Handy2 changed to OFF then if(Handy1.state == OFF && Handy2.state == OFF) { postUpdate(JemandAnwesend, OFF) sendBroadcastNotification("Niemand ist zuhause") } end

How do you guys handle this? Any suggestions for optimizing my rule?

Thanks!
Stefan

Hi,
there’s a nice example here. The group is used to test all the items related to presence, and in order to avoid “noise” in presence detection a time range is defined (5 min) before to confirm nobody is at home.
I’m using that rule for find if nobody is at home but I found out that the “coming home” rule was for me too slow to change the state.

I use a combination of Network Health Binding and the REST-API in conjunction with Locative. Smartphones are being detected through WiFi but due to iPhone’s energy saving feature this will not be used to indicate someone’s absence. This will be done through updating the presence item through a REST call issued by Locative which is actually drawing a virtual circle around your home which will allow you to put the rubbish out without getting absent. This works pretty well for my purposes.

Thomas

Here is my presence detection. It is based on Network Health and some BT sensors which report presence of a device via MQTT. Like you I have presence for me and my wife along with a general presence. It waits five minutes before it switches the general presence after all of the other presence detection Items go OFF. I also provide an override in case I have house guests.

NOTES:

  • I redacted IP addresses
  • The phone’s IP addresses are statically assigned by the router
  • The BT detection script I use is located here: https://github.com/rkoshak/sensorReporter
  • Pay attention to the group membership because most of the Items get rolled up into a Group and rules only deal with the Groups.
  • The g<Hostname>Sensors groups are defined in another file and are used to help me detect when one of my remote sensor devices (really Raspberry Pis with stuff wired to their GPIO and a BT dongle plugged in) come back online.

#Items:

Switch Present "Someone is Present" <present> // turns off 5 minutes after everyone leaves

Group:Switch:AND(OFF,ON) gPresent     "Present group" <present>
Group:Switch:AND(OFF,ON) gRichPresent "Rich Present" <present>
Group:Switch:AND(OFF,ON) gJennPresent "Jenn Present" <present>

Switch   S_V_RichPhoneIP   "Moto X PE Network [%s]"     <network>   (gRichPresent, gPresent)                  { nh="<IP>" }
Switch   S_V_RichChimeraBT "Moto X PE BT Chimera [%s]"  <bluetooth> (gRichPresent, gPresent, gChimeraSensors) { mqtt="<[mosquitto:presence_sensors/bluetooth/chimeraRich:state:default]" }
Switch   S_V_RichGarageaBT "Moto X PE Garage [%s]"      <bluetooth> (gRichPresent, gPresent, gCerberosSensors){ mqtt="<[mosquitto:presence_sensors/bluetooth/garageRich:state:default]" }
Switch   S_V_RichHydraBT   "Moto X PE Hydra [%s]"       <bluetooth> (gRichPresent, gPresent, gHydraSensors)   { mqtt="<[mosquitto:presence_sensors/bluetooth/hydraRich:state:default]" }

Switch  S_V_JennPhoneIP    "iPhone 6p Network [%s]"     <network>   (gJennPresent, gPresent)                  { nh="<IP>" }
Switch  S_V_JennChimeraBT  "iPhone 6p BT Chimera [%s]"  <bluetooth> (gJennPresent, gPresent, gChimeraSensors) { mqtt="<[mosquitto:presence_sensors/bluetooth/chimeraJenn:state:default]" }
Switch  S_V_JennGarageBT   "iPhone 6p BT Garage [%s]"   <bluetooth> (gJennPresent, gPresent, gCerberosSensors){ mqtt="<[mosquitto:presence_sensors/bluetooth/garageJenn:state:default]" }
Switch  S_V_JennHydraBT    "iPhone 6p BT Hydra [%s]"    <bluetooth> (gJennPresent, gPresent, gHydraSensors)   { mqtt="<[mosquitto:presence_sensors/bluetooth/hydraJenn:state:default]" }

Switch S_V_PresenceOverride "Override Presence Detection [%s]" <network>

#Rules:

import org.openhab.model.script.actions.*

val String logNamePresence = "presence"
var Timer presenceTimer = null

rule "gPresent changed"
when
    Item gPresent changed
then

        if(S_V_PresenceOverride.state != ON) {
                // Someone came home
                if(gPresent.state == ON && Present.state != ON) {
                        logInfo(logNamePresence, "Someone is home")
                        if(presenceTimer != null && !presenceTimer.hasTerminated) {
                                presenceTimer.cancel
                        }
                        presenceTimer = null
                        Present.sendCommand(ON)
                }

                // everyone left
                else if(gPresent.state == OFF && Present.state != OFF) {
                        logInfo(logNamePresence, "No one is home, setting timer")
                        if(presenceTimer == null) {
                                presenceTimer = createTimer(now.plusMinutes(5), [|
                                        if(gPresent.state == OFF) {
                                                logInfo(logNamePresence, "No one is home after five minutes, setting Present to OFF")
                                                if(Present.state != OFF) Present.sendCommand(OFF)
                                        }
                                        else {
                                                logInfo(logNamePresence, "Someone came home before the five minutes were up")
                                                if(Present.state != ON) Present.sendCommand(ON)
                                        }
                                        presenceTimer = null
                                ])
                        }
                }
        }
        else {
                logInfo(logNamePresence, "Presence detection is overridden")
                Present.sendCommand(ON)
        }
end

rule "Update Present to ON when Override is turned ON"
when
        Item S_V_PresenceOverride received command ON
then
        logInfo(logNamePresence, "Overriding presence detection")
        Present.sendCommand(ON)
end

#Theory of Operation:
There is one Switch for each of the sensors that can detect the presence of me or my wife. The states of these Switches are rolled up into a Group. Similarly the states for all these Switches are rolled up into a gPresent group. If any switch reports as ON then the groups it is a member of will also report as ON. Only if all the switches go OFF will gPresent go OFF.

The main thing the rules do is add a delay of five minutes before setting the Present switch to OFF after gPresent switches to OFF. If it goes back to ON before the five minutes are up Present remains ON.

Of course, if the override switch is ON, Present remains ON and is never allowed to go OFF. (It just occurred to me that I could simplify things a lot if I add the Override switch to the gPresent Group which would eliminate the “Update Present to ON when Override is turned ON” rule and simplify the other one but then I would lose the logging statements, need to decide how much I care about that.).

In rules that care about whether or not someone is home (e.g. setting Nest to Away) it only checks the Present state. However, for things like getting an alert when everyone leaves but the door was left open the rules check the gPresent state (it does me no good to find out I left the backdoor open five minutes after I left).

2 Likes

Just for completeness, if I add S_V_PresenceOverride to the gPresent group the rules reduce to the following:

import org.openhab.model.script.actions.*

val String logNamePresence = "presence"
var Timer presenceTimer = null

rule "gPresent changed"
when
    Item gPresent changed
then

        // Someone came home
        if(gPresent.state == ON && Present.state != ON) {
                logInfo(logNamePresence, "Someone came home")
                if(presenceTimer != null && !presenceTimer.hasTerminated) presenceTimer.cancel
                presenceTimer = null
                Present.sendCommand(ON)
        }

        // everyone left
        else if(gPresent.state == OFF && Present.state != OFF) {
                logInfo(logNamePresence, "No one is home, setting timer")
                if(presenceTimer == null) {
                        presenceTimer = createTimer(now.plusMinutes(5), [|
                                if(gPresent.state == OFF && Present.state != OFF) {
                                        logInfo(logNamePresence, "No one is home after five minutes, setting Present to OFF")
                                        Present.sendCommand(OFF)
                                }
                                presenceTimer = null
                        ])
                }
        }
end

Thank you folks for your ideas!

@rlkoshak: Rich, your approach is truly extensive (and complex…). But I haven’t expected anything other than that from you! :slight_smile:

Until now I thought that the NH binding could not be used with iPhones because of the deep sleep mode. But your item definitions indicate an iPhone. Do you get reliable results with NH? At the moment I am using the hping3 approach mentioned in another thread to check iPhone presence. I abandoned my BT check some days ago because BT range of my RPi3 hasn’t covered the whole building (wifi indeed does…), so I found this somehow redundant. At the moment I am only using wifi.

Why do you rely on both methods?

@Penrose: I am no big friend of geofencing (I think Locative is such a solution) to check presence. Either it reduces the battery capacity meaningful or it needs an app to be running on my iPhone. It has to work without that, doesn’t it Rich? :wink:

Because, as you said, NH doesn’t work reliably on iPhones. And I have several Pis about the house so I get good coverage with BT. And even the BT detection isn’t super reliable on my Pis with an old BT dongle. By using more than one approach and the five minute delay before fully going into away mode it is very reliable.

It really isn’t as complex as it looks. The rule (particularly the reduced version) is pretty much just setting a timer. The main complexity is in setting up the Groups to roll up the multiple detection sensors.

You are right! After diving into your rules, it isn’t as complex as it seems at first. As I do not have multiple Pis in my house, I stay with not using BT at the moment. Let’s see how reliable wifi detection is…Also I am not using the override feature. It might be very useful, but I don’t have a use case at the moment.

Thank you very much Rich! I adapted your rule for my installation and everything seems to work for now.

@Penrose how did you configure locative to post something to the openhab rest api? I tried post on https://:8080/rest/items/TestLocation with basic authentication enabled but it’s not working. The item’s state never changes. Do you also use https and basic authentication?

Just as an alternative approach I used a debounce timer to smooth things out a little. My boyfriend has an iPhone, and I have an Android, so for me presence detection was simple, for him not so much. The iPhone power saving feature turns off wifi temporarily when the screen is off but it comes back on for a few minutes every now and then to reconnect, update network traffic, and then goes back to sleep.

So I have a switch attached to the network health binding for each phone, which updates at about 5 seconds. This works great for my phone. For his phone I have the same but I set up a rule that watches to see if it goes off the network, which it does every time the screen turns off. Then it starts a timer for (as of now) 15 minutes. During those 15 minutes, if the phone comes back it stops the timer. If after those 15 minutes the phone has not responded it’s safe to assume he’s not there and his presence is turned off. I set the timer for my phone to 5 minutes and I will shorten his as I experiment but I have to say it works pretty well. There’s an obvious delay when I leave the house until it knows I’m actually gone but it allows me to walk outside or lose network connection and have time to get back to network before everything shuts down.

I plan on using IFTTT for my phone for redundancy but for his phone he doesn’t want any other apps on his phone so this is the best way for me to solve it. Not perfect but works pretty darn well. I just have to experiment to see how often the iPhone reconnects to update network traffic and I can shorten that timer. See the code I wrote:

rule "Josh's Phone Presence Update"
when
	Item JoshiPhonePresent changed
then
	if (JoshiPhonePresent.state == ON){
		if (JoshPhoneTimer != null) JoshPhoneTimer.cancel()
		if (JoshPresence.state != ON){
			
			JoshPresence.sendCommand(ON) 
		}
	}
	else{
		JoshPhoneTimer = createTimer(now.plusMinutes(15))[|
			
			JoshPresence.sendCommand(OFF)
		]
	} 
end

rule "Matt's Phone Presence Update"
when
	Item MattDroidPresent changed
then
	if (MattDroidPresent.state == ON){
		if (MattPhoneTimer != null) MattPhoneTimer.cancel()
		if (MattPresent.state != ON){
			
			MattPresent.sendCommand(ON) 
		}
	}
	else{
		MattPhoneTimer = createTimer(now.plusMinutes(5))[|
			
			MattPresent.sendCommand(OFF)
		]
	} 
end

If anyone wants to use it it’s just another option for those who don’t want to install extra apps.

Hope it helps someone!

Matt

I configured it as described in REST-API on the wiki so no real magic. Yes, I use basic authentication.
So if the item doesn’t change most likely the REST call doesn’t come through. Do you get an error message from Locative? Have you tried manually?

Thank you for your reply! However, I solved it in the meantime via a php script in-between.

hi @rlkoshak,

I am facing issue when write the rule for presence detection,
I set 2 items for each phone, 1 for final state and 1 for proxy
Ham_iphone & Ham_iphone_Proxy, so item state update to proxy item first

I try to use below code but found I don’t know how to write code that I want to post update to main item

rule "notification when home or away"
when
	Item gPrsents2 changed
then
	//log:tail .eclipse.smarthome.model.script.iPhone_Detect_DEBUG
	Thread::sleep(100)
	val iphoneProxy = gPrsents2.members.sortBy[lastUpdate].last as SwitchItem
	val iphoneN = iphoneProxy.name.split("_")
	val iphoneNS = (iphone2.get(0) + "_" + iphone2.get(1))
	if (iphoneProxy.state==ON){
		if (timers.get(iphoneProxy.name) != null) {
			timers.put(iphoneProxy.name, null)
			logInfo("iPhone_Detect_DEBUG", iphone2.get(0) + " back online Clear Timer")}
		if (iphoneProxy.state!=ON){
			if(timers.get(iphoneProxy.name) == null){
			iphoneNS.sendCommand(ON)
			sendBroadcastNotification(iphone2.get(0)  + " just back home")
			}
		}
	}
	else{
		timers.put(iphoneProxy.name, createTimer(now.plusMinutes(3), [|			
			iphoneNS.sendCommand(OFF)
			sendBroadcastNotification(iphone.label + " leave home")
			logInfo("iPhone_Detect_DEBUG", iphone2.get(0)  + " Offline)
		]))
	} 
end

I got error like below

07:29:01.724 [ERROR] [.script.engine.ScriptExecutionThread] - Rule 'notification when home or away': An error occured during the script execution: Could not invoke method: org.eclipse.smarthome.model.script.actions.BusEvent.sendCommand(java.lang.String,java.lang.String) on instance: null

If I set

val iphoneNS = (iphone2.get(0) + "_" + iphone2.get(1))  as SwitchItem

I got below error

07:30:46.880 [ERROR] [.script.engine.ScriptExecutionThread] - Rule 'Sharp TV status': org.eclipse.smarthome.core.library.items.SwitchItem

Can you tell me how should I ask the rules to auto post update on main by group?

Could please provide one example using the rest api to change your presence switch.
I am reading the wiki but only finding examples to read items but not to write.

for some reasons the example given in the Wiki
http://192.168.1.1:8080/CMD?My_Switch=OFF

does not work (I changed of course the item and server name)

Ideally an example should be giving using myopenhab to be able to report location when not being in the local wifi.

Also note I am on OH2.

You can access your own REST API with:

http://YOUR-IP:8080/doc/index.html

Then go down to items and show all items options.

There you find a PUT /items/{itemname}/state for the update of an item state. You can generate your own curl command for each item you have.

For example, when I want to switch my item “TestStefan” to OFF, this is my curl command:

curl -X PUT --header "Content-Type: text/plain" --header "Accept: application/json" -d "OFF" "http://MY-IP:8080/rest/items/TestStefan/state"

I am using this command in a script for my presence detection.

Hi Stefan,

Thanks for this information. I was aware of the curl syntax.
But I was wondering what to enter in the respective fields in the above mentioned “Locative” phone app.
It tried with the curl syntax but failed.

regards

Martin

Sorry then…my misunderstanding!

I don’t know (or use) the Locative app, so I can’t be of any further help.

On reviewing the code I see the following error. iphoneNS is a String, not an Item. You can’t send a command to a String. You cannot simply cast a String to a SwitchItem. However, you can change your iphoneNS.sendCommand(ON) and `iphoneNS.sendCommand(OFF) lines to use the sendCommand Actions (this is the one place where I do recommend using the Actions).

sendCommand(iphoneNS, ON)

Assuming you are running on OH 2 you can install the REST Documentation (PaperUI → Add Ons → Misc → REST Documentation) which includes a fully interactive documentation with examples and a place to test out your REST calls. The REST API will show up as an UI when you go to your index page.

That example only works with OH 1.x and it was never really officially supported. To send a command to an Item through the REST API you must use an HTTP POST command, not an HTTP GET command.

The only change is you replace http://address of OH server:8080 with https://user:password@myopenhab.org.

where user and password are your login credentials for myopenhab.org.

Only if it was installed. If one chooses Expert or Minimal the REST Documentation does not get installed.

Thank you rich,
still working on this script, is there any way to cast a string as a switch/string/number items or it can’t be done even the name is correct?

as I read many of your sample sample rules, you usually catch items under group,
so we can’t do multi actions on a related group items? only be a preset value XXxxXX.sendCommand(ON/OFF)?