Skip to content

Instantly share code, notes, and snippets.

@skylord123
Last active February 2, 2026 17:31
Show Gist options
  • Select an option

  • Save skylord123/438e7bbdb5474ab9b738f1c79284b7d9 to your computer and use it in GitHub Desktop.

Select an option

Save skylord123/438e7bbdb5474ab9b738f1c79284b7d9 to your computer and use it in GitHub Desktop.
Node-RED Ping & Latency tracking flow
image

The image above shows an example of this Node-RED flow configured with two additional hosts for redundancy.

Read the article for this here

This flow monitors network health by tracking average ping latency, packet loss, and overall internet connectivity status, and reports these values to Home Assistant. While it is designed with Home Assistant in mind, the flow is easy to adapt if you want to consume or act on the data elsewhere.

The flow pings the hosts defined in the Set list of hosts node on a 10-second interval. During each cycle, one ping is sent per second for a total of 10 seconds. For each configured endpoint, the flow calculates average latency and packet loss and publishes these metrics individually.

The Set list of hosts node also allows you to define which endpoints should be treated as external by setting msg.internet_connectivity_hosts. These hosts are used to determine overall internet status. If all hosts in this array fail to respond during a 10-second cycle, the internet_status value is reported as off.

Auto rebooting modem

There is another flow that builds on this here for automatically restarting your modem when issues occur.

Importing the flow

To import this flow, copy the contents of flow.json and use the Import option in the Node-RED editor.

