Huge thanks out to Martin Harizanov (https://harizanov.com) I don’t know the man but sifting through his very informative blog has allowed me to get this code (and many others) up and running. You can find his original post on the subject here https://harizanov.com/2014/03/presence-detection-using-phones-wifi-and-node-red/
This is basically his work with a few tweaks. I decided to send all my sensor logs to my home servers MySQL db. The RPi uses an SDCard that has limited write life, and I think there are some performance issues there too (I don’t know, never bothered to look it up, I’m talking out my ass). I have a home server that runs MySQL and Apache so I was going to just offload that work away from the RPi anyway.
To get the presence detecting done I ping for my phone every 30 seconds or so then that result goes to a function to get analyzed (by Mr. Harizanov’s code), then that result is given to another function that UPDATES a MySQL database then finally on to a MySQL node to make the connection.
Ping -> Analyze Ping Function -> Update MySQL Function -> Connect to MySQL Server
I noticed that it took about 2-4 ping results before it would change status, and that it didn’t matter if it was a 30 second or 5 minute delay. So I went with 30 seconds, it doesn’t hammer the Pi too bad and I don’t have to wait too long to test it, hah. I did not thoroughly test this theory, I just noticed it once or twice so I could be way off.
Hopefully this helps someone else in their time of need. One is glad to be of service.
Presence Detection
If you DO NOT have or use a cellphone (or a device that uses WiFi that goes with you everywhere) then this WILL NOT WORK for you.
It works by utilizing the fact that almost everyone has a cellphone and that they usually always have it with them. The idea is simple, Node-RED looks for your cellphone. If it can see your phone it assumes you are home along with it. How do we see your phone? Ping it. Basically ping the IP address of your phone every so often and it the ping comes back you are home, if it times out you are assumed away.
You will need
node-red-node-ping installed for this to work.
(http://flows.nodered.org/node/node-red-node-ping)
Nodes Required
- Ping Node http://flows.nodered.org/node/node-red-node-ping
- Function (Analyze ping results)
- Function (Insert data payload into MySQL db)
- MySQL Node to make the connection http://flows.nodered.org/node/node-red-node-mysql
You may want to add a debug node after the Update DB function and two inject nodes before the Update DB function, set as 1 and 0 to simulate your phone home/away.
Ping Node
This one is simple, just add the IP of your cellphone and set the timer. I used 30 seconds, although I will probably change this to 5 minutes. It helps to have control over your router so you can set static IPs.
Function Node (Analyze Ping Result)
Don’t forget the global variable, this is needed in the next step.
|
// anything stored in context is kept available for next time we get called context.gotping = context.gotping || 1; context.pingfails = context.pingfails || 0; if(msg.payload) { context.gotping = 1; context.pingfails = 0; } else { context.pingfails +=1; if(context.pingfails>3) context.gotping=0; } msg.payload=context.gotping; // set global variable context.global.variable1 = msg.payload; return msg; |
Function (Update MySQL db)
You will need to change the db settings to match yours of course
|
data = (msg.payload); time = new Date().toString(); if (data == 1) { var msg = { // topic : "INSERT INTO `mydb`.`mytable` (`one`, `two`) VALUES (NULL, '" + data + "');" topic : "UPDATE presence SET home = '" + data + "', lastin = '" + time + "' WHERE presenceid = '28';" }; } else { var msg = { // topic : "INSERT INTO `mydb`.`mytable` (`one`, `two`) VALUES (NULL, '" + data + "');" topic : "UPDATE presence SET home = '" + data + "', lastout = '" + time + "' WHERE presenceid = '28';" }; } msg.payload = data; return msg; |
Connect to MySQL node
This is pretty straight forward, setup with your servers credentials
That should get your Node-RED to detect if you are home or not based off your cellphones Wifi. Pretty cool, yeah but what do you do with that info? Log it? Display? Yes! Take the info from my last post and apply it here and we can check the status any time we want! http://itsalllost.com/node-red-requesting-sensor-data-via-http/
Here’s the code to check the status of the global variables (make sure they all match!)
|
[{"id":"d42812f2.2bd7f","type":"http in","z":"df14cbe6.20eb38","name":"","url":"/whoishome","method":"get","swaggerDoc":"","x":197.1428680419922,"y":887.1428833007812,"wires":[["ab5eccd2.54a13"]]},{"id":"4b806cd2.b47f94","type":"http response","z":"df14cbe6.20eb38","name":"Display the Page","x":867.6428680419922,"y":887.1428833007812,"wires":[]},{"id":"ab5eccd2.54a13","type":"function","z":"df14cbe6.20eb38","name":"Get Current Values","func":"msg.current = {\n variable1: context.global.variable1,\n variable2: context.global.variable2,\n variable3: context.global.variable3\n}\nreturn msg;","outputs":1,"noerr":0,"x":420.1428680419922,"y":887.1428833007812,"wires":[["6b295f84.94d6a"]]},{"id":"6b295f84.94d6a","type":"template","z":"df14cbe6.20eb38","name":"HTTP","field":"payload","format":"handlebars","template":"<html><head></head>\n<body>\n <form method=\"POST\" action=\"/homesubmit\">\n <p>Is variable1 home? {{current.variable1}}</p>\n <p>Is variable2 home? {{current.variable2}}</p>\n <p>Is variable3 home? {{current.variable3}}</p>\n <p><input type=\"submit\" value=\"Refresh\"></p>\n \n </form>\n \n</body>\n\n</html>","x":657.6428680419922,"y":887.1428833007812,"wires":[["4b806cd2.b47f94"]]},{"id":"70c3e0ae.8f3c2","type":"http in","z":"df14cbe6.20eb38","name":"Refresh Page Button","url":"/homesubmit","method":"post","swaggerDoc":"","x":208.1428680419922,"y":942.1428833007812,"wires":[["e3453d3f.1cbac"]]},{"id":"e3453d3f.1cbac","type":"function","z":"df14cbe6.20eb38","name":"Redirect back to /whoishome","func":"msg.res.redirect(\"/whoishome\");\n","outputs":"0","noerr":0,"x":819.1428680419922,"y":942.1428833007812,"wires":[]}] |
Here is the flow for presence detection in all it’s glory!
|
[{"id":"2abd1213.d542ee","type":"MySQLdatabase","z":"df14cbe6.20eb38","host":"10.0.0.1","port":"3306","db":"nodered-home","tz":""},{"id":"73794084.8c86c","type":"ping","z":"df14cbe6.20eb38","name":"Ping Justins Phone","host":"10.0.0.101","timer":"30","x":238.57142639160156,"y":210,"wires":[["f1163e28.0ee9c"]]},{"id":"f1163e28.0ee9c","type":"function","z":"df14cbe6.20eb38","name":"Analyze Result","func":"// anything stored in context is kept available for next time we get called\ncontext.gotping = context.gotping || 1;\ncontext.pingfails = context.pingfails || 0;\n\nif(msg.payload) {\n context.gotping = 1;\n context.pingfails = 0;\n} else {\n context.pingfails +=1;\n if(context.pingfails>3) context.gotping=0;\n}\n\nmsg.payload=context.gotping;\ncontext.global.justinhome = msg.payload;\nreturn msg;","outputs":1,"noerr":0,"x":479.35717010498047,"y":210.3213987350464,"wires":[["8de3658b.721c98"]]},{"id":"463b925.fb9c46c","type":"mysql","z":"df14cbe6.20eb38","mydb":"2abd1213.d542ee","name":"Connect to NodeRED-Home DB","x":940.2500267028809,"y":211.89287567138672,"wires":[[]]},{"id":"280cd3ce.d7f32c","type":"debug","z":"df14cbe6.20eb38","name":"","active":false,"console":"false","complete":"false","x":883.0356254577637,"y":259.71435546875,"wires":[]},{"id":"c4fab819.3b0548","type":"inject","z":"df14cbe6.20eb38","name":"Home","topic":"","payload":"1","payloadType":"string","repeat":"","crontab":"","once":false,"x":498.5714225769043,"y":252.85713958740234,"wires":[["8de3658b.721c98"]]},{"id":"222fa8c7.ddd058","type":"inject","z":"df14cbe6.20eb38","name":"Away","topic":"","payload":"0","payloadType":"string","repeat":"","crontab":"","once":false,"x":497.1428527832031,"y":290,"wires":[["8de3658b.721c98"]]},{"id":"8de3658b.721c98","type":"function","z":"df14cbe6.20eb38","name":"Update DB","func":"data = (msg.payload);\ntime = new Date().toString();\nif (data == 1) {\n var msg = {\n // topic : \"INSERT INTO `mydb`.`mytable` (`one`, `two`) VALUES (NULL, '\" + data + \"');\" \n topic : \"UPDATE presence SET home = '\" + data + \"', lastin = '\" + time + \"' WHERE presenceid = '26';\" \n };\n} else {\n var msg = {\n // topic : \"INSERT INTO `mydb`.`mytable` (`one`, `two`) VALUES (NULL, '\" + data + \"');\" \n topic : \"UPDATE presence SET home = '\" + data + \"', lastout = '\" + time + \"' WHERE presenceid = '26';\" \n };\n}\nmsg.payload = data;\nreturn msg;","outputs":1,"noerr":0,"x":681.4285888671875,"y":211.42857360839844,"wires":[["463b925.fb9c46c","280cd3ce.d7f32c"]]}] |