Monitoring IoT by Ping

Hello
I monitor some IoT by ping. These are then displayed in green or red on the dashboard if they are alive or not.
The more there are the more work and maintenance there is.
Is there a way where I read a comma or semicolon separated IP list and query them one by one so that all IPs are shown in the dashboard?

Today:
Ping Node manual IP Adress enteed>

Function

msg.payload = msg.payload !== false ?
 "true":
 "false";
switch (msg.payload) {
case "false" : 
    msg.color = "red";
    break;
case "true" : 
    msg.color = "lime";
    break;    
default : 
    msg.color = "black";
    break;
}
return msg;

Text Node
<font color= {{msg.color}} > {{msg.topic}} </font>

UPDATE - see this a bit further down

Hi - I had a bit play - you might get something from this...

Result...

image

The flow...

the code...

[{"id":"222b911b.7f483e","type":"exec","z":"b58d85fb.c44468","command":"ping -n 1","addpay":true,"append":"","useSpawn":"false","timer":"5","oldrc":false,"name":"","x":180,"y":200,"wires":[["6c078f6a.20f33"],["835178d3.275268"],["15d9eea6.b13c31"]]},{"id":"3569dd52.27fd92","type":"inject","z":"b58d85fb.c44468","name":"*** Setup devices to ping here ***","topic":"","payload":"[{\"name\":\"router\",\"ip\":\"192.168.0.1\"},{\"name\":\"tv\",\"ip\":\"192.168.0.51\"},{\"name\":\"smart speaker\",\"ip\":\"192.168.0.52\"},{\"name\":\"bad device\",\"ip\":\"192.168.1.99\"}]","payloadType":"json","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":190,"y":60,"wires":[["935ef4ca.1414c8"]]},{"id":"935ef4ca.1414c8","type":"split","z":"b58d85fb.c44468","name":"","splt":"\\n","spltType":"str","arraySplt":1,"arraySpltType":"len","stream":false,"addname":"","x":390,"y":60,"wires":[["801f89e0.5a45e8"]]},{"id":"6c078f6a.20f33","type":"function","z":"b58d85fb.c44468","name":"update thisPing","func":"\nmsg.thisPing.timestamp = new Date();\nmsg.thisPing.message = msg.payload;\n\nreturn msg;","outputs":1,"noerr":0,"x":420,"y":160,"wires":[["8d1f2fa0.953ce","38217bfa.ebbcd4"]]},{"id":"835178d3.275268","type":"function","z":"b58d85fb.c44468","name":"update thisPing","func":"\nmsg.thisPing.timestamp = new Date();\nmsg.thisPing.message = msg.payload;\nmsg.thisPing.error = true;\nmsg.thisPing.state = 'ng'\n\n\n\nreturn msg;","outputs":1,"noerr":0,"x":420,"y":200,"wires":[["682780dd.070e3","38217bfa.ebbcd4"]]},{"id":"15d9eea6.b13c31","type":"function","z":"b58d85fb.c44468","name":"update thisPing","func":"msg.thisPing.timestamp = new Date();\nmsg.thisPing.result = msg.payload;\nmsg.thisPing.error = msg.payload.code !== 0;\nmsg.thisPing.state = msg.thisPing.error === false ? \"ok\" : \"ng\";\n\n\nreturn msg;","outputs":1,"noerr":0,"x":420,"y":240,"wires":[["705d8927.7a4068","38217bfa.ebbcd4"]]},{"id":"801f89e0.5a45e8","type":"function","z":"b58d85fb.c44468","name":"Init thisPing","func":"let pingResults = flow.get(\"pingResults\") || {}\nmsg.thisPing = pingResults[msg.topic] || {};\n\nmsg.thisPing.name = msg.payload.name;\nmsg.thisPing.ip = msg.payload.ip;\nmsg.thisPing.timestamp = new Date();\nmsg.thisPing.result = '';\nmsg.thisPing.error = null;\nmsg.thisPing.state = 'pinging';\n\nmsg.topic = msg.thisPing.name;\nmsg.payload = msg.thisPing.ip;\n\npingResults[msg.topic] = msg.thisPing;//store ping obj in pingResults\nflow.set(\"pingResults\",pingResults);//save pingResults in flow context\n\nreturn msg;","outputs":1,"noerr":0,"x":550,"y":60,"wires":[["222b911b.7f483e","460bf53.537a60c"]]},{"id":"460bf53.537a60c","type":"debug","z":"b58d85fb.c44468","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","x":770,"y":60,"wires":[]},{"id":"8d1f2fa0.953ce","type":"debug","z":"b58d85fb.c44468","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","x":770,"y":160,"wires":[]},{"id":"682780dd.070e3","type":"debug","z":"b58d85fb.c44468","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","x":770,"y":200,"wires":[]},{"id":"705d8927.7a4068","type":"debug","z":"b58d85fb.c44468","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","x":770,"y":240,"wires":[]},{"id":"38217bfa.ebbcd4","type":"function","z":"b58d85fb.c44468","name":"Update flow context","func":"let pingResults = flow.get(\"pingResults\") || {}\nlet thisPing = msg.thisPing;\n\n//store ping item\npingResults[thisPing.name] = thisPing;\n\n//save pingResults in flow context\nflow.set(\"pingResults\",pingResults);\n\nreturn msg;","outputs":1,"noerr":0,"x":730,"y":300,"wires":[[]]},{"id":"c2440a8c.cf9c38","type":"template","z":"b58d85fb.c44468","name":"build html table","field":"html","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"\n<table width=\"100%\">\n  <tr>\n    <th>Name</th>\n    <th>IP</th> \n    <th>Status</th> \n  </tr>\n  {{#tableData}}\n  <tr class='{{rowclass}}'>\n    <td>{{{name}}}</td>\n    <td>{{ip}}</td> \n    <td>{{{stateInfo}}}</i></td> \n  </tr>\n  {{/tableData}}\n</table>","x":560,"y":420,"wires":[["81fa405b.5c169","2fdd97a9.33a278"]]},{"id":"81fa405b.5c169","type":"debug","z":"b58d85fb.c44468","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","x":770,"y":380,"wires":[]},{"id":"5cb5897e.9faec8","type":"inject","z":"b58d85fb.c44468","name":"","topic":"","payload":"[{\"name\":\"router\",\"ip\":\"192.168.0.1\"},{\"name\":\"tv\",\"ip\":\"192.168.0.51\"},{\"name\":\"smart speaker\",\"ip\":\"192.168.0.52\"},{\"name\":\"bad device\",\"ip\":\"192.168.1.99\"}]","payloadType":"json","repeat":"30","crontab":"","once":false,"onceDelay":0.1,"x":110,"y":420,"wires":[["5cef1fce.0085d"]]},{"id":"5cef1fce.0085d","type":"function","z":"b58d85fb.c44468","name":"convert pingResults to array","func":"let pingResults = flow.get(\"pingResults\") || {}\n\nlet tableData = [];\nfor (let k in pingResults) {\n    let r = pingResults[k];\n    if(r.state == \"ok\"){\n        r.rowclass = \"good\";\n        r.stateInfo = '<i class=\"fa fa-thumbs-up\" aria-hidden=\"true\"></i> OK'\n    } else if(r.state == \"ng\") {\n         r.rowclass = \"bad\";\n        r.stateInfo = '<i class=\"fa fa-thumbs-down\" aria-hidden=\"true\"></i> NG'\n    } else {\n        r.rowclass = \"unknown\";\n        r.stateInfo = '<i class=\"fa question\" aria-hidden=\"true\"></i> Unknown'\n    }\n  tableData.push(r);\n}\n\nmsg.tableData = tableData\n\nreturn msg;","outputs":1,"noerr":0,"x":320,"y":420,"wires":[["c2440a8c.cf9c38"]]},{"id":"2fdd97a9.33a278","type":"ui_template","z":"b58d85fb.c44468","group":"94ef3efa.b0b16","name":"","order":3,"width":"6","height":"5","format":"<style>\ntable {\n    color: #333;\n    font-family: Helvetica, Arial, sans-serif;\n    width: 100%;\n    border-collapse: collapse;\n    border-spacing: 0;\n}\ntd, th {\n    border: 1px solid transparent;\n    /* No more visible border */\n    height: 30px;\n    transition: all 0.3s;\n    /* Simple transition for hover effect */\n}\nth {\n    background: #DFDFDF;\n    /* Darken header a bit */\n    font-weight: bold;\n}\ntd {\n    background: #FAFAFA;\n    text-align: center;\n}\n\n/* Cells in even rows (2,4,6...) are one color */\n\ntr:nth-child(even) td {\n    background: #F1F1F1;\n}\n\n/* Cells in odd rows (1,3,5...) are another (excludes header cells)  */\n\ntr:nth-child(odd) td {\n    background: #FEFEFE;\n}\ntr td:hover {\n    background: #666;\n    color: #FFF;\n}\n\n/* Hover cell effect! */    \n\ntr.good {\n    color: green;\n}\n\ntr.bad {\n    color: red;\n}\n\ntr.unknown {\n    color: orange;\n}\n\n    \n</style>\n<div ng-bind-html=\"msg.html\"></div>\n","storeOutMessages":true,"fwdInMessages":true,"templateScope":"local","x":780,"y":420,"wires":[[]]},{"id":"94ef3efa.b0b16","type":"ui_group","z":"","name":"Default","tab":"6cc8c50b.445afc","disp":true,"width":"6","collapse":false},{"id":"6cc8c50b.445afc","type":"ui_tab","z":"","name":"Home","icon":"dashboard","disabled":false,"hidden":false}]
2 Likes

