How to watch devices availability in a network


#1

Devices like Raspberry pi, ESP8266, camera, etc. are often used in a LAN (local area network). Using more "ip devices" rises the need to watch the connection.

Here is a general solution based on the nodes node-red-node-ping and node-red-contrib-dsm:
ping

Please, go to the dsm wiki for more details: ping-notification


#2

tried it - getting this

.201 is my android phone - I'm just sitting at my desk


#3

Which dsm version are you using?
The best is to install the latest one anyway.


#4

Works fine on my home network monitoring a couple of Wemos D1 Minis.


#5

My experience is that pinging Android phone doesn't work.
To save energy Android will switch of the wifi most of the time. It will wake up after x times to check the mail etc. and switches back of the wifi.
Because of this you can't ping the phone.
There's a setting to keep always your Android wifi on, but this will reduce your battery life.
I'm using myself node-red-contrib-fritz to see what phones there are online, but for that you need a Fritzbox router.
For the other devices in the network like nas, raspberry etc piniging works fine.


#6

That's true of all mobile devices. There have been lots of threads over the last few years on this subject.

I make sure that all of my sensor platforms (ESP8266) should be sending out to MQTT an DEVICES/<deviceid> topic with a payload of Online about every 50 seconds (just under a minute). I use a LWT to mark the device Offline if the broker hasn't received anything in 60 seconds.

For devices like SONOFF which might not do something similar or those that don't do MQTT, I use Node-RED to monitor incoming MQTT messages for the devices and do the DEVICES/<deviceid> On/Offline message there.

That way I can always see what is on and offline. I now display that on my new dashboard too.

Not quite got the NAS, router and WiFi AP in that display yet, they are on my old dashboard, I'll migrate them soon. The locations are obtained from a standard device/location map variable in Node-RED.


#7

All of which, whilst perfectly valid, is not the cause of the errors that @cymplecy is seeing.


#8

It might be best to check which version of DSM you are using, as @Cflurin suggested.


#9

No errors after updating to latest but as everyone has said - it won't be useful to track if phone is in the house :frowning:

I'll just strap a wemos d1 mini to the back of phone with a big battery :slight_smile:


#10

Smartphones are an edge case and ping would not help.

The question is why want you to know whether your smartphone is connected?
To determine the presence of a person is one reason.

But I only use my iPhone to access the dashboard and have the pushover app installed to receive notifications not only at home but also when I'm away.


#11

I use the arp contrib node. Even if a device doesn't respond to pings, it'll show up in the ARP cache.


#12

Yes - that was the reason for choosing my phone.


#13

arp node not working for me

I'm on win 10 and arp- n isn't a valid command when I run it from cmd terminal so that prob explains why


#14

Haven't tested on a windows system. You might be able to use an exec node to run arp -a and parse the results. Don't currently have a windows install to throw together a test flow on at the moment, though.

Also what kind of networking hardware are you using? Might be able to get better presence indicators off of your router than another client. (The unifi node is pretty slick too, but it would be stupid to assume people would be willing to fork out cash for that level of gear in most places.)


#15

Here is another way to realize device monitoring (uses node-red-configurable-ping):