[{"id":"82859d0c1df0050b","type":"group","z":"b47ba089725ec70c","name":"Ping various servers every 10 seconds for 10 seconds to track avg ping and packet loss","style":{"label":true},"nodes":["a978a5c1a92a01b2","1bc198ac8d23265b","c54c4d6494cdcb36","515f0462774e183c","78dd07ed6b075853","c28c83a54bac2f44","e0d030183f1c8228","46479dbabaa359b2","5cb6856336bcc46d","027f4005a73c5034","a035688e3d1b7120","dc0442d0f700fdb0","b0db9a8e9605f5bb","90a6bcc7fc6f710a","6a64ef26cebc8bba","688b90cd5603679f","f6f9b8261827bdd1","c18ccc1d94c5f510","892033128401503e"],"x":54,"y":19,"w":1732,"h":282},{"id":"a978a5c1a92a01b2","type":"ha-sensor","z":"b47ba089725ec70c","g":"82859d0c1df0050b","name":"ping_packet_loss_google_com","entityConfig":"ea2b883ff8c78d93","version":0,"state":"payload.packetLoss","stateType":"msg","attributes":[],"inputOverride":"allow","outputProperties":[],"x":1110,"y":180,"wires":[["1bc198ac8d23265b"]]},{"id":"1bc198ac8d23265b","type":"ha-sensor","z":"b47ba089725ec70c","g":"82859d0c1df0050b","name":"ping_avg_google_com","entityConfig":"1a8072bcc854d35a","version":0,"state":"payload.avg","stateType":"msg","attributes":[{"property":"min","value":"payload.min","valueType":"msg"},{"property":"max","value":"payload.max","valueType":"msg"},{"property":"stddev","value":"payload.stddev","valueType":"msg"},{"property":"time","value":"payload.time","valueType":"msg"}],"inputOverride":"allow","outputProperties":[],"x":1380,"y":180,"wires":[["46479dbabaa359b2"]]},{"id":"c54c4d6494cdcb36","type":"ha-sensor","z":"b47ba089725ec70c","g":"82859d0c1df0050b","name":"ping_packet_loss_modem","entityConfig":"72c6b3d7c4fab70a","version":0,"state":"payload.packetLoss","stateType":"msg","attributes":[],"inputOverride":"allow","outputProperties":[],"x":1090,"y":60,"wires":[["515f0462774e183c"]]},{"id":"515f0462774e183c","type":"ha-sensor","z":"b47ba089725ec70c","g":"82859d0c1df0050b","name":"ping_avg_modem","entityConfig":"f38a57be8eb5a8b6","version":0,"state":"payload.avg","stateType":"msg","attributes":[{"property":"min","value":"payload.min","valueType":"msg"},{"property":"max","value":"payload.max","valueType":"msg"},{"property":"stddev","value":"payload.stddev","valueType":"msg"},{"property":"time","value":"payload.time","valueType":"msg"}],"inputOverride":"allow","outputProperties":[],"x":1370,"y":60,"wires":[["5cb6856336bcc46d"]]},{"id":"78dd07ed6b075853","type":"change","z":"b47ba089725ec70c","g":"82859d0c1df0050b","name":"Set list of hosts","rules":[{"t":"set","p":"payload","pt":"msg","to":"[\"google.com\", \"1.1.1.1\", \"192.168.0.1\", \"192.168.1.1\"]","tot":"json"},{"t":"set","p":"internet_connectivity_hosts","pt":"msg","to":"[\"google.com\", \"1.1.1.1\"]","tot":"json"}],"action":"","property":"","from":"","to":"","reg":false,"x":380,"y":180,"wires":[["c28c83a54bac2f44"]]},{"id":"c28c83a54bac2f44","type":"function","z":"b47ba089725ec70c","g":"82859d0c1df0050b","name":"Ping Hosts (Array)","func":"/**\n * The function expects msg.payload to be an array of hosts.\n * It will ping all hosts in parallel and send an individual message for each host.\n *\n * Each resulting message will have:\n * msg.topic = the host that was pinged\n * msg.payload = result if alive\n * msg.error = error info if host not reachable\n *\n * The first output is for successes, the second output is for failures.\n * The third output is for internet connectivity status.\n */\n\n// Ensure we have an array of hosts\nconst hosts = Array.isArray(msg.payload) ? msg.payload : [msg.payload];\n\n// Get the list of hosts to check for internet connectivity\nconst internetHosts = Array.isArray(msg.internet_connectivity_hosts) \n ? msg.internet_connectivity_hosts \n : [];\n\n/**\n * Ping configuration object\n * @typedef {Object} PingConfig\n * @property {boolean} numeric\n * @property {number} timeout - Timeout in seconds\n * @property {number} deadline - Max time before ping exits\n * @property {number} min_reply - Number of ECHO requests\n * @property {boolean} v6\n * @property {string} sourceAddr\n * @property {number} packetSize\n * @property {string[]} extra\n */\n\nconst pingConfig = {\n timeout: 1, // Timeout in seconds for each ping request\n deadline: 10, // Maximum time to wait for the ping command to complete\n min_reply: 10\n};\n\n// Track internet connectivity results\nlet internetCheckResults = [];\nlet internetChecksPending = 0;\n\nhosts.forEach((host) => {\n const isInternetHost = internetHosts.includes(host);\n \n if (isInternetHost) {\n internetChecksPending++;\n }\n \n ping.promise.probe(host, pingConfig)\n .then((res) => {\n const newMsg = { topic: host };\n if (res.alive) {\n newMsg.payload = res;\n node.send([newMsg, null, null]);\n } else {\n newMsg.error = `Host ${host} is not reachable.`;\n newMsg.payload = {\n \"inputHost\": host,\n \"host\": host,\n \"alive\": false,\n \"output\": newMsg.error,\n \"time\": null,\n \"times\": [],\n \"min\": null,\n \"max\": null,\n \"avg\": null,\n \"stddev\": newMsg.error,\n \"packetLoss\": \"100.000\",\n \"numeric_host\": null\n }\n node.send([null, newMsg, null]);\n }\n \n // Track internet connectivity status\n if (isInternetHost) {\n internetCheckResults.push(res.alive);\n checkInternetStatus();\n }\n })\n .catch((error) => {\n const newMsg = {\n topic: host,\n error: error.toString(),\n payload: {\n \"inputHost\": host,\n \"host\": host,\n \"alive\": false,\n \"output\": error.toString(),\n \"time\": null,\n \"times\": [],\n \"min\": null,\n \"max\": null,\n \"avg\": null,\n \"stddev\": error.toString(),\n \"packetLoss\": \"100.000\",\n \"numeric_host\": null\n }\n };\n node.send([null, newMsg, null]);\n \n // Track internet connectivity status\n if (isInternetHost) {\n internetCheckResults.push(false);\n checkInternetStatus();\n }\n });\n});\n\n// Check if all internet hosts have been pinged and send status\nfunction checkInternetStatus() {\n if (internetCheckResults.length === internetChecksPending && internetChecksPending > 0) {\n // Check if any internet host responded\n const internetUp = internetCheckResults.some(result => result === true);\n \n const statusMsg = {\n payload: internetUp ? \"on\" : \"off\"\n };\n \n node.send([null, null, statusMsg]);\n }\n}\n\nreturn null;","outputs":3,"timeout":"","noerr":0,"initialize":"","finalize":"","libs":[{"var":"ping","module":"ping"}],"x":590,"y":180,"wires":[["e0d030183f1c8228"],["027f4005a73c5034","e0d030183f1c8228"],["688b90cd5603679f"]]},{"id":"e0d030183f1c8228","type":"switch","z":"b47ba089725ec70c","g":"82859d0c1df0050b","name":"Route by Host","property":"topic","propertyType":"msg","rules":[{"t":"eq","v":"192.168.0.1","vt":"str"},{"t":"eq","v":"192.168.1.1","vt":"str"},{"t":"eq","v":"google.com","vt":"str"},{"t":"eq","v":"1.1.1.1","vt":"str"},{"t":"eq","v":"40.160.235.163","vt":"str"},{"t":"eq","v":"152.70.141.174","vt":"str"}],"checkall":"true","repair":false,"outputs":6,"x":800,"y":140,"wires":[["c54c4d6494cdcb36"],["90a6bcc7fc6f710a"],["a978a5c1a92a01b2"],["f6f9b8261827bdd1"],[],[]]},{"id":"46479dbabaa359b2","type":"debug","z":"b47ba089725ec70c","g":"82859d0c1df0050b","name":"ping google.com debug","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":1630,"y":180,"wires":[]},{"id":"5cb6856336bcc46d","type":"debug","z":"b47ba089725ec70c","g":"82859d0c1df0050b","name":"ping 192.168.0.1 debug","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":1630,"y":60,"wires":[]},{"id":"027f4005a73c5034","type":"debug","z":"b47ba089725ec70c","g":"82859d0c1df0050b","name":"ping error","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":800,"y":220,"wires":[]},{"id":"a035688e3d1b7120","type":"inject","z":"b47ba089725ec70c","g":"82859d0c1df0050b","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":200,"y":200,"wires":[["78dd07ed6b075853"]]},{"id":"dc0442d0f700fdb0","type":"cron","z":"b47ba089725ec70c","g":"82859d0c1df0050b","name":"Every 10 seconds","crontab":"*/10 * * * * *","x":170,"y":160,"wires":[["78dd07ed6b075853"]]},{"id":"b0db9a8e9605f5bb","type":"debug","z":"b47ba089725ec70c","g":"82859d0c1df0050b","name":"ping 192.168.1.1 debug","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":1630,"y":120,"wires":[]},{"id":"90a6bcc7fc6f710a","type":"ha-sensor","z":"b47ba089725ec70c","g":"82859d0c1df0050b","name":"ping_packet_loss_router","entityConfig":"ddaa68197395b5cd","version":0,"state":"payload.packetLoss","stateType":"msg","attributes":[],"inputOverride":"allow","outputProperties":[],"x":1090,"y":120,"wires":[["6a64ef26cebc8bba"]]},{"id":"6a64ef26cebc8bba","type":"ha-sensor","z":"b47ba089725ec70c","g":"82859d0c1df0050b","name":"ping_avg_router","entityConfig":"30240a95566bef72","version":0,"state":"payload.avg","stateType":"msg","attributes":[{"property":"min","value":"payload.min","valueType":"msg"},{"property":"max","value":"payload.max","valueType":"msg"},{"property":"stddev","value":"payload.stddev","valueType":"msg"},{"property":"time","value":"payload.time","valueType":"msg"}],"inputOverride":"allow","outputProperties":[],"x":1360,"y":120,"wires":[["b0db9a8e9605f5bb"]]},{"id":"688b90cd5603679f","type":"ha-binary-sensor","z":"b47ba089725ec70c","g":"82859d0c1df0050b","name":"internet_status","entityConfig":"e20667a1e81a4348","version":0,"state":"payload","stateType":"msg","attributes":[],"inputOverride":"allow","outputProperties":[],"x":820,"y":260,"wires":[[]]},{"id":"f6f9b8261827bdd1","type":"ha-sensor","z":"b47ba089725ec70c","g":"82859d0c1df0050b","name":"ping_packet_loss_cloudflare_dns","entityConfig":"40d7f76d943d3952","version":0,"state":"payload.packetLoss","stateType":"msg","attributes":[],"inputOverride":"allow","outputProperties":[],"x":1120,"y":240,"wires":[["c18ccc1d94c5f510"]]},{"id":"c18ccc1d94c5f510","type":"ha-sensor","z":"b47ba089725ec70c","g":"82859d0c1df0050b","name":"ping_avg_cloudflare_dns","entityConfig":"707a3abafd13c410","version":0,"state":"payload.avg","stateType":"msg","attributes":[{"property":"min","value":"payload.min","valueType":"msg"},{"property":"max","value":"payload.max","valueType":"msg"},{"property":"stddev","value":"payload.stddev","valueType":"msg"},{"property":"time","value":"payload.time","valueType":"msg"}],"inputOverride":"allow","outputProperties":[],"x":1390,"y":240,"wires":[["892033128401503e"]]},{"id":"892033128401503e","type":"debug","z":"b47ba089725ec70c","g":"82859d0c1df0050b","name":"ping 1.1.1.1 debug","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":1610,"y":240,"wires":[]},{"id":"ea2b883ff8c78d93","type":"ha-entity-config","server":"233a9c63.e2baf4","deviceConfig":"c76f3feee4e3dec6","name":"ping_packet_loss_google_com","version":"6","entityType":"sensor","haConfig":[{"property":"name","value":"Google.com Packet Loss"},{"property":"icon","value":""},{"property":"entity_picture","value":""},{"property":"entity_category","value":""},{"property":"device_class","value":""},{"property":"unit_of_measurement","value":"%"},{"property":"state_class","value":"measurement"}],"resend":false,"debugEnabled":false},{"id":"1a8072bcc854d35a","type":"ha-entity-config","server":"233a9c63.e2baf4","deviceConfig":"c76f3feee4e3dec6","name":"ping_avg_google_com","version":"6","entityType":"sensor","haConfig":[{"property":"name","value":"Google.com Ping Average"},{"property":"icon","value":""},{"property":"entity_picture","value":""},{"property":"entity_category","value":""},{"property":"device_class","value":""},{"property":"unit_of_measurement","value":"ms"},{"property":"state_class","value":"measurement"}],"resend":false,"debugEnabled":false},{"id":"72c6b3d7c4fab70a","type":"ha-entity-config","server":"233a9c63.e2baf4","deviceConfig":"c76f3feee4e3dec6","name":"ping_packet_loss_modem","version":"6","entityType":"sensor","haConfig":[{"property":"name","value":"Modem Packet Loss"},{"property":"icon","value":""},{"property":"entity_picture","value":""},{"property":"entity_category","value":""},{"property":"device_class","value":""},{"property":"unit_of_measurement","value":"%"},{"property":"state_class","value":"measurement"}],"resend":false,"debugEnabled":false},{"id":"f38a57be8eb5a8b6","type":"ha-entity-config","server":"233a9c63.e2baf4","deviceConfig":"c76f3feee4e3dec6","name":"modem_ping_average","version":"6","entityType":"sensor","haConfig":[{"property":"name","value":"Modem Ping Average"},{"property":"icon","value":""},{"property":"entity_picture","value":""},{"property":"entity_category","value":""},{"property":"device_class","value":""},{"property":"unit_of_measurement","value":"ms"},{"property":"state_class","value":"measurement"}],"resend":false,"debugEnabled":false},{"id":"ddaa68197395b5cd","type":"ha-entity-config","server":"233a9c63.e2baf4","deviceConfig":"c76f3feee4e3dec6","name":"ping_packet_loss_router","version":"6","entityType":"sensor","haConfig":[{"property":"name","value":"Router Packet Loss"},{"property":"icon","value":""},{"property":"entity_picture","value":""},{"property":"entity_category","value":""},{"property":"device_class","value":""},{"property":"unit_of_measurement","value":"%"},{"property":"state_class","value":"measurement"}],"resend":false,"debugEnabled":false},{"id":"30240a95566bef72","type":"ha-entity-config","server":"233a9c63.e2baf4","deviceConfig":"c76f3feee4e3dec6","name":"router_ping_average","version":"6","entityType":"sensor","haConfig":[{"property":"name","value":"Router Ping Average"},{"property":"icon","value":""},{"property":"entity_picture","value":""},{"property":"entity_category","value":""},{"property":"device_class","value":""},{"property":"unit_of_measurement","value":"ms"},{"property":"state_class","value":"measurement"}],"resend":false,"debugEnabled":false},{"id":"e20667a1e81a4348","type":"ha-entity-config","server":"233a9c63.e2baf4","deviceConfig":"c76f3feee4e3dec6","name":"","version":6,"entityType":"binary_sensor","haConfig":[{"property":"name","value":"Internet Status"},{"property":"icon","value":"mdi:web"},{"property":"entity_picture","value":""},{"property":"entity_category","value":""},{"property":"device_class","value":""}],"resend":false,"debugEnabled":false},{"id":"40d7f76d943d3952","type":"ha-entity-config","server":"233a9c63.e2baf4","deviceConfig":"c76f3feee4e3dec6","name":"ping_packet_loss_cloudflare_dns","version":6,"entityType":"sensor","haConfig":[{"property":"name","value":"Cloudflare DNS Packet Loss"},{"property":"icon","value":""},{"property":"entity_picture","value":""},{"property":"entity_category","value":""},{"property":"device_class","value":""},{"property":"unit_of_measurement","value":"%"},{"property":"state_class","value":"measurement"}],"resend":false,"debugEnabled":false},{"id":"707a3abafd13c410","type":"ha-entity-config","server":"233a9c63.e2baf4","deviceConfig":"c76f3feee4e3dec6","name":"ping_avg_cloudflare_dns","version":6,"entityType":"sensor","haConfig":[{"property":"name","value":"Cloudflare DNS Ping Average"},{"property":"icon","value":""},{"property":"entity_picture","value":""},{"property":"entity_category","value":""},{"property":"device_class","value":""},{"property":"unit_of_measurement","value":"ms"},{"property":"state_class","value":"measurement"}],"resend":false,"debugEnabled":false},{"id":"233a9c63.e2baf4","type":"server","name":"Home Assistant","version":6,"addon":false,"rejectUnauthorizedCerts":true,"ha_boolean":["y","yes","true","on","home","open"],"connectionDelay":true,"cacheJson":true,"heartbeat":false,"heartbeatInterval":"30","areaSelector":"friendlyName","deviceSelector":"friendlyName","entitySelector":"friendlyName","statusSeparator":"at: ","statusYear":"hidden","statusMonth":"short","statusDay":"numeric","statusHourCycle":"h23","statusTimeFormat":"h:m","enableGlobalContextStore":true},{"id":"c76f3feee4e3dec6","type":"ha-device-config","name":"Ping Tests","hwVersion":"","manufacturer":"Node-RED","model":"","swVersion":""},{"id":"784bda9a2dc9a7fa","type":"global-config","env":[],"modules":{"node-red-contrib-home-assistant-websocket":"0.80.3","node-red-contrib-cron":"0.0.4"}}]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment