Stuck with how to handle messages

I am sorry for the vague name in the SUBJECT field. I am kind of stuck just now.

The story
I have 3 machines all checking 4 things:
(in no particular order)
Router
Uplink
Internet
Modem

Internet - THE INTERNET. For the sake of an IP, I use 8.8.8.8
Uplink - my PUBLIC IP address. (not static.)
Modem - the ISP modem (to give it a name. I'm not really sure what they're called nowadays, but....)
Router - my router.

They all check these and create a message containing all 4 device names and their status.

So - as an example - this is a message:

{"topic":"STATUS/ADSL_Link_Status/","payload":{"Who":"TimePi","router":"online","pip":"online","modem":"online","internet":"online"},"qos":0,"retain":false,"_msgid":"d6861cb97bfa8a3e","delay":60000}

(and before you bite me for the name, ADSL is a legacy name and I'm not really going to change it just because it is a wrong name.)

There are 3 of these messages sent around between all 3 devices and they ..... come to a consensus of what's going on.

(with me so far?)

All well and good at this stage, but if one or two of them disagree with the LOCAL one:
what happens?

(Good question)

And this is where I'm at.

Alas today I just discovered that one of them wasn't actually sending it's message and I put that code it and now they ARE all sending the messages.

Where I'm stuck
I have 3 machines. None MORE IMPORTANT THAN THE OTHERS which is the first stumbling block.

Q1:
Does the LOCAL machine have a higher weight than the other two, or do they have a higher weight than it?

Q2:
(Trying to keep it modular)
How/WHERE can I assign weights (priorities?) to the messages?
Assigning them at the local machine fixes their weight throughout the whole triumvirate.

Q3:
If all 3 ARE the same and things are all OK, LIME is the colour.
If all 3 ARE the same and things are all BAD, RED is the colour.
If all 3 ARE the same and the devices are UNKNOWN, BLACK is the colour.
But if 2 don't agree with LOCAL...... GREEN is the colour.
I may make it more resolved and if only 1 of the 3 is GOOD then SPRING GREEN is the colour.
That way I can see how things are sitting with the 3 devices and what they SEE.
(Ok, I haven't asked the question yet - sorry.)

This arcs back to Q2. How would I do that?

I won't go any further as it may be a bit much to take on at this stage.

Thoughts?

I would have a couple of questions

  1. Why would one machine have a different answer to the others?
  2. Where is the decision on who is not agreeing being made?

But if 2 don't agree with LOCAL ..... would appear to define LOCAL as the decision maker.

Why do you need to assign a priority order? If anyone of them disagrees with the others then presumably you have a problem
What is going to happen (apart from the colour change) if they do not agree?

1 Like

Very good questions.

:slight_smile:

I didn't explain it too good because I am not sure how it will a work.

In answer to your questions:
1 - It has happened now a couple of times in the past few days where one of the three says the internet is not pingable.

2 - That's where I'm kinda stuck. Where.
At the sending part or the receiving part?
I think the receiving part would be better, but I'm not sure how to do it.

Why do I need to assign a Priority?

Again, I'm not sure.
Maybe I could just count who says what and act on the majority.
(But again: I don't know how I would do that.)

What's going to happen:
Well, in most cases all three will agree and the colour will be LIME or RED. (Maybe BLACK, but that's a whole other story)

If one of them thinks differently then the colour changes so if I happen to be passing my and notice it, I can look into what's happening.

Is that any easier to understand?
(Thanks.)

The bigger picture of why this is all happening.
It is just my curiosity to log things.
Way back when...... I had only one RasPi. It sat there Happily working, but no one was checking it was Alive.
Then came a second one who's job it was to make sure the original one was ok.
Then the question came on Who's checking that machine?
So I had them checking each other.
(long story - not important)
A third machine got involved and so I expanded the system so all three watched one another.
I then expanded that to check the router, modem, uplink and public IP address.
For no other reason than it seemed like a good exercise to do at the time.
And there have been some learning curves explored ITMT.

I hope (and I now see it is/was wrongly) it was working, but recent events have shown me that the things I thought checked one another weren't.

So I'm looking at a bit of a restructure.
One machine was missing an entire routine for broadcasting the checks to the others.
(Shock/horror!)

Sorry, this is now getting too long.
Hoping to hear back.
Thanks again.

Just to (maybe) help, here are some screen shots to show you what I'm seeing.

This is the annoying one.

See the two UPLINK messages.

1 yesterday.
1 today.
(relatively speaking)

So, what's the problem?

The other two machines.

You see today's but not yesterdays.

And lastly.

You can again see today's but not yesterday's.

Which - to me - means the first (shown) machine is somehow mucking up.

It is on WiFi. But before you blame WiFi dropouts, there is a system for detecting that.

(Also on the other machines.)

There have been no MQTT comms losses detected other than the one/s show here.

So they don't tally with WiFi drop outs.

To recap:
YESTERDAY'S Uplink failure is the one in question.
It only showed on one machine.
(Though, this could be partly due to the fact that the coding wasn't sharing what it saw/sees if the uplink went down, the other two would have complained all the same)

The reports are confusing.
Yesterday 2 machines said the uplink went down and their times concur.
Today 2 machines said the uplink went down and their times concur, but they are a different two than yesterday's two. (Ok, one is common..... c'mon. There's only 3 machines.)

To me that is even more confusing.

(Just realised: It was NOT the first shown machine that wasn't broadcasting. It was the LAST ONE!)
If it is confusing, tell me and I'll try to explain it again but in a different way.

Just to share progress.

This is a function node's code to do a better job than what I had to show the state of things.

// 2025-11-11 1618 — Colour logic: local vs remote, with SPRINGGREEN when local offline, RED if all offline

function makeIcon(colour, icon = "fa-bullseye") {
    return `<font color="${colour}"><i class="fa ${icon} fa-2x"></i></font>`
}

// Determine colour for this subsystem based on all three Pis
function getColour(localState, remoteStates) {
    // Check if localState and remoteStates are valid, default to 'offline' if not
    localState = (localState && localState.toLowerCase()) || 'offline'
    remoteStates = remoteStates.map(state => (state && state.toLowerCase()) || 'offline')

    const allStates = [localState, ...remoteStates]

    // If ALL are offline → RED
    if (allStates.every(s => s === "offline")) return "red"

    // If THIS Pi is offline → SPRINGGREEN
    if (localState === "offline") return "springgreen"

    // THIS Pi is online, determine based on remotes
    const onlineCount = remoteStates.filter(s => s === "online").length
    if (onlineCount === 2) return "lime"        // all 3 online
    if (onlineCount === 1) return "green"       // one other offline

    return "springgreen"                        // both others offline
}

// --- get or create persistent state ---
let state = context.get("state") || {
    router: {}, pip: {}, modem: {}, uplink: {}
}

// --- parse message ---
const p = msg.payload
if (!p || !p.Who) return null

const who = p.Who // e.g. "TimePi"
const thisPi = global.get("ThisPiName") || "TimePi" // local device name

// rename "internet" → "uplink"
p.uplink = p.internet

// update stored state
for (let key of ["router", "pip", "modem", "uplink"]) {
    if (p[key]) state[key][who] = p[key]
}

// helper: determine colour for one subsystem
function colourFor(subsys) {
    const all = state[subsys]
    const local = all[thisPi]
    const remotes = Object.entries(all)
        .filter(([k]) => k !== thisPi)
        .map(([_, v]) => v)
    return getColour(local, remotes)
}

// compute colours
const routerColour = colourFor("router")
const pipColour    = colourFor("pip")
const modemColour  = colourFor("modem")
const uplinkColour = colourFor("uplink")

// build 4 outgoing messages
let msg1 = { payload: makeIcon(routerColour), colour: routerColour }
let msg2 = { payload: makeIcon(pipColour),    colour: pipColour }
let msg3 = { payload: makeIcon(modemColour),  colour: modemColour }
let msg4 = { payload: makeIcon(uplinkColour), colour: uplinkColour }

// save updated state
context.set("state", state)

// optional: show quick status
node.status({
    text: `r:${routerColour} p:${pipColour} m:${modemColour} u:${uplinkColour}`
})

// output
return [msg1, msg2, msg3, msg4]

Do all three Pi's interrogate the router etc. in the same way?
If 1 of the Pi's gets a 'not connected' result how many times do they try again before moving on?
If there is a 'not connected' result do the other Pi's try again?
Are the UpLink (etc) messages logged with the name of the device that logged them? and is there a consolidated log? (I am going on the thought that since the two screen shots are from different Pi's that the logging is local)

You have a lot of checking going on there, I am going to have to think about my system.

(Again: Good questions)

Every 20 seconds each pi goes through a routine that pings all 4 IP addresses and reports their ping times.

The 4 messages are then made into one and sent out via MQTT.
(All 3 machines now do the same, with the same code - 90% the same.)

Anyway.

The message/s are received and then sent into the node posted.
It then configures/sets the icon colour depending on what's happening.

This is an ongoing thing every 20 seconds.

Yes, all logging is local.

(3 screen shots I hope.) :wink:

Nothing exciting really happens. Just me, logging useless stuff.

I am going to add code so that if the IP address starts with a specific number then the icon is also changed to the "WiFi" one indicating that the router has fallen onto the mobile network.

But that is a bit of a side thing in the the scheme at this point.

There definitely seems to be something happening as I am getting rogue messages the past few days.

To further help anyone interested understand what's going on and/or why here is a couple of bits of code:

This is the scanning code:

[{"id":"13abcd68.e79bfb","type":"inject","z":"f4651491.221e58","g":"28ee592794c503b8","name":"","repeat":"","crontab":"","once":false,"onceDelay":"","topic":"","payload":"Go","payloadType":"str","x":600,"y":160,"wires":[["85c45048.3cbcf8"]]},{"id":"85c45048.3cbcf8","type":"function","z":"f4651491.221e58","g":"28ee592794c503b8","name":"check list","func":"const uplinkip = global.get(\"uplink_IP\");\nlet checks = [\n    { 'topic': 'modem', 'host': \"192.168.100.1\"},\n    { 'topic': 'UpLink', 'host': uplinkip},\n    { 'topic': 'router', 'host': \"192.168.17.1\"},\n    { 'topic': 'internet', 'host': '8.8.8.8'}\n];\n\nmsg.payload = checks;\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":610,"y":200,"wires":[["53ba88cd.06d308"]]},{"id":"53ba88cd.06d308","type":"ping","z":"f4651491.221e58","g":"28ee592794c503b8","mode":"triggered","name":"Am I online?","host":"","timer":"20","inputs":1,"x":620,"y":240,"wires":[["0b4165813f698b57","42baddf2b729f12f"]]},{"id":"0b4165813f698b57","type":"change","z":"f4651491.221e58","g":"28ee592794c503b8","name":"** 2024 109 21","rules":[{"t":"move","p":"ping.topic","pt":"msg","to":"device","tot":"msg"},{"t":"move","p":"ping.host","pt":"msg","to":"IP","tot":"msg"},{"t":"delete","p":"ping","pt":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":725,"y":240,"wires":[["6642834ad4526b6a"]],"l":false,"info":"changed from `device` to `_topic`"},{"id":"6642834ad4526b6a","type":"junction","z":"f4651491.221e58","g":"28ee592794c503b8","x":760,"y":240,"wires":[["8975af459e32a828","ebb24c19.2c9048","5b4966d5a711779c"]]},{"id":"8975af459e32a828","type":"junction","z":"f4651491.221e58","g":"28ee592794c503b8","x":240,"y":320,"wires":[["4dd6d0b46d37ba0d","b4f1ee1d8d012603","6f4156c2.d6fc","aecd4e09.96f4b"]]},{"id":"6f4156c2.d6fc","type":"function","z":"f4651491.221e58","g":"28ee592794c503b8","name":"Router","func":"let msg1 = {};\nconst _topic = '192.168.17.1';\nif (msg.topic == _topic)\n{\n    //node.warn(msg.payload);\n    if (msg.payload > 0)\n    {\n        node.status({fill:\"green\",shape:\"dot\",text:\"Connected\"});\n//        msg.link = \"router\";\n        msg.payload = {\"device\":\"router\",\"Status\":\"online\"}\n        msg1.payload = true;\n    }\n    else\n    {\n        node.status({fill:\"red\",shape:\"dot\",text:\"Dis-Connected\"});\n        msg.link = \"router\";\n//        msg.payload = false;\n        msg.payload = {\"device\":\"router\",\"Status\":\"offline\"}\n        msg1.payload = false;\n    }\n    return [msg,msg1];\n}\n","outputs":2,"noerr":0,"initialize":"","finalize":"","libs":[],"x":340,"y":320,"wires":[["216ee71197f64362"],["4e60e974a200c626"]]},{"id":"aecd4e09.96f4b","type":"function","z":"f4651491.221e58","g":"28ee592794c503b8","name":"Uplink","func":"let msg1 = {};\nconst _topic = global.get(\"uplink_IP\");\nif (msg.topic == _topic)\n{\n    //node.warn(msg.payload);\n    if (msg.payload > 0)\n    {\n        node.status({fill:\"green\",shape:\"dot\",text:\"Connected\"});\n//        msg.link = \"router\";\n        msg.payload = {\"device\":\"pip\",\"Status\":\"online\"}\n        msg1.payload = true\n    }\n    else\n    {\n        node.status({fill:\"red\",shape:\"dot\",text:\"Dis-Connected\"});\n        msg.link = \"router\";\n//        msg.payload = false;\n        msg.payload = {\"device\":\"pip\",\"Status\":\"offline\"}\n        msg1.payload = false\n    }\n    return [msg,msg1];\n}\n","outputs":2,"noerr":0,"initialize":"","finalize":"","libs":[],"x":340,"y":370,"wires":[["216ee71197f64362"],["4e60e974a200c626"]]},{"id":"b4f1ee1d8d012603","type":"function","z":"f4651491.221e58","g":"28ee592794c503b8","name":"Modem","func":"const _topic = '192.168.100.1';\nif (msg.topic == _topic)\n{\n    //node.warn(msg.payload);\n    if (msg.payload > 0)\n    {\n        node.status({fill:\"green\",shape:\"dot\",text:\"Connected\"});\n//        msg.link = \"router\";\n        msg.payload = {\"device\":\"modem\",\"Status\":\"online\"}\n    }\n    else\n    {\n        node.status({fill:\"red\",shape:\"dot\",text:\"Dis-Connected\"});\n        msg.link = \"router\";\n//        msg.payload = false;\n        msg.payload = {\"device\":\"modem\",\"Status\":\"offline\"}\n    }\n    return msg;\n}\n","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":330,"y":420,"wires":[["216ee71197f64362"]]},{"id":"4dd6d0b46d37ba0d","type":"function","z":"f4651491.221e58","g":"28ee592794c503b8","name":"Internet","func":"const _topic = '8.8.8.8';\nif (msg.topic == _topic)\n{\n    //node.warn(msg.payload);\n    if (msg.payload > 0)\n    {\n        node.status({fill:\"green\",shape:\"dot\",text:\"Connected\"});\n//        msg.link = \"router\";\n        msg.payload = {\"device\":\"internet\",\"Status\":\"online\"}\n    }\n    else\n    {\n        node.status({fill:\"red\",shape:\"dot\",text:\"Dis-Connected\"});\n        msg.link = \"router\";\n//        msg.payload = false;\n        msg.payload = {\"device\":\"internet\",\"Status\":\"offline\"}\n    }\n    return msg;\n}\n","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":330,"y":470,"wires":[["216ee71197f64362"]]},{"id":"216ee71197f64362","type":"link out","z":"f4651491.221e58","g":"28ee592794c503b8","name":"link out 29","mode":"link","links":["4cc73847426b0839"],"x":445,"y":470,"wires":[]},{"id":"4cc73847426b0839","type":"link in","z":"f4651491.221e58","g":"28ee592794c503b8","name":"link in 36","links":["216ee71197f64362"],"x":575,"y":470,"wires":[["2bb3411b18df8237"]]},{"id":"2bb3411b18df8237","type":"delay","z":"f4651491.221e58","g":"28ee592794c503b8","name":"rate limit","pauseType":"rate","timeout":"5","timeoutUnits":"seconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"allowrate":false,"outputs":1,"x":645,"y":470,"wires":[["be218399df7f8c83","a35a186da6678f9b"]],"l":false},{"id":"a35a186da6678f9b","type":"function","z":"f4651491.221e58","g":"28ee592794c503b8","name":"Make ONE message *","func":"// Works in Node-RED 3.0+\n\n(async () => {\n\n    let A\n    let B\n    let C\n    let D\n    let counter = context.get(\"counter\") || 0\n\n    const device_name = global.get(\"myDeviceName\")\n\n    if (msg.topic == \"NEWDAY\") {\n        node.send(msg)\n        return\n    }\n\n    if (msg.payload == \"RESET\") {\n        context.set(\"counter\", 0)\n        context.set(\"rounter_status\", \"unknown\")\n        context.set(\"pip_status\", \"unknown\")\n        context.set(\"modem_status\", \"unknown\")\n        context.set(\"internet_status\", \"unknown\")\n        node.status({ text: \"RESET\" })\n        return\n    }\n\n    let device = msg.payload.device\n    let status = msg.payload.Status\n\n    if (device == \"router\") {\n        context.set(\"router\", 1)\n        context.set(\"router_status\", status)\n        context.set(\"counter\", counter + 1)\n        node.status({ fill: \"green\", text: \"router\" })\n    }\n\n    if (device == \"pip\") {\n        context.set(\"pip\", 1)\n        context.set(\"pip_status\", status)\n        context.set(\"counter\", counter + 1)\n        node.status({ fill: \"red\", text: \"Uplink\" })\n    }\n\n    if (device == \"modem\") {\n        context.set(\"modem\", 1)\n        context.set(\"modem_status\", status)\n        context.set(\"counter\", counter + 1)\n        node.status({ fill: \"yellow\", text: \"modem\" })\n    }\n\n    if (device == \"internet\") {\n        context.set(\"internet\", 1)\n        context.set(\"internet_status\", status)\n        context.set(\"counter\", counter + 1)\n        node.status({ fill: \"blue\", text: \"internet\" })\n    }\n\n    A = context.get(\"router_status\")\n    B = context.get(\"pip_status\")\n    C = context.get(\"modem_status\")\n    D = context.get(\"internet_status\")\n    counter = context.get(\"counter\")\n\n    if (counter == 4) {\n\n        msg.payload = {\n            \"Who\": device_name,\n            \"router\": A,\n            \"pip\": B,\n            \"modem\": C,\n            \"internet\": D\n        }\n\n        context.set(\"counter\", 0)\n\n        //  I need a 1 second delay here.\n        await new Promise(resolve => setTimeout(resolve, 1000))\n        node.status({ fill: \"grey\", text: \"reset\" })\n\n        node.send(msg)\n        return\n    }\n\n})()\nreturn\n","outputs":1,"timeout":"","noerr":0,"initialize":"","finalize":"","libs":[],"x":850,"y":470,"wires":[["4b53eb7c1b6415fe","6579ac1985f5a613"]],"info":"2024 09 05\nchanged line 62\nfrom `msg.payload.device`\nto `msg.payload.Who`\n\n2024 09 21\n==\nBig rewrite."},{"id":"6579ac1985f5a613","type":"gate","z":"f4651491.221e58","g":"28ee592794c503b8","name":"B","controlTopic":"control","defaultState":"closed","openCmd":"B","closeCmd":"A","toggleCmd":"toggle","defaultCmd":"default","statusCmd":"","persist":false,"x":1035,"y":470,"wires":[["92590e333b73dc4e"]],"l":false},{"id":"92590e333b73dc4e","type":"mqtt out","z":"f4651491.221e58","g":"28ee592794c503b8","name":"","topic":"STATUS/ADSL_Link_Status/","qos":"0","retain":"true","respTopic":"","contentType":"","userProps":"","correl":"","expiry":"","broker":"1bbfcdd.2d24532","x":1230,"y":470,"wires":[]},{"id":"ebb24c19.2c9048","type":"function","z":"f4651491.221e58","g":"28ee592794c503b8","name":"Check","func":"const time_ = new Date().toLocaleString('en-AU', { hour12: false })\n\nlet Modem = context.get('Modem') || 0\nlet Uplink = context.get('UpLink') || 0\n//node.warn(msg.device)\nif (msg.device == \"router\") {\n    //node.warn(\"Modem test\")\n    //node.warn(\"Status is \" + msg.payload)  //  Number if pinged.\n    //if (msg.payload == \"Line Good\")\n    if (msg.payload > 0) {\n        context.set('Modem', 1)\n        if (Uplink === 0) {\n            node.status({ fill: \"yellow\", shape: \"dot\", text: \"Modem Seen\" })\n        }\n        msg = { payload: 'Online', device: 'Modem', who: global.get(\"myDeviceName\"), time: time_ }\n\n        //        node.warn(***  modem good  ***)\n\n        //\n        //      Construct msg.payload to say modem seen and message from TelePi.\n        //\n        return msg\n    }\n    else {\n        context.set('Modem', 0)\n        node.status({ fill: \"black\", shape: \"dot\", text: \"Modem NOT Seen\" })\n        msg = { payload: 'OffLine', device: 'Modem', who: global.get(\"myDeviceName\"), time: time_ }\n        //\n        //      Construct msg.payload to say modem not seen and message from TelePi.\n        //\n        return msg\n    }\n}\nif (msg.device == \"UpLink\") {\n    if (Modem === 1) {\n        if (msg.payload > 0) {\n            node.status({ fill: \"green\", shape: \"dot\", text: \"UpLink and router Ok\" })\n            msg = { payload: 'Online', device: 'UpLink', who: global.get(\"myDeviceName\"), time: time_ }\n            context.set(\"UpLink\", 1)\n\n            //            node.warn(****  uplink good  ****)\n            //\n            //      This needs modifying to say it is from TelePi.\n            //\n            return msg\n        }\n        else {\n            node.status({ fill: \"red\", shape: \"dot\", text: \"UpLink Down\" })\n            msg = { payload: 'Offline', device: 'UpLink', who: global.get(\"myDeviceName\"), time: time_ }\n            context.set(\"UpLink\", 0)\n            //\n            //      This needs modifying to say it is from TelePi.\n            //\n            return msg\n        }\n    }\n    else {\n        node.status({ fill: \"grey\", shape: \"dot\", text: \"Unknown\" })\n        context.set('MODEM', 0)\n        //\n        //      This needs modifying to say it is from TelePi.\n        //\n        msg = { payload: 'UNKNOWN', device: 'Uplink', who: global.get(\"myDeviceName\"), time: time_ }\n        return msg\n    }\n}\n","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":830,"y":290,"wires":[["ca6954d2.d7de18","c14f8ba9.f016e8"]]},{"id":"1bbfcdd.2d24532","type":"mqtt-broker","name":"TIMEPI MQTT","broker":"192.168.17.39","port":"1883","clientid":"","autoConnect":true,"usetls":false,"compatmode":false,"protocolVersion":"4","keepalive":"60","cleansession":true,"birthTopic":"SOM","birthQos":"2","birthRetain":"false","birthPayload":"TelePi Comms UP","birthMsg":{},"closeTopic":"EOM","closeQos":"0","closeRetain":"false","closePayload":"TelePi shutting DOWN","closeMsg":{},"willTopic":"EOM","willQos":"0","willRetain":"false","willPayload":"TelePi Comms FAILURE","willMsg":{},"sessionExpiry":""}]

This is the main bit. As you may see there are other parts/branches going elsewhere doing their thing. I included the function node as it is handy to see as it too indicates the statuses of the devices.

Picture to help understand too.

This is the decoding code:

[{"id":"f7b1610d.876c5","type":"mqtt in","z":"f4651491.221e58","g":"0281319e741d201a","name":"Internet link status","topic":"STATUS/ADSL_Link_Status/#","qos":"2","datatype":"auto","broker":"1bbfcdd.2d24532","nl":false,"rap":false,"inputs":0,"x":310,"y":1460,"wires":[["d50b4d6.59dc73"]]},{"id":"d50b4d6.59dc73","type":"json","z":"f4651491.221e58","g":"0281319e741d201a","name":"Router/Uplink status","property":"payload","action":"","pretty":false,"x":295,"y":1400,"wires":[["b0db4ba59311ae7a"]],"l":false},{"id":"b0db4ba59311ae7a","type":"change","z":"f4651491.221e58","g":"0281319e741d201a","name":"get delay time","rules":[{"t":"set","p":"delay","pt":"msg","to":"$globalContext(\"SCAN_TIME\") * 3","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":355,"y":1400,"wires":[["5d04fddb689e7492"]],"l":false},{"id":"5d04fddb689e7492","type":"gate","z":"f4651491.221e58","g":"0281319e741d201a","name":"Master STOP","controlTopic":"CONTROL3","defaultState":"open","openCmd":"GO","closeCmd":"STOP","toggleCmd":"toggle","defaultCmd":"default","statusCmd":"status","persist":false,"storeName":"memory","x":415,"y":1400,"wires":[["ef4ffc8e4aa932d3"]],"l":false},{"id":"2d5448ed.f7eda","type":"switch","z":"f4651491.221e58","g":"0281319e741d201a","name":"Whic device's message","property":"payload.Who","propertyType":"msg","rules":[{"t":"eq","v":"BedPi","vt":"str"},{"t":"eq","v":"TelePi","vt":"str"},{"t":"eq","v":"TimePi","vt":"str"}],"checkall":"true","repair":false,"outputs":3,"x":685,"y":1400,"wires":[["7c425021.22e4b8","76d8311c4b394f6b"],["8d6ff708.0509a8","76d8311c4b394f6b"],["a95028815942d0e0","e4f887a60fe797e5","76d8311c4b394f6b"]],"l":false},{"id":"7c425021.22e4b8","type":"trigger","z":"f4651491.221e58","g":"0281319e741d201a","name":"BedPi","op1":"","op2":"offline","op1type":"pay","op2type":"str","duration":"80","extend":true,"overrideDelay":true,"units":"s","reset":"","bytopic":"all","topic":"topic","outputs":1,"x":810,"y":1330,"wires":[["2c25412de15859fd"]]},{"id":"8d6ff708.0509a8","type":"trigger","z":"f4651491.221e58","g":"0281319e741d201a","name":"TelePi","op1":"","op2":"offline","op1type":"pay","op2type":"str","duration":"80","extend":true,"overrideDelay":true,"units":"s","reset":"","bytopic":"all","topic":"topic","outputs":1,"x":810,"y":1380,"wires":[["0cdf66dcfb1d2a84"]]},{"id":"e4f887a60fe797e5","type":"trigger","z":"f4651491.221e58","g":"0281319e741d201a","name":"TimePi","op1":"","op2":"offline","op1type":"pay","op2type":"str","duration":"80","extend":true,"overrideDelay":true,"units":"s","reset":"","bytopic":"all","topic":"topic","outputs":1,"x":810,"y":1430,"wires":[["025688311b9ca9ed"]]},{"id":"025688311b9ca9ed","type":"function","z":"f4651491.221e58","g":"0281319e741d201a","name":"TimePi","func":"msg = { \"topic\": \"STATUS/ADSL_Link_Status\", \"payload\": { \"Who\": \"TimePi\", \"Modem\": \"Offline\", \"UpLink\": \"Offline\" } }\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":905,"y":1430,"wires":[["6176f7af89e288f4"]],"l":false},{"id":"0cdf66dcfb1d2a84","type":"function","z":"f4651491.221e58","g":"0281319e741d201a","name":"TelePi","func":"msg = { \"topic\": \"STATUS/ADSL_Link_Status\", \"payload\": { \"Who\": \"TelePi\", \"Modem\": \"Offline\", \"UpLink\": \"Offline\" } }\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":905,"y":1380,"wires":[["6176f7af89e288f4"]],"l":false},{"id":"2c25412de15859fd","type":"function","z":"f4651491.221e58","g":"0281319e741d201a","name":"BedPi","func":"msg = { \"topic\": \"STATUS/ADSL_Link_Status\", \"payload\": { \"Who\": \"BedPi\", \"Modem\": \"Offline\", \"UpLink\": \"Offline\" } }\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":905,"y":1330,"wires":[["6176f7af89e288f4"]],"l":false},{"id":"6176f7af89e288f4","type":"junction","z":"f4651491.221e58","g":"0281319e741d201a","x":980,"y":1400,"wires":[["b7dfa060.6a4218"]]},{"id":"b7dfa060.6a4218","type":"link out","z":"f4651491.221e58","g":"0281319e741d201a","name":"","links":["9ed8578c5ce3be0b"],"x":1035,"y":1400,"wires":[]},{"id":"9ed8578c5ce3be0b","type":"link in","z":"f4651491.221e58","g":"625538364d766902","name":"","links":["b7dfa060.6a4218"],"x":1185,"y":1400,"wires":[["e990f055317a6d64","941bb21fce19482b"]]},{"id":"941bb21fce19482b","type":"function","z":"f4651491.221e58","g":"625538364d766902","name":"Modem/Uplink status Working 2025 11 11","func":"// 2025-11-11 2045 — Colour logic: local vs remote, with SPRINGGREEN when local offline, RED if all offline, and BLACK for unknown\n\nfunction makeIcon(colour, icon = \"fa-bullseye\") {\n    return `<font color=\"${colour}\"><i class=\"fa ${icon} fa-2x\"></i></font>`\n}\n\n// Determine colour for this subsystem based on all three Pis\nfunction getColour(localState, remoteStates) {\n    // Check if localState and remoteStates are valid, default to 'offline' or 'unknown' if not\n    localState = (localState && localState.toLowerCase()) || 'offline'\n    remoteStates = remoteStates.map(state => (state && state.toLowerCase()) || 'offline')\n\n    // If ALL are 'unknown' → BLACK\n    if ([localState, ...remoteStates].every(s => s === \"unknown\")) return \"black\"\n\n    // If ALL are offline → RED\n    if ([localState, ...remoteStates].every(s => s === \"offline\")) return \"red\"\n\n    // If THIS Pi is offline → SPRINGGREEN\n    if (localState === \"offline\") return \"springgreen\"\n\n    // THIS Pi is online, determine based on remotes\n    const onlineCount = remoteStates.filter(s => s === \"online\").length\n    if (onlineCount === 2) return \"lime\"        // all 3 online\n    if (onlineCount === 1) return \"green\"       // one other offline\n\n    return \"springgreen\"                        // both others offline\n}\n\n// --- get or create persistent state ---\nlet state = context.get(\"state\") || {\n    router: {}, pip: {}, modem: {}, uplink: {}\n}\n\n// --- parse message ---\nconst p = msg.payload\nif (!p || !p.Who) return null\n\nconst who = p.Who // e.g. \"TimePi\"\nconst thisPi = global.get(\"ThisPiName\") || \"TimePi\" // local device name\n\n// rename \"internet\" → \"uplink\"\np.uplink = p.internet\n\n// update stored state\nfor (let key of [\"router\", \"pip\", \"modem\", \"uplink\"]) {\n    if (p[key]) state[key][who] = p[key]\n}\n\n// helper: determine colour for one subsystem\nfunction colourFor(subsys) {\n    const all = state[subsys]\n    const local = all[thisPi]\n    const remotes = Object.entries(all)\n        .filter(([k]) => k !== thisPi)\n        .map(([_, v]) => v)\n    return getColour(local, remotes)\n}\n\n// compute colours\nconst routerColour = colourFor(\"router\")\nconst pipColour = colourFor(\"pip\")\nconst modemColour = colourFor(\"modem\")\nconst uplinkColour = colourFor(\"uplink\")\n\n// build 4 outgoing messages\nlet msg1 = { payload: makeIcon(routerColour), colour: routerColour }\nlet msg2 = { payload: makeIcon(pipColour), colour: pipColour }\nlet msg3 = { payload: makeIcon(modemColour), colour: modemColour }\nlet msg4 = { payload: makeIcon(uplinkColour), colour: uplinkColour }\n\n// save updated state\ncontext.set(\"state\", state)\n\n// optional: show quick status\nnode.status({\n    text: `r:${routerColour} p:${pipColour} m:${modemColour} u:${uplinkColour}`\n})\n\n// output\nreturn [msg1, msg2, msg3, msg4]\n","outputs":4,"timeout":"","noerr":0,"initialize":"","finalize":"","libs":[],"x":1520,"y":1400,"wires":[["78bee52b04dedd1f","8729ed4697d99590"],["11e761b006a3855f","61107b78e705a231"],["1a80600afcc60d67"],["9cef804b9ddfc40a"]],"outputLabels":["router","pip","modem (Telstra)","internet"],"info":"2025 11 11\n==\n| This Pi | Remote 1 | Remote 2 | Colour          | Meaning                 |\n| ------- | -------- | -------- | --------------- | ----------------------- |\n| online  | online   | online   | **LIME**        | All OK                  |\n| online  | online   | offline  | **GREEN**       | One other offline       |\n| online  | offline  | offline  | **SPRINGGREEN** | Only this Pi online     |\n| offline | (any)    | (any)    | **SPRINGGREEN** | Local subsystem offline |\n| offline | offline  | offline  | **RED**         | All Pis offline         |\n\n\n\n2025 07 29\n==\nModified code.\nMade functions for colours.\nIf public_IP starts with 1.x icon is wifi\n\n\n2024 09 21\n==\n\nBig restructure\nadded startup code to wipe values\n"},{"id":"1bbfcdd.2d24532","type":"mqtt-broker","name":"TIMEPI MQTT","broker":"192.168.17.39","port":"1883","clientid":"","autoConnect":true,"usetls":false,"compatmode":false,"protocolVersion":"4","keepalive":"60","cleansession":true,"birthTopic":"SOM","birthQos":"2","birthRetain":"false","birthPayload":"TelePi Comms UP","birthMsg":{},"closeTopic":"EOM","closeQos":"0","closeRetain":"false","closePayload":"TelePi shutting DOWN","closeMsg":{},"willTopic":"EOM","willQos":"0","willRetain":"false","willPayload":"TelePi Comms FAILURE","willMsg":{},"sessionExpiry":""}]

I didn't include my subflow (advblocker) as it isn't really important and I don't want to pollute the code. Just wire round it.

And again, there is more to the code, but for the sake of being as brief as possible, nothing else is really important. (AFAIK)

These are the parts that do the Heavy lifting.

TODAY I got up and all three machines said the uplink was down.

Yet looking at the actual device: it wasn't.
The IP address was ok, and it cleared when I resent the IP address to all machines.
So something is happening there. But I'm not seeing anything to indicate a problem.
(And ok: I may not - probably didn't post that part of the code. No worries. But just saying.)

Not wanting to go too far off topic, but from a previous day when this sort of happened but only 1 machine said the internet was down.

When I went to a terminal and tried to ping (sorry traceroute) the IP address it didn't go too far.
I can't remember the exact IP addresses, but it got to the route (I think) and became 127.0.0.x I think.

Oh dear!
I just saw that my IP address WAS changed.

But this reflects when I rebroadcasted the nw IP address. NOT when it changed.
Which was (Probably) at 03:46:31 when the uplink went offline.
Ok, yeah. That's when it went off line, not when I got the new IP address.

I'll have to look into that part too.