Two way switch infinite loop

I have the switches as MQTT in and MQTT out.

OK, now I can import the flow I see you are not using dashboard switches.

The technique to debug this is to sprinkle node.warn() statements throughout the code. The output from those will go into the node red log. Start the loop going and then stop node-red. You can then look back through the log and work out exactly what is happening so you can see how to fix it.

that would go in the function section?

node.warn("some string") outputs the text to the log, so you can use them to show the flow through the function by inserting them at appropriate points. So you might add the line
node.warn("Point A")
to indicate a particular point, or you might output the value of a variable at that point

node.warn(`Point C.  msg.payload is ${msg.payload}`)

which will include the current msg.payload value in the text.

I just cannot figure it out. I redeployed it and had all going well there. then randomly same thing happens for no apparent reason. It will just start sporadically switching bits on and off.

This is the current state including the motion sensors for late night.

[
    {
        "id": "13a1a7ceb6a497a4",
        "type": "group",
        "z": "2a17042efaa3b23d",
        "style": {
            "stroke": "#999999",
            "stroke-opacity": "1",
            "fill": "none",
            "fill-opacity": "1",
            "label": true,
            "label-position": "nw",
            "color": "#a4a4a4"
        },
        "nodes": [
            "7ff3a66a.bd9c08",
            "52e0bc3b.617904",
            "acbfa624.3650d8",
            "bb484857.94bc",
            "a0834568.7e447",
            "406daced.472844",
            "a33d117e.961728",
            "d2d365921bab591a",
            "e29e54f8eb805979",
            "a8fba4fcb78b6fb1",
            "11c7e80d.388958",
            "bcf61976eca52521",
            "20f6aead.8b5d52"
        ],
        "x": 14,
        "y": 99,
        "w": 1072,
        "h": 262
    },
    {
        "id": "7ff3a66a.bd9c08",
        "type": "mqtt in",
        "z": "2a17042efaa3b23d",
        "g": "13a1a7ceb6a497a4",
        "name": "",
        "topic": "zigbee2mqtt/Stairs Switch/state_l1",
        "qos": "2",
        "datatype": "auto-detect",
        "broker": "12dd1610.da9e4a",
        "nl": false,
        "rap": false,
        "rh": "0",
        "inputs": 0,
        "x": 340,
        "y": 220,
        "wires": [
            [
                "bb484857.94bc"
            ]
        ]
    },
    {
        "id": "52e0bc3b.617904",
        "type": "mqtt in",
        "z": "2a17042efaa3b23d",
        "g": "13a1a7ceb6a497a4",
        "name": "",
        "topic": "zigbee2mqtt/Landing Switch/state_l3",
        "qos": "2",
        "datatype": "auto-detect",
        "broker": "12dd1610.da9e4a",
        "nl": false,
        "rap": false,
        "rh": "0",
        "inputs": 0,
        "x": 330,
        "y": 280,
        "wires": [
            [
                "bb484857.94bc"
            ]
        ]
    },
    {
        "id": "acbfa624.3650d8",
        "type": "mqtt out",
        "z": "2a17042efaa3b23d",
        "g": "13a1a7ceb6a497a4",
        "name": "",
        "topic": "zigbee2mqtt/Stairs Switch/l1/set",
        "qos": "",
        "retain": "false",
        "respTopic": "",
        "contentType": "",
        "userProps": "",
        "correl": "",
        "expiry": "",
        "broker": "12dd1610.da9e4a",
        "x": 910,
        "y": 320,
        "wires": []
    },
    {
        "id": "bb484857.94bc",
        "type": "function",
        "z": "2a17042efaa3b23d",
        "g": "13a1a7ceb6a497a4",
        "name": "2way stairs lights",
        "func": "var newMsg = { topic: \"zigbee2mqtt/Stairs Switch/state_l1\"}; \nvar hallstairs=flow.get('hallstairs') || 0;\nvar newMsg = { topic: \"zigbee2mqtt/Landing Switch/state_l3\" };\nvar landingstairs = flow.get('landingstairs') || 0;\nvar newMsg = { topic: \"zigbee2mqtt/Stairs/state\" };\nvar stairs = flow.get('stairs') || 0;\n\n//if (msg.topic===\"\"zigbee2mqtt/Landing Switch/state_l3\") {\nif (landingstairs === 0) {\n    if (msg.payload === \"ON\") {\n        newMsg.payload = \"ON\";\n        landingstairs = 1\n        flow.set('landingstairs', landingstairs);\n        return newMsg;\n    }\n}\nelse {\n    if (msg.payload === \"OFF\") {\n        newMsg.payload = \"OFF\";\n        landingstairs = 0;\n        flow.set('landingstairs', landingstairs);\n        return newMsg;\n    }\n}\n//if (msg.topic===\"zigbee2mqtt/Stairs Switch/state_l1\") {\nif (hallstairs === 0) {\n    if (msg.payload === \"ON\") {\n        newMsg.payload = \"ON\";\n        hallstairs = 1\n        flow.set('hallstairs', hallstairs);\n        return newMsg;\n    }\n}\nelse {\n    if (msg.payload === \"OFF\") {\n        newMsg.payload = \"OFF\";\n        hallstairs = 0;\n        flow.set('hallstairs', hallstairs);\n        return newMsg;\n    }\n}\n//if (msg.topic===\"zigbee2mqtt/Stairs/state\") {\nif (stairs === 0) {\n    if (msg.payload === \"ON\") {\n        newMsg.payload = \"ON\";\n        stairs = 1\n        flow.set('stairs', stairs);\n        return newMsg;\n    }\n}\nelse {\n    if (msg.payload === \"OFF\") {\n        newMsg.payload = \"OFF\";\n        stairs = 0;\n        flow.set('stairs', stairs);\n        return newMsg;\n    }\n}\n//}",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 630,
        "y": 240,
        "wires": [
            [
                "a0834568.7e447",
                "d2d365921bab591a",
                "acbfa624.3650d8",
                "a8fba4fcb78b6fb1"
            ]
        ],
        "inputLabels": [
            "House/LightLobbyA/stat/POWER"
        ]
    },
    {
        "id": "a0834568.7e447",
        "type": "mqtt out",
        "z": "2a17042efaa3b23d",
        "g": "13a1a7ceb6a497a4",
        "name": "",
        "topic": "zigbee2mqtt/Stairs/set",
        "qos": "",
        "retain": "false",
        "respTopic": "",
        "contentType": "",
        "userProps": "",
        "correl": "",
        "expiry": "",
        "broker": "12dd1610.da9e4a",
        "x": 860,
        "y": 200,
        "wires": []
    },
    {
        "id": "406daced.472844",
        "type": "inject",
        "z": "2a17042efaa3b23d",
        "g": "13a1a7ceb6a497a4",
        "name": "",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "ON",
        "payloadType": "str",
        "x": 110,
        "y": 240,
        "wires": [
            [
                "bb484857.94bc"
            ]
        ],
        "outputLabels": [
            "House/LightLobbyA/stat/POWER"
        ]
    },
    {
        "id": "a33d117e.961728",
        "type": "inject",
        "z": "2a17042efaa3b23d",
        "g": "13a1a7ceb6a497a4",
        "name": "",
        "props": [
            {
                "p": "payload",
                "v": "OFF",
                "vt": "str"
            },
            {
                "p": "topic",
                "v": "",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "OFF",
        "payloadType": "str",
        "x": 110,
        "y": 300,
        "wires": [
            [
                "bb484857.94bc"
            ]
        ],
        "outputLabels": [
            "House/LightLobbyA/stat/POWER"
        ]
    },
    {
        "id": "d2d365921bab591a",
        "type": "mqtt out",
        "z": "2a17042efaa3b23d",
        "g": "13a1a7ceb6a497a4",
        "name": "",
        "topic": "zigbee2mqtt/Landing Switch/l3/set",
        "qos": "",
        "retain": "false",
        "respTopic": "",
        "contentType": "",
        "userProps": "",
        "correl": "",
        "expiry": "",
        "broker": "1071c397f831a1ed",
        "x": 920,
        "y": 260,
        "wires": []
    },
    {
        "id": "e29e54f8eb805979",
        "type": "mqtt in",
        "z": "2a17042efaa3b23d",
        "g": "13a1a7ceb6a497a4",
        "name": "",
        "topic": "zigbee2mqtt/Stairs/state",
        "qos": "2",
        "datatype": "auto-detect",
        "broker": "1071c397f831a1ed",
        "nl": false,
        "rap": true,
        "rh": 0,
        "inputs": 0,
        "x": 350,
        "y": 320,
        "wires": [
            [
                "bb484857.94bc"
            ]
        ]
    },
    {
        "id": "a8fba4fcb78b6fb1",
        "type": "debug",
        "z": "2a17042efaa3b23d",
        "g": "13a1a7ceb6a497a4",
        "name": "debug 1",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "false",
        "statusVal": "",
        "statusType": "auto",
        "x": 820,
        "y": 160,
        "wires": []
    },
    {
        "id": "11c7e80d.388958",
        "type": "trigger",
        "z": "2a17042efaa3b23d",
        "g": "13a1a7ceb6a497a4",
        "name": "",
        "op1": "{\"service\": \"turn_on\"}",
        "op2": "{\"service\": \"turn_off\"}",
        "op1type": "json",
        "op2type": "json",
        "duration": "5",
        "extend": true,
        "overrideDelay": false,
        "units": "min",
        "reset": "",
        "bytopic": "all",
        "topic": "topic",
        "outputs": 1,
        "x": 470,
        "y": 140,
        "wires": [
            [
                "bb484857.94bc"
            ]
        ]
    },
    {
        "id": "bcf61976eca52521",
        "type": "time-range-switch",
        "z": "2a17042efaa3b23d",
        "g": "13a1a7ceb6a497a4",
        "name": "late night",
        "lat": "",
        "lon": "",
        "startTime": "23:00",
        "endTime": "08:30",
        "startOffset": "0",
        "endOffset": "0",
        "x": 300,
        "y": 160,
        "wires": [
            [
                "11c7e80d.388958"
            ],
            []
        ]
    },
    {
        "id": "20f6aead.8b5d52",
        "type": "server-state-changed",
        "z": "2a17042efaa3b23d",
        "g": "13a1a7ceb6a497a4",
        "name": "hall motion",
        "server": "b4b6ee5a.46dfd",
        "version": 4,
        "exposeToHomeAssistant": false,
        "haConfig": [
            {
                "property": "name",
                "value": ""
            },
            {
                "property": "icon",
                "value": ""
            }
        ],
        "entityidfilter": [
            "binary_sensor.hall_motion_occupancy",
            "binary_sensor.landing_motion_occupancy"
        ],
        "entityidfiltertype": "list",
        "outputinitially": false,
        "state_type": "str",
        "haltifstate": "",
        "halt_if_type": "str",
        "halt_if_compare": "is",
        "outputs": 1,
        "output_only_on_state_change": true,
        "for": "0",
        "forType": "num",
        "forUnits": "minutes",
        "ignorePrevStateNull": false,
        "ignorePrevStateUnknown": false,
        "ignorePrevStateUnavailable": false,
        "ignoreCurrentStateUnknown": false,
        "ignoreCurrentStateUnavailable": false,
        "outputProperties": [
            {
                "property": "payload",
                "propertyType": "msg",
                "value": "",
                "valueType": "entityState"
            },
            {
                "property": "data",
                "propertyType": "msg",
                "value": "",
                "valueType": "eventData"
            },
            {
                "property": "topic",
                "propertyType": "msg",
                "value": "",
                "valueType": "triggerId"
            }
        ],
        "x": 140,
        "y": 160,
        "wires": [
            [
                "bcf61976eca52521"
            ]
        ]
    },
    {
        "id": "12dd1610.da9e4a",
        "type": "mqtt-broker",
        "name": "",
        "broker": "localhost",
        "port": "1883",
        "clientid": "",
        "autoConnect": true,
        "usetls": false,
        "protocolVersion": "4",
        "keepalive": "60",
        "cleansession": true,
        "birthTopic": "",
        "birthQos": "0",
        "birthRetain": "false",
        "birthPayload": "",
        "birthMsg": {},
        "closeTopic": "",
        "closeQos": "0",
        "closeRetain": "false",
        "closePayload": "",
        "closeMsg": {},
        "willTopic": "",
        "willQos": "0",
        "willRetain": "false",
        "willPayload": "",
        "willMsg": {},
        "userProps": "",
        "sessionExpiry": ""
    },
    {
        "id": "1071c397f831a1ed",
        "type": "mqtt-broker",
        "name": "",
        "broker": "localhost",
        "port": "1883",
        "clientid": "",
        "autoConnect": true,
        "usetls": false,
        "protocolVersion": "4",
        "keepalive": "60",
        "cleansession": true,
        "birthTopic": "",
        "birthQos": "0",
        "birthRetain": "false",
        "birthPayload": "",
        "birthMsg": {},
        "closeTopic": "",
        "closeQos": "0",
        "closeRetain": "false",
        "closePayload": "",
        "closeMsg": {},
        "willTopic": "",
        "willQos": "0",
        "willRetain": "false",
        "willPayload": "",
        "willMsg": {},
        "userProps": "",
        "sessionExpiry": ""
    },
    {
        "id": "b4b6ee5a.46dfd",
        "type": "server",
        "name": "Home Assistant",
        "version": 4,
        "addon": true,
        "rejectUnauthorizedCerts": true,
        "ha_boolean": "y|yes|true|on|home|open",
        "connectionDelay": true,
        "cacheJson": true,
        "heartbeat": true,
        "heartbeatInterval": "30",
        "areaSelector": "friendlyName",
        "deviceSelector": "friendlyName",
        "entitySelector": "friendlyName",
        "statusSeparator": "at: ",
        "statusYear": "2-digit",
        "statusMonth": "short",
        "statusDay": "numeric",
        "statusHourCycle": "h23",
        "statusTimeFormat": "h:m"
    }
]

The reason will become apparent when you add the diagnostics and look at where it started to go wrong. Consider, for example what would happen if two of the switched were changed manually at almost the same time, so you have two messages coming in one after the other, so the first one will send off the new states to MQTT, but then the function will interpret the second message that is already waiting to be actioned. At that moment, the flow context variables will not represent the state when the second switch was activated.

1 Like

When you get a message coming in from MQTT saying that a switch has changed, have you any way of knowing whether the change is as a result of your code changing the switch via MQTT or whether it is an external agency changing the switch?

no, its just an mutt state input. so the same message is received regardless of the switch being physically initiated, or via HomeKit/homeassistant etc

That makes it very tricky to avoid such problems. I suspect you will be able to trip it into the loop condition by switching both switches at approximately the same time.
One possibility might be to feed the three MQTT values into a Join node in key/value pairs mode, sending after every value comes in.
I know logically what to do next but I am not sure the best way to code it. I am starting a new thread to ask for help with the best way to do it.

1 Like

But what you could do is add extra attributes to the message telling you who sent it.

So if it is the button (switch/what ever) it would be more like:

msg.payload{"switch":"x","who":"switch 1"}

Yes, you will have to add a bit to decode the newer style message, but it may help you.
Or not. :slight_smile:

The messages are coming from a Shelley switch.

Oh.

Sorry.

I guess that makes it difficult. :frowning:

OK, I have a solution, at least in outline. As I suggested earlier, feed the three MQTT values into a Join node in key/value pairs mode, sending after every value comes in. Then feed that into this flow (from the thread I linked to earlier), which will ensure that only one message gets through every 0.5 seconds, which will prevent the loop from ensuing.

image

[{"id":"8c004dc7912dc3ac","type":"trigger","z":"bdd7be38.d3b55","name":"","op1":"","op2":"","op1type":"pay","op2type":"payl","duration":"0.5","extend":false,"overrideDelay":false,"units":"s","reset":"","bytopic":"all","topic":"topic","outputs":1,"x":430,"y":3920,"wires":[["f2d6fb1495637cd9"]]},{"id":"f2d6fb1495637cd9","type":"rbe","z":"bdd7be38.d3b55","name":"Block repeated messages","func":"rbe","gap":"","start":"","inout":"out","septopics":false,"property":"payload","topi":"topic","x":665,"y":3920,"wires":[[]]}]

Then in your function you will receive a message containing all three inputs. Amend the logic there to remember the previous states of the inputs and work out what to do with the new states. You will have to cope with the initial condition which will be that the message may not yet contain values for all inputs. If the mqtt topics for the inputs are Retained (so you are guaranteed to get values immediately on startup) then change the Join node to wait for three messages (but keep the And every message thereafter), and you will know that your function will always have all three states.

1 Like

This seems to be working. awesome!

1 Like

I have this all working well now, apart from one item. The landing lamp.
The rest come through Matt, but the landing lamp is wifi and as a home assitant device entity.
when I add it to the function like this:
var newMsg = { topic: "Landing Lamp"};
var landinglamp = flow.get("light.landing.lamp") || 0;
it doesn’t seem to send messages to the function. What stupid thing am I doing wrong?

var newMsg = { topic: "zigbee2mqtt/Stairs Switch/state_l2"}; 
var halllamps=flow.get('halllamps') || 0;
var newMsg = { topic: "zigbee2mqtt/Landing Switch/state_l2" };
var landinglamps = flow.get('landinglamps') || 0;
var newMsg = { topic: "Landing Lamp"};
var landinglamp = flow.get("light.landing.lamp") || 0;


//if (msg.topic===""zigbee2mqtt/Landing Switch/state_l2") {
if (landinglamps === 0) {
    if (msg.payload === "ON") {
        newMsg.payload = "ON";
        landinglamps = 1
        flow.set('landinglamps', landinglamps);
        return newMsg;
    }
}```

This will just keep overwriting the value of var newMsg so it will only ever be the last value you set it to ?

The varnewmsg bit is meant to change to on or off to send on the the next devices. The Mqtt ones work fine but the entity.landing_lamp doesn’t. And I’m not sure if I am writing it wrong. Sorry on mobile atm.

As written newMsg will allways have a topic of Landing Lamp

How could I get it to have the state? Eg. On or off?

It's hard to understand exactly what you are trying to do here. Don't worry we all had to start somewhere :wink:

Perhaps you could share more of the flow and a description of what you want this function to do, even a sketch might help !

Looking at your function -

You don't seem to be using the incoming topics as they are commented out in the logic ?

You don't seem to be using topics on the outgoing messages either, as there are 3 MQTT OUT nodes which already have the topic set. This makes this code redundant, and you cannot define 3 values to the same variable like this anyway as it will always be the last value you set as I pointed out.

var newMsg = { topic: "zigbee2mqtt/Stairs Switch/state_l2"}; 
var newMsg = { topic: "zigbee2mqtt/Landing Switch/state_l2" };
var newMsg = { topic: "Landing Lamp"};

To send to the topic you define you need to leave topic blank in the MQTT node, and then only have 1 node.
You can set payload and topic within your if statments like this

var newMsg = { topic: "zigbee2mqtt/Stairs Switch/state_l2", payload: msg.payload } ;

As it stand if any of the 3 MQTT topics sends an On or Off payload, the function will stop at the first matching condition - eg if (landingstairs === 0) it will then send an ON payload to all three outgoing topics.
I'm guessing that all three will then send a new message back into the function, and after several iterations it may stop if there are no more matches ?

Regarding landinglamp there is no outgoing topic for that in the flow you shared.