Good afternoon friend ... need to make any adjustments to work on the raspberry pi 4? I would like to monitor everyone I have on the network

Did you import the flow @Steve-Mcl provided and try it?

1 Like

yes, I imported, and put the ips referring to my network but it didn't work

You have to put in the IP addresses of the devices you want to monitor.

You're making it difficult to help you with your lack of explicit information, screenshots, additional info.

What did the debug output say? Did you inspect the side bar debug output?

As you can see from my screen shot - it does work.

@Steve-Mcl

ok, sorry if you were not detail oriented .. but let's go again .. I imported your script normally until then ok. but in exec this is giving an error (error 2) similar to the image you posted but yours (error1), the ips list on the dashbord but they are all off, the ip 192.168.0.84 should be in ok status because it is the raspberry I'm running NR ..
I thank you for your attention, friend

pi@raspberrypi:~ $ ping 192.168.0.84
PING 192.168.0.84 (192.168.0.84) 56(84) bytes of data.
64 bytes from 192.168.0.84: icmp_seq=1 ttl=64 time=0.047 ms
64 bytes from 192.168.0.84: icmp_seq=2 ttl=64 time=0.060 ms
64 bytes from 192.168.0.84: icmp_seq=3 ttl=64 time=0.096 ms
64 bytes from 192.168.0.84: icmp_seq=4 ttl=64 time=0.105 ms
64 bytes from 192.168.0.84: icmp_seq=5 ttl=64 time=0.095 ms
64 bytes from 192.168.0.84: icmp_seq=6 ttl=64 time=0.086 ms