[{"id":"c4336609.fe4b48","type":"inject","z":"b473c044.f9da7","name":"init","topic":"config","payload":"","payloadType":"date","repeat":"","crontab":"","once":true,"onceDelay":"0.4","x":215,"y":829,"wires":[["3e8bb7dd.009148"]]},{"id":"3e8bb7dd.009148","type":"change","z":"b473c044.f9da7","name":"config","rules":[{"t":"set","p":"payload","pt":"msg","to":"[{\"name\":\"TR Puffer1 FriWa\",\"loc\":\"KG_TR\",\"system\":\"heating\",\"device\":\"tr1wemos\",\"address\":\"tr1.lan\",\"check\":\"ping\",\"interval\":300,\"errormsg\":\"Error unreachable! Ger├Ąt: '@dev@', Name: '@name@', Adr: '@adr@' antwortet nicht\",\"errortype\":\"err_unreach\"},{\"name\":\"TR Puffer 2 Solar\",\"loc\":\"KG_TR\",\"system\":\"heating\",\"device\":\"tr4wemos\",\"address\":\"tr4.lan\",\"check\":\"ping\",\"interval\":300,\"errormsg\":\"Error unreachable! Ger├Ąt: '@dev@', Name: '@name@', Adr: '@adr@' antwortet nicht\",\"errortype\":\"err_unreach\"},{\"name\":\"TR Deltasol\",\"loc\":\"KG_TR\",\"system\":\"deltasol\",\"device\":\"tr5wemos\",\"address\":\"tr5.lan\",\"check\":\"ping\",\"interval\":300,\"errormsg\":\"Error unreachable! Ger├Ąt: '@dev@', Name: '@name@', Adr: '@adr@' antwortet nicht\",\"errortype\":\"err_unreach\"},{\"name\":\"OG Xiaomi Gateway\",\"loc\":\"OG_WZ\",\"system\":\"xiaomi\",\"device\":\"xiaomi_og\",\"address\":\"xiaomiog.lan\",\"check\":\"ping\",\"interval\":1000,\"errormsg\":\"Error unreachable! Ger├Ąt: '@dev@', Name: '@name@', Adr: '@adr@' antwortet nicht\",\"errortype\":\"err_unreach\"},{\"name\":\"KG Xiaomi Gateway\",\"loc\":\"KG_VR\",\"system\":\"heating\",\"device\":\"xiaomi_kg\",\"address\":\"xiaomikg.lan\",\"check\":\"ping\",\"interval\":1000,\"errormsg\":\"Error unreachable! Ger├Ąt: '@dev@', Name: '@name@', Adr: '@adr@' antwortet nicht\",\"errortype\":\"err_unreach\"},{\"name\":\"CCU\",\"loc\":\"EG_KZ\",\"system\":\"ccu\",\"device\":\"ccu\",\"address\":\"ccu.lan\",\"check\":\"ping\",\"interval\":120,\"errormsg\":\"Error unreachable! Ger├Ąt: '@dev@', Name: '@name@', Adr: '@adr@' antwortet nicht\",\"errortype\":\"err_unreach\"},{\"name\":\"Opi Strom\",\"loc\":\"KG_VR\",\"system\":\"strom\",\"device\":\"opi0_strom\",\"address\":\"192.168.4.208\",\"check\":\"ping\",\"interval\":120,\"errormsg\":\"Error unreachable! Ger├Ąt: '@dev@', Name: '@name@', Adr: '@adr@' antwortet nicht\",\"errortype\":\"err_unreach\"},{\"name\":\"Gefrierschrank OG\",\"loc\":\"OG_SPEIS\",\"system\":\"kueche\",\"device\":\"ht4\",\"address\":\"/temperature/kueche/ht/4\",\"check\":\"temperature\",\"limitmax\":-14,\"limitmin\":-99,\"interval\":7200,\"errormsg\":\"Error temperature! Ger├Ąt: '@dev@', Name: '@name@', Adr: '@adr@' ├╝berschreitet Temperatur Limit (@val@)\",\"errortype\":\"err_templimit\"},{\"name\":\"Gefriertruhe KG\",\"loc\":\"KG_GARAGE\",\"system\":\"kueche\",\"device\":\"ht1\",\"address\":\"/temperature/kg_garage/ht/1\",\"check\":\"temperature\",\"limitmax\":-11,\"limitmin\":-99,\"interval\":7200,\"errormsg\":\"Error temperature! Ger├Ąt: '@dev@', Name: '@name@', Adr: '@adr@' ├╝berschreitet Temperatur Limit (@val@)\",\"errortype\":\"err_templimit\"},{\"name\":\"Gefrierschrank OG\",\"loc\":\"OG_SPEIS\",\"system\":\"kueche\",\"device\":\"ht4\",\"address\":\"/temperature/kueche/ht/4\",\"check\":\"nodata\",\"interval\":7200,\"errormsg\":\"Error no_data! Ger├Ąt: '@dev@', Name: '@name@', Adr: '@adr@' sendet keine Daten\",\"errortype\":\"err_nodata\"},{\"name\":\"Gefriertruhe KG\",\"loc\":\"KG_GARAGE\",\"system\":\"kueche\",\"device\":\"ht1\",\"address\":\"/temperature/kg_garage/ht/1\",\"check\":\"nodata\",\"interval\":7200,\"errormsg\":\"Error no_data! Ger├Ąt: '@dev@', Name: '@name@', Adr: '@adr@' sendet keine Daten\",\"errortype\":\"err_nodata\"},{\"name\":\"Opi Strom\",\"loc\":\"KG_VR\",\"system\":\"strom\",\"device\":\"opi0_strom\",\"address\":\"sensor/status/power/power/eme/balance\",\"check\":\"nodata\",\"interval\":600,\"errormsg\":\"Error no_data! Ger├Ąt: '@dev@', Name: '@name@', Adr: '@adr@' sendet keine Daten\",\"errortype\":\"err_nodata\",\"erroraction\":\"reboot_opistrom\"},{\"name\":\"Batterie alle Xiaomi\",\"loc\":\"undef\",\"system\":\"xiaomi\",\"device\":\"undef\",\"address\":\"/status/batterylevel/\",\"check\":\"battery\",\"limitmax\":99,\"limitmin\":0.3,\"errormsg\":\"Error battery_low! Ger├Ąt: '@dev@', Name: '@name@', Adr: '@adr@' hat eine schwache Batterie (@val@).\",\"errortype\":\"err_lowbat\"}]","tot":"json"},{"t":"set","p":"priority","pt":"msg","to":"0","tot":"num"}],"action":"","property":"","from":"","to":"","reg":false,"x":240,"y":862,"wires":[["2182c4b9.1e528c"]]},{"id":"2182c4b9.1e528c","type":"split","z":"b473c044.f9da7","name":"","splt":"\\n","spltType":"str","arraySplt":1,"arraySpltType":"len","stream":false,"addname":"","x":269,"y":894,"wires":[["f118f760.1034d8"]]},{"id":"f118f760.1034d8","type":"change","z":"b473c044.f9da7","name":"prep cfg","rules":[{"t":"set","p":"host","pt":"msg","to":"payload.address","tot":"msg"},{"t":"set","p":"errormsg","pt":"msg","to":"payload.errormsg","tot":"msg"},{"t":"change","p":"errormsg","pt":"msg","from":"@name@","fromt":"str","to":"payload.name","tot":"msg"},{"t":"change","p":"errormsg","pt":"msg","from":"@adr@","fromt":"str","to":"payload.address","tot":"msg"},{"t":"change","p":"errormsg","pt":"msg","from":"@dev@","fromt":"str","to":"payload.device","tot":"msg"},{"t":"set","p":"topic","pt":"msg","to":"payload.name","tot":"msg"},{"t":"set","p":"resend_interval","pt":"msg","to":"payload.interval","tot":"msg"},{"t":"set","p":"config","pt":"msg","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":307,"y":927,"wires":[["ea5e4bcd.36aa08"]]},{"id":"ea5e4bcd.36aa08","type":"switch","z":"b473c044.f9da7","name":"","property":"payload.check","propertyType":"msg","rules":[{"t":"eq","v":"ping","vt":"str"},{"t":"eq","v":"temperature","vt":"str"},{"t":"eq","v":"battery","vt":"str"},{"t":"eq","v":"nodata","vt":"str"}],"checkall":"true","repair":false,"outputs":4,"x":422,"y":985,"wires":[["60ca0951.8cab48"],["7f31b465.514b4c"],["7f31b465.514b4c"],[]],"outputLabels":["ping","temperature","battery",null]},{"id":"60ca0951.8cab48","type":"delay","z":"b473c044.f9da7","name":"","pauseType":"rate","timeout":"20","timeoutUnits":"seconds","rate":"1","nbRateUnits":"12","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"x":590,"y":960,"wires":[["4b78ed9d.ebe954"]]},{"id":"7f31b465.514b4c","type":"change","z":"b473c044.f9da7","name":"","rules":[{"t":"set","p":"topic","pt":"msg","to":"config.address","tot":"msg"},{"t":"set","p":"priority","pt":"msg","to":"0","tot":"num"}],"action":"","property":"","from":"","to":"","reg":false,"x":611,"y":999,"wires":[["297a66a7.cad58a"]]},{"id":"4b78ed9d.ebe954","type":"function","z":"b473c044.f9da7","name":"Resend v2","func":"//resends msgs based on topic every x seconds (msg.resend_interval)\n\nvar interval = 2000; //default interval in ms\nvar timers = context.get(\"timers\")||{};\nvar send_first = true;\nvar resend_count = -1;   //resend count - set to -1 for infinite\n\n//-------------------------------------\nvar first = false;\n\n\nif (typeof msg.resend_interval != \"undefined\") \n    interval = msg.resend_interval * 1000;\nif (typeof msg.clear_all != \"undefined\") {\n    for (var t in timers) {\n        if (timers.hasOwnProperty(t)) {\n            clearInterval(timers[t].tid);        \n        }\n    }\n    return null;\n}\n\nvar tob = timers[msg.topic]||{};\nif (tob) {\n    clearInterval(tob.tid);\n    tob.count = resend_count;\n} else {\n    first = true;\n}\n\nif (typeof msg.clear != \"undefined\") {\n    return null;\n}\n\ntob.tid = setInterval(function() {\n        var tob_ = timers[msg.topic];\n        if (tob.count > 0 || tob.count < 0) {\n            node.send(msg);\n            if (tob.count > -1) {\n                tob.count--;\n                timers[msg.topic] = tob;\n                context.set(\"timers\",timers);\n            }\n        } else {\n            clearInterval(tob.tid);\n        }\n    }, interval);  \n\ntob.msg = RED.util.cloneMessage(msg);\ntimers[msg.topic] = tob;\ncontext.set(\"timers\",timers);\n\nif (first)\n    return msg;\nelse\n    return null;\n\n","outputs":1,"noerr":0,"x":756,"y":916,"wires":[["f533a638.3f6dd8"]]},{"id":"f533a638.3f6dd8","type":"conf ping","z":"b473c044.f9da7","name":"ping","host":"","timeout":"5","requests":"1","x":830,"y":868,"wires":[["57a0d660.fb21c8"]]},{"id":"57a0d660.fb21c8","type":"switch","z":"b473c044.f9da7","name":"","property":"payload","propertyType":"msg","rules":[{"t":"false"}],"checkall":"true","repair":false,"outputs":1,"x":941,"y":831,"wires":[["38cd2fab.fcc33"]]},{"id":"38cd2fab.fcc33","type":"change","z":"b473c044.f9da7","name":"prep MQTT","rules":[{"t":"set","p":"topic","pt":"msg","to":"red/event/error/@sys@/@dev@","tot":"str"},{"t":"change","p":"topic","pt":"msg","from":"@dev@","fromt":"str","to":"config.device","tot":"msg"},{"t":"change","p":"topic","pt":"msg","from":"@sys@","fromt":"str","to":"config.system","tot":"msg"},{"t":"delete","p":"payload","pt":"msg"},{"t":"set","p":"payload.value","pt":"msg","to":"config.errortype","tot":"msg"},{"t":"set","p":"payload.reason","pt":"msg","to":"errormsg","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":1069,"y":805,"wires":[["6d2dafb7.cb1da"]]},{"id":"54750ee2.c1af9","type":"change","z":"b473c044.f9da7","name":"prep MQTT","rules":[{"t":"set","p":"topic","pt":"msg","to":"red/event/error/@sys@/@dev@","tot":"str"},{"t":"change","p":"topic","pt":"msg","from":"@dev@","fromt":"str","to":"payload[0].config.device","tot":"msg"},{"t":"change","p":"topic","pt":"msg","from":"@sys@","fromt":"str","to":"payload[0].config.system","tot":"msg"},{"t":"set","p":"errormsg","pt":"msg","to":"payload[0].errormsg","tot":"msg"},{"t":"change","p":"errormsg","pt":"msg","from":"@val@","fromt":"str","to":"payload[1].payload.value","tot":"msg"},{"t":"set","p":"errortype","pt":"msg","to":"payload[0].config.errortype","tot":"msg"},{"t":"delete","p":"payload","pt":"msg"},{"t":"set","p":"payload.value","pt":"msg","to":"errortype","tot":"msg"},{"t":"set","p":"payload.reason","pt":"msg","to":"errormsg","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":1182,"y":951,"wires":[["6d2dafb7.cb1da"]]},{"id":"ffe27d2.436228","type":"switch","z":"b473c044.f9da7","name":"check limits","property":"payload[1].payload.value","propertyType":"msg","rules":[{"t":"btwn","v":"payload[0].payload.limitmin","vt":"msg","v2":"payload[0].payload.limitmax","v2t":"msg"},{"t":"else"}],"checkall":"true","repair":false,"outputs":2,"x":1001,"y":972,"wires":[[],["54750ee2.c1af9"]]},{"id":"297a66a7.cad58a","type":"function","z":"b473c044.f9da7","name":"AJV8_partial_match","func":"var countmsg = 2;          //how many items to join?\n\nvar nodublicates = false;   //only join if payload unequal existing payload\nvar limit;                 //only join if payloads are on both sides of the limit\nvar hysteresis;      //hysteresis \nvar clearaftersend = false;//clear queue after sending\nvar sendinstantly = false;\nvar reverse = false;         //reverse output array\nvar priomode = false;\nvar storagemode = true;     \nvar partial = true;\n//------------ different topic mode ------------------\nvar diftopics = false;      //true: join only different topics; false: join only same topics\n\nvar inmsg = msg;\nvar outmsg = { \"topic\": inmsg.topic , \"payload\": \"\"};\nvar msgs = context.get(\"aggmsgs\")||{};\nvar topicmsgs = [];\nif (partial) {\n    if (typeof inmsg.priority != \"undefined\") \n        topicmsgs = msgs[inmsg.topic]||[];\n    else {\n        for (var tmsgs in msgs) {\n            if (msgs.hasOwnProperty(tmsgs)) {\n                if(inmsg.topic.indexOf(tmsgs) > -1) {\n                    topicmsgs = msgs[tmsgs];\n                    break;\n                }\n            }\n        } \n        if (topicmsgs.length === 0)\n            return null;\n    }\n}\nelse\n    topicmsgs = msgs[inmsg.topic]||[];\n\nvar lastval = context.get(\"lastval\");\nvar inprio = typeof inmsg.priority != \"undefined\" ? inmsg.priority: countmsg-1 ;     //default prio is 1\n\nif (inmsg.topic === \"\")\n    return null;\n\nif (typeof inmsg.reset != \"undefined\") {\n    msgs = {};\n    topicmsgs = [];\n    lastval = undefined;\n    context.set(\"aggmsgs\",msgs);\n    context.set(\"lastval\",lastval);\n    return null;\n}\nif (typeof inmsg.resettopic != \"undefined\") {\n    topicmsgs = [];\n    lastval = undefined;\n}\nif (typeof hysteresis !== 'undefined' && typeof lastval !== 'undefined') {\n    if (Math.abs(inmsg.payload - lastval) <= hysteresis) {\n        return null;\n    }\n}\nif (typeof lastval !== 'undefined') {\n    if (nodublicates && lastval == inmsg.payload)\n        return null;\n    if (typeof limit !== 'undefined' && countmsg == 2) {\n        if (!((lastval <= limit && inmsg.payload >= limit) || (lastval >= limit && inmsg.payload <= limit)))   \n            return null;\n    }\n} \n\nif (diftopics) {\n//node.warn(topicmsgs.length);\n    if (topicmsgs.length > 0) {     //msg with same topic -> replace old msg\n        topicmsgs.shift();\n    }\n    topicmsgs.push(inmsg);\n    msgs[inmsg.topic] = topicmsgs;    \n    if (Object.keys(msgs).length > countmsg) {\n        for (var tmsgs in msgs) {\n            if (msgs.hasOwnProperty(tmsgs)) {   \n                delete msgs[tmsgs];\n                break;\n            }\n        }\n    }\n    context.set(\"aggmsgs\",msgs);\n    context.set(\"lastval\",inmsg.payload);\n//node.warn(Object.keys(msgs).length);\n    if (Object.keys(msgs).length == countmsg) {\n        var aout = {};\n        outmsg.topic = \"\";\n        for (var tmsgs in msgs) {\n            if (msgs.hasOwnProperty(tmsgs)) {\n                aout[tmsgs] = msgs[tmsgs][0];\n                outmsg.topic += tmsgs + \"_\";\n            }\n        }\n        if (clearaftersend) {\n            context.set(\"aggmsgs\",undefined);\n            context.set(\"lastval\",undefined);\n        }\n        outmsg.topic = outmsg.topic.substr(0,String(outmsg.topic).length-1);\n        outmsg.payload = aout;\n        if (reverse) {\n            var revArr = outmsg.payload.map(a => Object.assign({}, a));\n            revArr.reverse();\n            outmsg.payload = revArr;\n        }\n        return outmsg;\n    } else\n        return null;\n} else {\n    if (topicmsgs.length == countmsg && !storagemode){\n        topicmsgs.shift();\n    }\n    if (storagemode) {\n        topicmsgs[inprio] = inmsg;\n    } else {\n        topicmsgs.push(inmsg);\n    }\n\n    msgs[inmsg.topic] = topicmsgs;\n    context.set(\"aggmsgs\",msgs);\n    context.set(\"lastval\",inmsg.payload);\n    \n    if (storagemode) {                          //check send msg\n        if (inprio < topicmsgs.length - 1)      //only send on trigger msg => no priority\n            return null;\n        for (var i = 0; i < topicmsgs.length; i++) { //only send if storage is full\n            if (topicmsgs[i] === undefined)\n                return null;\n        }    \n    }\n    \n    \n    if (topicmsgs.length == countmsg || sendinstantly) {\n        outmsg.payload = topicmsgs;\n        if (clearaftersend) {\n            msgs[inmsg.topic] = undefined;\n            context.set(\"aggmsgs\",msgs);\n            context.set(\"lastval\",undefined);\n        }\n        if (reverse) {\n            var revArr = outmsg.payload.map(a => Object.assign({}, a));\n            revArr.reverse();\n            outmsg.payload = revArr;\n        }\n        return outmsg;\n    }\n    else\n        return null;\n}\n\n","outputs":1,"noerr":0,"x":811,"y":1028,"wires":[["ffe27d2.436228"]]},{"id":"8196fd0a.cc8fd","type":"json","z":"b473c044.f9da7","name":"","x":464,"y":1130,"wires":[["297a66a7.cad58a"]]},{"id":"74eec9d.363e338","type":"mqtt in","z":"b473c044.f9da7","name":"","topic":"sensor/status/temperature/#","qos":"0","broker":"e36a332b.c61f3","x":285,"y":1085,"wires":[["8196fd0a.cc8fd"]]},{"id":"6d2dafb7.cb1da","type":"debug","z":"b473c044.f9da7","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","x":1250,"y":848,"wires":[]},{"id":"e36a332b.c61f3","type":"mqtt-broker","z":"","broker":"localhost","port":"1883","clientid":"","usetls":false,"compatmode":false,"keepalive":"15","cleansession":true,"willTopic":"","willQos":"0","willPayload":"","birthTopic":"","birthQos":"0","birthPayload":""}]

#16

Does arp really work for you? I gave up on it, it was completely unreliable on my system.
If the phone's wifi is not connected how can arp know whether the phone is still in the building or not?


#17

That's one of the caveats of doing presence using IP networking: you have to have the device join the network.

Another potential option that doesn't rely on a full connection is using bluetooth. Basically just use the exec node to run a command line bluetooth scanner like hcitool at set intervals to dump a list of detected bluetooth devices (and even RSSI!) Much more work to get it running, and the range is way more limited, but if you can deploy that flow on multiple raspberry pis (or similar) around your location you can even achieve per-room tracking. (I'm playing around with this using fitbits; was inspired by an asset tracking system we're building at my hospital.)


#18

Luckily I actually have some Unifi kit at home so I'll check that out :slight_smile:


#19

Sorry to @cflurin that this thread has derailed into a "is my phone at home" :slight_smile: