MQTT connect/disconnect every 15 seconds

Got a real weird one now. Thought I had sorted a good way to do mqtt connect + subscribe, but I have problem with it again.

I have this mqtt setup running on several raspberry pis. Normally this runs smooth with little to no hickup. Last week we started a new pi, so I copied the code over. And updated .env to get new connection credentials.

But on this new pi, connection toggles between connected / disconnected every 15 seconds! What a weird behavior? Was similar some times last year on another pi, but it sorted itself out before I spent more time on it.

I'm not sure where to look for details here as I don't find any info for why mqtt disconnects.

Thanks in advance to anyone who can share their thoughts!

Nodes:

[
    {
        "id": "a5184c5aab2f6cda",
        "type": "subflow",
        "name": "log",
        "info": "# Writes log to file\r\nMust set env variable `logTopic` in parent flow to determine filename.\r\nThis can't be done as input variable because the parent may also be a subflow.\r\n\r\n### Inputs\r\n\r\n: logMessage (string)          :  the log message to write to file can be provided in the node configuration, or as a message property. By default it will use `msg.logMessage`.\r\n: logTopic (string)            :  determines parts of the file name. Typically enter flow name for this purpose.\r\n: logCompleteMsgObj (boolean):  Add the entire msg object to the log message.\r\n: stdOut (boolean)           :  Also log to standard out (using debug node).\r\n\r\n### Details\r\n\r\nLogs are written to /data/logs/`msg.logTopic`_yyyy-MM-dd.txt. `msg.logTopic` is used as filename prefix. Typically use flow name for this purpose. Creates file and folder if they don't exist.\r\n\r\n`msg.logMessage` is used as the main content of the log to write. Datetime is automatically added as prefix. Each log is appended with newline.",
        "category": "",
        "in": [
            {
                "x": 100,
                "y": 80,
                "wires": [
                    {
                        "id": "0db769e63c22272e"
                    }
                ]
            }
        ],
        "out": [
            {
                "x": 1040,
                "y": 80,
                "wires": [
                    {
                        "id": "5f7d3b7030f7bb02",
                        "port": 0
                    },
                    {
                        "id": "0db769e63c22272e",
                        "port": 0
                    }
                ]
            }
        ],
        "env": [
            {
                "name": "logMessage",
                "type": "str",
                "value": "",
                "ui": {
                    "label": {
                        "en-US": "Log message"
                    },
                    "type": "input",
                    "opts": {
                        "types": [
                            "str"
                        ]
                    }
                }
            },
            {
                "name": "logCompleteMsgObj",
                "type": "bool",
                "value": "false",
                "ui": {
                    "label": {
                        "en-US": "Log complete msg"
                    },
                    "type": "checkbox"
                }
            },
            {
                "name": "stdOut",
                "type": "bool",
                "value": "false",
                "ui": {
                    "label": {
                        "en-US": "Debug (sidebar)"
                    },
                    "type": "checkbox"
                }
            },
            {
                "name": "logTopic",
                "type": "env",
                "value": "$parent.logTopic",
                "ui": {
                    "type": "hide"
                }
            }
        ],
        "meta": {},
        "color": "#C7E9C0",
        "icon": "font-awesome/fa-file-text"
    },
    {
        "id": "b1add847796af765",
        "type": "function",
        "z": "a5184c5aab2f6cda",
        "name": "prepare log",
        "func": "msg._backup = RED.util.cloneMessage(msg);\n\nconst date = new Date().toISOString().replaceAll(\"-\", \"-\").replaceAll(\"T\", \"-\").replaceAll(\":\", \"-\").replaceAll(\".\", \"-\").replaceAll(\"Z\", \"-\");\nconst list = date.split(\"-\");\nconst year = list[0];\nconst month = list[1];\nconst day = list[2];\nconst hour = list[3];\nconst minute = list[4];\nconst second = list[5];\n\nlet logTopic;\ntry{\n    logTopic = env.get(\"logTopic\").toLowerCase();\n    if(!logTopic){\n        node.error(\"ERROR: Log failed getting logTopic env from parent. Did you remember to set it?\");\n        return;\n    }\n} catch (error) {\n    node.error(\"ERROR: Log failed getting logTopic env from parent. Did you remember to set it?\");\n    return;\n}\n\nmsg.stdOut = env.get(\"stdOut\");\n\nlet logMessage = env.get(\"logMessage\") ? env.get(\"logMessage\") : msg.logMessage;\nlet logCompleteMsgObj = env.get(\"logCompleteMsgObj\");\nif(!logCompleteMsgObj){\n    logCompleteMsgObj = msg?.logCompleteMsgObj ?? false;\n}\n\nif(logCompleteMsgObj){\n    logMessage += \"\\n\";\n    logMessage += JSON.stringify(msg._backup);\n}\n\nmsg.filename = `/data/logs/${logTopic}/${logTopic}_${year}-${month}-${day}.log`;\nmsg.payload = `${year}-${month}-${day} ${hour}:${minute}:${second} - ${logMessage}`;\n\nconst maxLineLength = 10000;\nif(msg.payload.length > maxLineLength){\n    // Safeguard against excessively large logs\n    msg.payload = msg.payload.slice(0, maxLineLength) + '... [TRUNCATED]';\n}\n\nreturn msg;",
        "outputs": 1,
        "timeout": 0,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 470,
        "y": 140,
        "wires": [
            [
                "3ea911242530d241",
                "2c7e47352b7df809"
            ]
        ]
    },
    {
        "id": "3ea911242530d241",
        "type": "file",
        "z": "a5184c5aab2f6cda",
        "name": "write log file",
        "filename": "filename",
        "filenameType": "msg",
        "appendNewline": true,
        "createDir": true,
        "overwriteFile": "false",
        "encoding": "utf8",
        "x": 670,
        "y": 140,
        "wires": [
            [
                "5f7d3b7030f7bb02"
            ]
        ]
    },
    {
        "id": "e96bad283c240487",
        "type": "catch",
        "z": "a5184c5aab2f6cda",
        "name": "",
        "scope": null,
        "uncaught": false,
        "x": 660,
        "y": 260,
        "wires": [
            [
                "5ac9480a70f4d93a"
            ]
        ]
    },
    {
        "id": "5ac9480a70f4d93a",
        "type": "debug",
        "z": "a5184c5aab2f6cda",
        "name": "log failed error",
        "active": true,
        "tosidebar": true,
        "console": true,
        "tostatus": false,
        "complete": "true",
        "targetType": "full",
        "statusVal": "",
        "statusType": "auto",
        "x": 850,
        "y": 260,
        "wires": []
    },
    {
        "id": "fac2f56d19607dc3",
        "type": "debug",
        "z": "a5184c5aab2f6cda",
        "name": "log std out",
        "active": true,
        "tosidebar": true,
        "console": true,
        "tostatus": false,
        "complete": "payload",
        "targetType": "msg",
        "statusVal": "",
        "statusType": "auto",
        "x": 830,
        "y": 200,
        "wires": []
    },
    {
        "id": "2c7e47352b7df809",
        "type": "switch",
        "z": "a5184c5aab2f6cda",
        "name": "std out?",
        "property": "stdOut",
        "propertyType": "msg",
        "rules": [
            {
                "t": "true"
            },
            {
                "t": "else"
            }
        ],
        "checkall": "true",
        "repair": false,
        "outputs": 2,
        "x": 660,
        "y": 200,
        "wires": [
            [
                "fac2f56d19607dc3"
            ],
            []
        ],
        "inputLabels": [
            "msg.stdOut"
        ],
        "outputLabels": [
            "true",
            "false"
        ]
    },
    {
        "id": "5f7d3b7030f7bb02",
        "type": "function",
        "z": "a5184c5aab2f6cda",
        "name": "revert",
        "func": "return msg._backup;",
        "outputs": 1,
        "timeout": 0,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 810,
        "y": 140,
        "wires": [
            []
        ]
    },
    {
        "id": "0db769e63c22272e",
        "type": "function",
        "z": "a5184c5aab2f6cda",
        "name": "logMessage?",
        "func": "const logMessage = env.get(\"logMessage\") ? env.get(\"logMessage\") : msg.logMessage;\n\nconst skip = logMessage ? null : msg;\nconst log = logMessage ? msg : null;\nreturn [skip, log];",
        "outputs": 2,
        "timeout": 0,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 220,
        "y": 80,
        "wires": [
            [],
            [
                "b1add847796af765"
            ]
        ],
        "outputLabels": [
            "Skip",
            "Log"
        ]
    },
    {
        "id": "71082c29770ea643",
        "type": "inject",
        "z": "a5184c5aab2f6cda",
        "g": "6f8fb577b11364ed",
        "name": "",
        "props": [],
        "repeat": "",
        "crontab": "00 00 * * *",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "x": 210,
        "y": 400,
        "wires": [
            [
                "09a9a5f729dd65b4"
            ]
        ]
    },
    {
        "id": "0c31a507622aeb8d",
        "type": "file",
        "z": "a5184c5aab2f6cda",
        "g": "6f8fb577b11364ed",
        "name": "write empty file",
        "filename": "filename",
        "filenameType": "msg",
        "appendNewline": false,
        "createDir": true,
        "overwriteFile": "false",
        "encoding": "utf8",
        "x": 540,
        "y": 400,
        "wires": [
            []
        ]
    },
    {
        "id": "09a9a5f729dd65b4",
        "type": "function",
        "z": "a5184c5aab2f6cda",
        "g": "6f8fb577b11364ed",
        "name": "empty log",
        "func": "const date = new Date().toISOString().replaceAll(\"-\", \"-\").replaceAll(\"T\", \"-\").replaceAll(\":\", \"-\").replaceAll(\".\", \"-\").replaceAll(\"Z\", \"-\");\nconst list = date.split(\"-\");\nconst year = list[0]; // YYYY\nconst month = list[1]; // MM\nconst day = list[2]; // DD\n\nconst logTopic = env.get(\"$parent.logTopic\").toLowerCase();\nif(!logTopic){\n  return; // silent quit\n}\n\nmsg.filename = `/data/logs/${logTopic}/${logTopic}_${year}-${month}-${day}.log`;\nmsg.payload = \"\";\n\nreturn msg;",
        "outputs": 1,
        "timeout": 0,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 370,
        "y": 400,
        "wires": [
            [
                "0c31a507622aeb8d"
            ]
        ]
    },
    {
        "id": "6f8fb577b11364ed",
        "type": "group",
        "z": "a5184c5aab2f6cda",
        "name": "Create new log file every day",
        "style": {
            "stroke": "#0070c0",
            "fill": "#bfdbef",
            "label": true,
            "color": "#000000"
        },
        "nodes": [
            "0c31a507622aeb8d",
            "09a9a5f729dd65b4",
            "71082c29770ea643"
        ],
        "x": 114,
        "y": 359,
        "w": 532,
        "h": 82
    },
    {
        "id": "5b9dee777ab3c51a",
        "type": "function",
        "z": "97437e63b6ef93ef",
        "g": "a756d21da814ee7f",
        "name": "connect",
        "func": "const output = {\n    action: \"connect\", // \"connect\" / \"subscribe\"\n    topic: \"v1/devices/me/rpc/request/+\",\n\n    broker: {\n        broker: \"mqtt.thingsboard.cloud\",\n        port: 1883,\n        \n        force: false, \n        cleansession: false,\n        protocolVersion: 5,\n\n        clientid: env.get(\"TB_MQTT_CLIENT_ID\"),\n        username: env.get(\"TB_MQTT_ACCESS_TOKEN\"),\n        //password: env.get(\"password\")\n    }\n};\n\nconst connectMsg = RED.util.cloneMessage(output);\nconst subscribeMsg = RED.util.cloneMessage(output);\nsubscribeMsg.action = \"subscribe\";\n\nreturn [[connectMsg, subscribeMsg]];",
        "outputs": 1,
        "timeout": 0,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 840,
        "y": 280,
        "wires": [
            [
                "a976cec64a7db708"
            ]
        ]
    },
    {
        "id": "6e4976dd19813128",
        "type": "inject",
        "z": "97437e63b6ef93ef",
        "g": "a756d21da814ee7f",
        "name": "connect & subscribe",
        "props": [
            {
                "p": "timestamp",
                "v": "",
                "vt": "date"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": true,
        "onceDelay": 0.1,
        "topic": "",
        "x": 240,
        "y": 280,
        "wires": [
            [
                "796b5dc9d1733fe9"
            ]
        ]
    },
    {
        "id": "616ac68b4c37c932",
        "type": "status",
        "z": "97437e63b6ef93ef",
        "g": "a756d21da814ee7f",
        "name": "",
        "scope": [
            "a976cec64a7db708"
        ],
        "x": 200,
        "y": 340,
        "wires": [
            [
                "b5bf24d17b3a6e18"
            ]
        ]
    },
    {
        "id": "b5bf24d17b3a6e18",
        "type": "switch",
        "z": "97437e63b6ef93ef",
        "g": "a756d21da814ee7f",
        "name": "connected?",
        "property": "status.text",
        "propertyType": "msg",
        "rules": [
            {
                "t": "eq",
                "v": "node-red:common.status.connected",
                "vt": "str"
            },
            {
                "t": "eq",
                "v": "node-red:common.status.disconnected",
                "vt": "str"
            }
        ],
        "checkall": "false",
        "repair": false,
        "outputs": 2,
        "x": 350,
        "y": 340,
        "wires": [
            [
                "3d133c46811031da"
            ],
            [
                "66d02ea984627117"
            ]
        ],
        "outputLabels": [
            "connected",
            "disconnected"
        ]
    },
    {
        "id": "66d02ea984627117",
        "type": "subflow:a5184c5aab2f6cda",
        "z": "97437e63b6ef93ef",
        "g": "a756d21da814ee7f",
        "name": "log",
        "env": [
            {
                "name": "logMessage",
                "value": "MQTT disconnected, attempting to re-connect.",
                "type": "str"
            },
            {
                "name": "logTopic",
                "value": "$parent.logTopic",
                "type": "env"
            }
        ],
        "x": 530,
        "y": 360,
        "wires": [
            [
                "8ad508cf707b872d"
            ]
        ]
    },
    {
        "id": "3d133c46811031da",
        "type": "subflow:a5184c5aab2f6cda",
        "z": "97437e63b6ef93ef",
        "g": "a756d21da814ee7f",
        "name": "log",
        "env": [
            {
                "name": "logMessage",
                "value": "MQTT connected.",
                "type": "str"
            },
            {
                "name": "logTopic",
                "value": "$parent.logTopic",
                "type": "env"
            }
        ],
        "x": 530,
        "y": 320,
        "wires": [
            []
        ]
    },
    {
        "id": "a976cec64a7db708",
        "type": "mqtt in",
        "z": "97437e63b6ef93ef",
        "g": "a756d21da814ee7f",
        "name": "mqtt subscribe",
        "topic": "",
        "qos": "0",
        "datatype": "json",
        "broker": "c032668b54c22629",
        "nl": true,
        "rap": true,
        "rh": "2",
        "inputs": 1,
        "x": 1000,
        "y": 280,
        "wires": [
            [
                "1c21e709aefadba1"
            ]
        ]
    },
    {
        "id": "c99afae0c869b98b",
        "type": "catch",
        "z": "97437e63b6ef93ef",
        "g": "a756d21da814ee7f",
        "name": "",
        "scope": "group",
        "uncaught": true,
        "x": 210,
        "y": 420,
        "wires": [
            [
                "211330c1380ef9c3"
            ]
        ]
    },
    {
        "id": "c279cb6c019b09d2",
        "type": "subflow:a5184c5aab2f6cda",
        "z": "97437e63b6ef93ef",
        "g": "a756d21da814ee7f",
        "name": "log",
        "env": [
            {
                "name": "logMessage",
                "value": "Unexpected error.",
                "type": "str"
            },
            {
                "name": "logCompleteMsgObj",
                "value": "true",
                "type": "bool"
            },
            {
                "name": "logTopic",
                "value": "$parent.logTopic",
                "type": "env"
            }
        ],
        "x": 490,
        "y": 420,
        "wires": [
            []
        ]
    },
    {
        "id": "8ad508cf707b872d",
        "type": "delay",
        "z": "97437e63b6ef93ef",
        "g": "a756d21da814ee7f",
        "name": "",
        "pauseType": "delay",
        "timeout": "15",
        "timeoutUnits": "seconds",
        "rate": "1",
        "nbRateUnits": "1",
        "rateUnits": "second",
        "randomFirst": "1",
        "randomLast": "5",
        "randomUnits": "seconds",
        "drop": false,
        "allowrate": false,
        "outputs": 1,
        "x": 660,
        "y": 360,
        "wires": [
            [
                "5b9dee777ab3c51a"
            ]
        ]
    },
    {
        "id": "211330c1380ef9c3",
        "type": "function",
        "z": "97437e63b6ef93ef",
        "g": "a756d21da814ee7f",
        "name": "clean log",
        "func": "// hide access token from logs\nif(msg?.broker?.username){\n    msg.broker.username = \"MQTT Access Token\";\n}\nif(msg?._backup?.broker?.username){\n    msg._backup.broker.username = \"MQTT Access Token\";\n}\nreturn msg;",
        "outputs": 1,
        "timeout": 0,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 360,
        "y": 420,
        "wires": [
            [
                "c279cb6c019b09d2"
            ]
        ]
    },
    {
        "id": "796b5dc9d1733fe9",
        "type": "delay",
        "z": "97437e63b6ef93ef",
        "g": "a756d21da814ee7f",
        "name": "",
        "pauseType": "delay",
        "timeout": "5",
        "timeoutUnits": "seconds",
        "rate": "1",
        "nbRateUnits": "1",
        "rateUnits": "second",
        "randomFirst": "1",
        "randomLast": "5",
        "randomUnits": "seconds",
        "drop": false,
        "allowrate": false,
        "outputs": 1,
        "x": 660,
        "y": 280,
        "wires": [
            [
                "5b9dee777ab3c51a"
            ]
        ]
    },
    {
        "id": "c032668b54c22629",
        "type": "mqtt-broker",
        "name": "tb-nr-mqtt",
        "broker": "thingsboard",
        "port": "1883",
        "clientid": "",
        "autoConnect": false,
        "usetls": false,
        "protocolVersion": "5",
        "keepalive": "60",
        "cleansession": true,
        "autoUnsubscribe": true,
        "birthTopic": "",
        "birthQos": "0",
        "birthRetain": "false",
        "birthPayload": "",
        "birthMsg": {},
        "closeTopic": "",
        "closeQos": "0",
        "closeRetain": "false",
        "closePayload": "",
        "closeMsg": {},
        "willTopic": "",
        "willQos": "0",
        "willRetain": "false",
        "willPayload": "",
        "willMsg": {},
        "userProps": "",
        "sessionExpiry": ""
    }
]

Did you set a UNIQUE client Id for the 2nd PI?

MQTT uses client id to identify a connection & will disconnect any existing ones if finds. this leads to a round robin effect of 1:connect, 2:connect, 1: disconnect, 1: reconnect, 2: disconnect etc

3 Likes

Yeah this has unique client id to this pi, checked in debug. It's a customer-related name. I also modified it further to test without prevail.

I'm not sure if this is a problem in the code, a network problem local to the pi or a remote server problem (Thingsboard).

This is what I get from node red (via docker logs):

18 Mar 15:55:35 - [info] [mqtt-broker:tb-nr-mqtt] Connected to broker: instance-5-5123@mqtt://mqtt.thingsboard.cloud:1883
18 Mar 15:55:50 - [info] [mqtt-broker:tb-nr-mqtt] Broker instance-5-5123@mqtt://mqtt.thingsboard.cloud:1883 disconnected client: 142
18 Mar 15:56:05 - [info] [mqtt-broker:tb-nr-mqtt] Connected to broker: instance-5-5123@mqtt://mqtt.thingsboard.cloud:1883
18 Mar 15:56:21 - [info] [mqtt-broker:tb-nr-mqtt] Broker instance-5-5123@mqtt://mqtt.thingsboard.cloud:1883 disconnected client: 142
18 Mar 15:56:36 - [info] [mqtt-broker:tb-nr-mqtt] Connected to broker: instance-5-5123@mqtt://mqtt.thingsboard.cloud:1883
18 Mar 15:56:51 - [info] [mqtt-broker:tb-nr-mqtt] Broker instance-5-5123@mqtt://mqtt.thingsboard.cloud:1883 disconnected client: 142
18 Mar 15:57:06 - [info] [mqtt-broker:tb-nr-mqtt] Connected to broker: instance-5-5123@mqtt://mqtt.thingsboard.cloud:1883
18 Mar 15:57:22 - [info] [mqtt-broker:tb-nr-mqtt] Broker instance-5-5123@mqtt://mqtt.thingsboard.cloud:1883 disconnected client: 142

I think thingsboard has a relatively low timeout. Try reducing keep alive to 25 or 14 - OR - try reading a value every 10s (to keep things moving)

Does thingsboard support MQTT v5? V5 has much richer disconnect reasons. Might shed some light.

Failing that, I would switch off PI1 and see if the connection persists. Might lead to other clues.

1 Like

What happens if you disconnect the wire between the Delay 15s node and the Connect function?

1 Like

This is my MQTT settings on a pi that has stable working mqtt connection:

const output = {
    action: "connect", // "connect" / "subscribe"
    topic: "v1/devices/me/rpc/request/+",

    broker: {
        broker: env.get("TB_MQTT_BROKER"),
        port: 1883,
        
        force: false, 
        cleansession: false,
        protocolVersion: 5,

        clientid: env.get("TB_MQTT_CLIENT_ID"),
        username: env.get("TB_MQTT_USER"),
        password: env.get("TB_MQTT_PASS")
    }
};

const connectMsg = RED.util.cloneMessage(output);
const subscribeMsg = RED.util.cloneMessage(output);
subscribeMsg.action = "subscribe";

return [[connectMsg, subscribeMsg]];

It's the darndest thing, same exact code runs on the other pis but against other TB devices?!? I know this has a slightly different credentials because TB offers 2 different ways, and this use clientId, username and password (a bit overkill to have 3 values), but still that shouldn't make any difference.

Have you tried disconnecting the wire I suggested?

1 Like

Sorry missed that. Yeah I did so earlier today. AFAIK the connection kept toggling! Will investigate more tomorrow. It may be something silly that the same clientId is re-used accidentally somewhere, or some weird device seting in Thingsboard that makes a difference.

The co-incidence of a 15 second delay and the reconnection time is enough to make me suspect that there is a feedback loop involving that delay, which is why I think it is worth trying again.

To check the client id possibility just remove that from the message. Then node-red will allocate a unique id for you.

1 Like

Yeah that was suspicious to me too, however I did reduce it to 5 sec and still connection only lasted 15 seconds. Will double check that later.

Sorry, just passing by.

clientid: env.get("TB_MQTT_CLIENT_ID")`

I'm kinda missing what's going on with this.

I am a big user of subflows myself.

Excuse me for asking, but looking at the code and the pictures you posted I see a subflow being defined/declared but can't see it in the picture of the flow you posted.
Ok, log. My bad.

Only for the sake of getting all the cards on the table would you mind sharing all the device names and what you TB_MQTT_BROKER name is?

Don't need the usernames/passwords.

But as it was mentioned if you have 2 clients with the same names, things get crazy.

I'm also not getting what this code is supposed to do.
Sorry if that is out of scope.

(And I am not using MQTT-5) but I take it you too aren't.

Oh, last minute quickie....

Could you open the mqtt subscribe node and take a screen print and post it please?

1 Like

That would be my bet...
Typically, this kind of flip-flop is due to reuse of the same client-id from multiple source as Steve suggested.

2 Likes

Did you get to the bottom of this?

Sorry got so busy with another node red task. So much to do :smiley: I'll do some more controlled/organized testing next week and post back here. In the mean time, thanks all for creating such a strong and lively community for node red!

Today we solved the problem. Thinking outside the box and trying everything. It turned out a dev pi we haven't used in ages actually used the same username (thingsboard use this as access token). And it had 15 seconds delay haha. So even if I changed delay in production, they still crash every 15 seconds. Doh! Learn something new every day. Thanks a lot for your help and ideas, you actually hinted to this as the cause.

You might want to consider using a Trigger node rather than a Delay node for the 15 seconds watchdog. That has the advantage that if the status changes back to Connected before the delay has run out then you can use that to cancel the pending reconnect, so removing the possibility of getting a loop round the delay. Something like this
image

[{"id":"e949ef20b7e04fa0","type":"trigger","z":"bdd7be38.d3b55","name":"Disconnected watchdog","op1":"","op2":"","op1type":"nul","op2type":"pay","duration":"15","extend":false,"overrideDelay":false,"units":"s","reset":"node-red:common.status.connected","bytopic":"all","topic":"topic","outputs":1,"x":860,"y":4640,"wires":[["2ba6e2e04c02d893"]]},{"id":"49bbb671073e15f6","type":"change","z":"bdd7be38.d3b55","name":"Move status text to payload","rules":[{"t":"move","p":"status.text","pt":"msg","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":590,"y":4640,"wires":[["e949ef20b7e04fa0"]]}]

The Switch node is still needed in order to ignore Connecting messages.

1 Like

Glorious! Always on the lookout for cool solutions.