The command ping -n 1 works fine in Windows but in Linux (Raspbian) it works if you change to ping -c 1

You are welcome.

^ This is the first piece of important detail - you are using linux (raspian)

The second important detail is you get error 2 - pretty sure this means invalid argument in linux

The problem...

The simple flow is supposed to ping each device once - that's what the -n 1 specifies in ping -n 1 (and as @krambriw points out, ping has different command line parameters in raspian).
So because of this, on Raspian, the ping command doesnt work and error 2 occurs.

The solution

Open the ping -n 1 node and change ping -n 1 to ping -c 1

Let us know if that works.

Question - anyone know if we can determine OS in a flow?

I'd make this demo universal if I could.

It might be better to use node-red-node-ping which would presumably cope with such differences.

Colin, if i'm not mistaken, node-red-node-ping node doesn't accept input - OP needs a list to process.

I am aware there is another ping node that accepts input but, I wanted to make it operate without additional external nodes to simplify things - plug and play for any user - however, in hindsight... :confused:

Yes, you are correct, I missed that.

lsb_release -a
will tell you which linux you are using
On Raspbian:

$ lsb_release -a
No LSB modules are available.
Distributor ID:	Raspbian
Description:	Raspbian GNU/Linux 10 (buster)
Release:	10
Codename:	buster

That will presumably give you something different on Windows, or error, so you could use that. There may well be a nicer way though.

I am currently porting ICMP from names as a set of tools for Node-Red , you should be able to listen for pings/messages , i will post an announcement rely soon(probably today) , but there should be some things that will be needed fixing.

Well in node.js - require('os').platform(); is probably the most useful.

Yeah cheers Dave, was aware of that however that would require a user to modify settings.js in node-red world right?

Was just wondering if there were a specific env var that we could query on a standard flow that reliably identified the breed (windows/*nix) for this kind of situation?

In a Function node, I wonder does the node object hold any environment clues? :thinking:

@Steve-Mcl You uncomment it in settings.js

    // The following property can be used to seed Global Context with predefined
    // values. This allows extra node modules to be made available with the
    // Function node.
    // For example,
    //    functionGlobalContext: { os:require('os') }
    // can be accessed in a function block as:
    //    global.get("os")
    functionGlobalContext: {
		os:require('os'),

Then in a function node you can use something like

var os = global.get("os");
node.warn(os.platform())

which on my mac returns:

3/31/2020, 8:14:21 AMnode: 46eaf429.ea9bf4
function : (warn)
"darwin"
1 Like

Yeah, as I suspected - however thats not really quick and "handy" for a demo (asking folks to modify settings.js)

Was just curious if an ENV var could identify windows/nix to simplify areas where we might use an EXEC node to do some dirty work and be compatible (as best as possible) with other OSs

Hindsight glasses on...
I think for these cases, It would be useful for the flow to know its host (e.g. if(node.platform == "win32") kinda thing)

I would have to disagree - while "useful", I think it only encourages people to write platform specific code which we don't really want in function nodes etc. At least inside a node you can hope that a developer does the right thing and handles the multiple cases.