Two Doors and one Light

I have a garage door and a house door. I want the lights to turn on if one of doors opens, and turn off again if it closes, so that both would again be closed.

I also want it so that if I open door A and keep it open, and I open door B and then close it again with door A still open, the lights stay on. I only want the lights to turn off when both doors are closed. Right now this flow works to turn the light on if one of the two doors is opened, but doesn't shut off if both doors are closed.

[
    {
        "id": "085e4594d99b4a0d",
        "type": "tab",
        "label": "Testing Flows",
        "disabled": false,
        "info": "",
        "env": []
    },
    {
        "id": "9d25a80297403094",
        "type": "inject",
        "z": "085e4594d99b4a0d",
        "name": "",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "garage_door",
        "payload": "off",
        "payloadType": "str",
        "x": 140,
        "y": 220,
        "wires": [
            [
                "269c5aa1aad8e96f",
                "c737fead868bfd86",
                "a5d0914eb21e55e8"
            ]
        ]
    },
    {
        "id": "5f5a7ccba5173b08",
        "type": "api-call-service",
        "z": "085e4594d99b4a0d",
        "name": "",
        "server": "4e3730d1.5341d",
        "version": 5,
        "debugenabled": false,
        "domain": "light",
        "service": "turn_on",
        "areaId": [],
        "deviceId": [],
        "entityId": [
            "light.pc"
        ],
        "data": "",
        "dataType": "jsonata",
        "mergeContext": "",
        "mustacheAltTags": false,
        "outputProperties": [],
        "queue": "none",
        "x": 710,
        "y": 220,
        "wires": [
            []
        ]
    },
    {
        "id": "54ca9a09ebd2f068",
        "type": "inject",
        "z": "085e4594d99b4a0d",
        "name": "",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "house_garage",
        "payload": "off",
        "payloadType": "str",
        "x": 150,
        "y": 280,
        "wires": [
            [
                "269c5aa1aad8e96f",
                "c737fead868bfd86",
                "a5d0914eb21e55e8"
            ]
        ]
    },
    {
        "id": "d3f00d79fc53a671",
        "type": "api-call-service",
        "z": "085e4594d99b4a0d",
        "name": "",
        "server": "4e3730d1.5341d",
        "version": 5,
        "debugenabled": false,
        "domain": "light",
        "service": "turn_off",
        "areaId": [],
        "deviceId": [],
        "entityId": [
            "light.pc"
        ],
        "data": "",
        "dataType": "jsonata",
        "mergeContext": "",
        "mustacheAltTags": false,
        "outputProperties": [],
        "queue": "none",
        "x": 730,
        "y": 300,
        "wires": [
            []
        ]
    },
    {
        "id": "898f8471174a8259",
        "type": "debug",
        "z": "085e4594d99b4a0d",
        "name": "debug 8",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "false",
        "statusVal": "",
        "statusType": "auto",
        "x": 500,
        "y": 500,
        "wires": []
    },
    {
        "id": "269c5aa1aad8e96f",
        "type": "debug",
        "z": "085e4594d99b4a0d",
        "name": "debug 9",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "false",
        "statusVal": "",
        "statusType": "auto",
        "x": 360,
        "y": 100,
        "wires": []
    },
    {
        "id": "c737fead868bfd86",
        "type": "switch",
        "z": "085e4594d99b4a0d",
        "name": "",
        "property": "payload",
        "propertyType": "msg",
        "rules": [
            {
                "t": "eq",
                "v": "on",
                "vt": "str"
            }
        ],
        "checkall": "true",
        "repair": false,
        "outputs": 1,
        "x": 430,
        "y": 220,
        "wires": [
            [
                "5f5a7ccba5173b08"
            ]
        ]
    },
    {
        "id": "a5d0914eb21e55e8",
        "type": "and-gate",
        "z": "085e4594d99b4a0d",
        "name": "",
        "rules": [
            {
                "t": "eq",
                "v": "offf",
                "vt": "str",
                "propertyType": "msg",
                "property": "payload",
                "topic": "garage_door"
            },
            {
                "t": "eq",
                "v": "off",
                "vt": "str",
                "propertyType": "msg",
                "property": "payload",
                "topic": "house_garage"
            }
        ],
        "outputTopic": "Both Lights Off",
        "gateType": "and",
        "emitOnlyIfTrue": true,
        "x": 380,
        "y": 380,
        "wires": [
            [
                "d3f00d79fc53a671",
                "898f8471174a8259"
            ]
        ]
    },
    {
        "id": "4e3730d1.5341d",
        "type": "server",
        "name": "Home Assistant",
        "addon": true,
        "rejectUnauthorizedCerts": true,
        "ha_boolean": "",
        "connectionDelay": false,
        "cacheJson": false,
        "heartbeat": false,
        "heartbeatInterval": "",
        "statusSeparator": "",
        "enableGlobalContextStore": false
    }
]

I don't know what your AND gate is, nor do I have Home Assistant, but it seems to me you want boolean OR not AND

If door A is open OR door B is open: Lights ON
else: Lights OFF

I have not looked at it that way, but that might work. Let me see if I can get that working in a test environment

That didn't work, or maybe I don't have the Or logic configured correctly....

Hi and welcome to the forum.

Sorry I haven't loaded your flow - I have only just turned on the machine and cam catching up on things.

Ok, I don't know how your doors are signaling their status.

Have you heard of context?

But here's my take:

Each door sends a message that has a specific thing (say topic) that identifies which door it is.
Then the payload saying if it is open or closed.

Each door sends it's message and all you do is write some code in a function node to determine which door is in which state.
Then if A is open the it doesn't matter what B is. A turn light on message is sent.
If B sends an open the turn light on message is sent.
If B and A are closed then turn light off is sent.

You need context to remember the other door's status to allow both doors closed detection to happen.

Hope that helps.

If I get time later on, I will try to bash out some code to show you what I mean.

This is a couple of examples.

I've done it a few different ways showing how you can optimise the code.

[{"id":"154b389d7f73a166","type":"function","z":"61bbf627ae47ef79","name":"Option 1","func":"//   Set context from incoming message.\ncontext.set(msg.topic,msg.payload);\n\nlet A = context.get(\"A\");\nlet B = context.get(\"B\");\n\n//  If door A is open, turn on the light.\nif (A == \"open\")\n{\n    msg.payload = \"Turn light on\";\n    return msg;\n}\n\n//  If door B is open, turn on the light.\nif (B == \"open\") {\n    msg.payload = \"Turn light on\";\n    return msg;\n}\n\n//  If BOTH doors are closed, turn the light off.\nif (A == \"close\" && B == \"close\")\n{\n    msg.payload = \"Turn the light off\";\n    return msg;\n}\n","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":430,"y":350,"wires":[["a37eb7f20823f422"]]},{"id":"cc9107c8a94043f7","type":"inject","z":"61bbf627ae47ef79","name":"Door A CLOSED","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"A","payload":"close","payloadType":"str","x":210,"y":290,"wires":[["154b389d7f73a166"]]},{"id":"7ef9a23670bf58a0","type":"inject","z":"61bbf627ae47ef79","name":"Door A OPEN","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"A","payload":"open","payloadType":"str","x":200,"y":330,"wires":[["154b389d7f73a166"]]},{"id":"4bc310c180bc7143","type":"inject","z":"61bbf627ae47ef79","name":"Door B CLOSED","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"B","payload":"close","payloadType":"str","x":210,"y":390,"wires":[["154b389d7f73a166"]]},{"id":"309ff4b87feb6e0f","type":"inject","z":"61bbf627ae47ef79","name":"Door B OPEN","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"B","payload":"open","payloadType":"str","x":200,"y":430,"wires":[["154b389d7f73a166"]]},{"id":"a37eb7f20823f422","type":"debug","z":"61bbf627ae47ef79","name":"debug 54","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":610,"y":350,"wires":[]},{"id":"f8c59645b396861a","type":"function","z":"61bbf627ae47ef79","name":"Option 2","func":"//   Set context from incoming message.\ncontext.set(msg.topic,msg.payload);\n\nlet A = context.get(\"A\") || 0;\nlet B = context.get(\"B\") || 0;\n\n//  If either door is open, turn on the light.\nif (A == \"open\" || B == \"open\")\n{\n    msg.payload = \"Turn light on\";\n    return msg;\n}\n\n//  If BOTH doors are closed, turn the light off.\nif (A == \"close\" && B == \"close\")\n{\n    msg.payload = \"Turn the light off\";\n    return msg;\n}\n","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":430,"y":390,"wires":[[]]},{"id":"73b35f57043661a3","type":"inject","z":"61bbf627ae47ef79","name":"Door A CLOSED","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"A","payload":"0","payloadType":"num","x":210,"y":520,"wires":[["d6278c6e7911ca98"]]},{"id":"1334457b7e56ea8a","type":"inject","z":"61bbf627ae47ef79","name":"Door A OPEN","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"A","payload":"1","payloadType":"num","x":200,"y":560,"wires":[["d6278c6e7911ca98"]]},{"id":"b9a2a3469b812d1d","type":"inject","z":"61bbf627ae47ef79","name":"Door B CLOSED","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"B","payload":"0","payloadType":"num","x":210,"y":620,"wires":[["d6278c6e7911ca98"]]},{"id":"b6b6ad1a631a4344","type":"inject","z":"61bbf627ae47ef79","name":"Door B OPEN","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"B","payload":"1","payloadType":"num","x":200,"y":660,"wires":[["d6278c6e7911ca98"]]},{"id":"d6278c6e7911ca98","type":"function","z":"61bbf627ae47ef79","name":"Option 3","func":"//   Set context from incoming message.\ncontext.set(msg.topic,msg.payload);\n\nlet A = context.get(\"A\") || 0;\nlet B = context.get(\"B\") || 0;\n\n//  If either door is open, turn on the light.\nif (A == 1 || B == 1)\n{\n    msg.payload = \"Turn light on\";\n    return msg;\n}\n\n//  If BOTH doors are closed, turn the light off.\nif (A == 0 && B == 0)\n{\n    msg.payload = \"Turn the light off\";\n    return msg;\n}\n","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":430,"y":580,"wires":[["cd18b5d5227568dd"]]},{"id":"cd18b5d5227568dd","type":"debug","z":"61bbf627ae47ef79","name":"debug 55","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":610,"y":580,"wires":[]}]

NOTE!

The first two are problematic in that they don't have default states set exactly.
The last one does a better job.
It doesn't need you to cycle the doors to set their state.
Oh, it also assumes that 1 is ON and 0 if OFF.... Assumes. Bad choce.
I wrote it that way.
You just change the messages being sent to match that way/structure.

Hope that helps.

Thanks for this! I was aware that context existed but do not really know how to write JS code. I'll look at these three and see which one fits the bill the best.

It looks simple when I see it written, but I don't have the programming knowledge to know why it is the way it is.

No problems.

I know how it is to be at the bottom of the learning curve.

All 3 basically do the same thing. I just made each version sligthly better than the other one showing you some tricks in JS.

So this statement:
You need context to remember the other door's status to allow both doors closed detection to happen.

You are saying that when a node changes state, it is not remembered and context helps remember it, puts it in memory so to speak?

Why is there not a node that allows for a series, Door A open yes / no, Door B open yes / no and then set the lights that way?

Node-Red is a per message action thing.
A message arrives and something is done.

But if you have 2 doors, you need to use context so the other door's status is also available.

Each time a message arrive in this (or any) function node, any previous messages are forgotten.
So - say - door A is closed. Door B's state is unknown.
But using context, that is/was stored and retrieved when a new message comes in.

So to sort of answer that last part:

Context.
:wink:
And that's what I did with the function node.

Got it, so kind of like keeping the state in ram for later reacall.

I'll play around with your function and modify as needed, probably be back with more questions. :slight_smile:

I would say more of a stack than actual memory. But that may be a terminology thing.

What system are you using to send the door open/closed messages? MQTT?

I did my best to copy what you have, but when I hit either open or close it toggles the light. I assume it would only toggle the light if both were set to close?

[
    {
        "id": "085e4594d99b4a0d",
        "type": "tab",
        "label": "Testing Flows",
        "disabled": false,
        "info": "",
        "env": []
    },
    {
        "id": "9d25a80297403094",
        "type": "inject",
        "z": "085e4594d99b4a0d",
        "name": "Garage Door Open",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "A",
        "payload": "1",
        "payloadType": "str",
        "x": 150,
        "y": 220,
        "wires": [
            [
                "269c5aa1aad8e96f",
                "2ce3f5e7ef130a4a"
            ]
        ]
    },
    {
        "id": "5f5a7ccba5173b08",
        "type": "api-call-service",
        "z": "085e4594d99b4a0d",
        "name": "",
        "server": "4e3730d1.5341d",
        "version": 5,
        "debugenabled": false,
        "domain": "light",
        "service": "toggle",
        "areaId": [],
        "deviceId": [],
        "entityId": [
            "light.pc"
        ],
        "data": "",
        "dataType": "jsonata",
        "mergeContext": "",
        "mustacheAltTags": false,
        "outputProperties": [],
        "queue": "none",
        "x": 630,
        "y": 320,
        "wires": [
            []
        ]
    },
    {
        "id": "54ca9a09ebd2f068",
        "type": "inject",
        "z": "085e4594d99b4a0d",
        "name": "House Garage Door Open",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "B",
        "payload": "1",
        "payloadType": "str",
        "x": 170,
        "y": 340,
        "wires": [
            [
                "269c5aa1aad8e96f",
                "2ce3f5e7ef130a4a"
            ]
        ]
    },
    {
        "id": "269c5aa1aad8e96f",
        "type": "debug",
        "z": "085e4594d99b4a0d",
        "name": "debug 9",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "false",
        "statusVal": "",
        "statusType": "auto",
        "x": 360,
        "y": 100,
        "wires": []
    },
    {
        "id": "01b21925ba7ede01",
        "type": "inject",
        "z": "085e4594d99b4a0d",
        "name": "Garage Door Close",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "A",
        "payload": "0",
        "payloadType": "str",
        "x": 150,
        "y": 280,
        "wires": [
            [
                "269c5aa1aad8e96f",
                "2ce3f5e7ef130a4a"
            ]
        ]
    },
    {
        "id": "505f41630ade6d47",
        "type": "inject",
        "z": "085e4594d99b4a0d",
        "name": "House Garage Door Close",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "B",
        "payload": "0",
        "payloadType": "str",
        "x": 170,
        "y": 400,
        "wires": [
            [
                "269c5aa1aad8e96f",
                "2ce3f5e7ef130a4a"
            ]
        ]
    },
    {
        "id": "2ce3f5e7ef130a4a",
        "type": "function",
        "z": "085e4594d99b4a0d",
        "name": "Option 3",
        "func": "//   Set context from incoming message.\ncontext.set(msg.topic,msg.payload);\n\nlet A = context.get(\"A\") || 0;\nlet B = context.get(\"B\") || 0;\n\n//  If either door is open, turn on the light.\nif (A == 1 || B == 1)\n{\n    msg.payload = \"Turn light on\";\n    return msg;\n}\n\n//  If BOTH doors are closed, turn the light off.\nif (A == 0 && B == 0)\n{\n    msg.payload = \"Turn the light off\";\n    return msg;\n}\n",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 440,
        "y": 320,
        "wires": [
            [
                "5f5a7ccba5173b08"
            ]
        ]
    },
    {
        "id": "4e3730d1.5341d",
        "type": "server",
        "name": "Home Assistant",
        "addon": true,
        "rejectUnauthorizedCerts": true,
        "ha_boolean": "",
        "connectionDelay": false,
        "cacheJson": false,
        "heartbeat": false,
        "heartbeatInterval": "",
        "statusSeparator": "",
        "enableGlobalContextStore": false
    }
]

ARGH! I don't use/have it. (As I just realised when I imported the flow.)

Ok, so it is HA.

Cringe. I can't help you with that.

You put the debug at the wrong place in the flow.
You need to put it going out of option 3 and you will see the different messages going out.
You need to teach (tell?) HA what those messages are and how to handle them.

Got it.

Ya, I figured Node Red and HA aren't mutually exclusive, although I've never heard of NodeRed in any other context besides HA.

Is there any other way to do this? Could I have a node that constantly checks the state of the entities and thus have it know if one door or the other is closed?

You are SO CLOSE it isn't worth changing now.

All you have to do is change the two message in the function node to what is needed to get HA to do what you want.

As is - before my stuff - what were you sending into HA from the doors?
Door A and Door B. What are their messages?

See, be it HA or NR, it is all message controlled.
You just send the correct/right message to get things done.

My example is wrong for your needs because it sends what I told it, not knowing what your system uses.

So there are two options:
Change your system to use my message, or change my bit of code to use/send the messages your system uses.

So, to know what these messages are, I guess (can't say as I don't have) put a debug node going into HA from your doors.
You may have to show entire message also.
Or are the doors part of the HA scheme?
Can they talk to NR?
You are out of my skill set.

This should be what you are looking for?

1/14/2023, 9:41:07 PM[node: debug 59]

garage_door : msg.payload : string[4]

"offf"

1/14/2023, 9:41:07 PM[node: debug 59] garage_door : msg.payload : string[2]

"on"

1/14/2023, 9:41:08 PM[node: debug 59] house_garage_door : msg.payload : string[3]

"off"

1/14/2023, 9:41:09 PM[node: debug 59] house_garage_door : msg.payload : string[2]

"on"

{"_msgid":"c0aef3a43cc77333","payload":"offf","topic":"garage_door"}

{"_msgid":"262caef4a3c631c6","payload":"on","topic":"garage_door"}

{"_msgid":"a7107b8ea837acbe","payload":"off","topic":"house_garage_door"}

{"_msgid":"2bde859e08483a4d","payload":"on","topic":"house_garage_door"}

Is this what you are looking for?

I tried editing your code, but I don't think it came out right

//   Set context from incoming message.
context.set(msg.topic,msg.payload);

let garage_door = context.get("off") || 0;
let house_garage_door = context.get("off") || 0;

//  If either door is open, turn on the light.
if (house_garage_door == 1 || garage_door == 1)
{
    msg.payload = "Turn light on";
    return msg;
}

//  If BOTH doors are closed, turn the light off.
if (house_garage_door == 0 && garage_door == 0)
{
    msg.payload = "Turn the light off";
    return msg;
}
1 Like

Ok, so HOW do the doors talk to NR?

I'm not going to import the flow, as it will have HA stuff which I then have to delete.

Do what is needed to See the message.
Put a debug node - display full message and show me the 4 messages.
Door1, open/close
Door2, open/close.

We can work from there.

Good, but not perfect.

Ok, now I am going to have to show you how to make those things better.

I also hope the debug is set to show complete msg object.

Screenshot from 2023-01-15 16-49-34

As an example.

Expand the message then click on the clipboard icon to copy the message to the clipboard.

Up the top are buttons.

Click the </> one.

type or paste code here

Paste the clipboard where it tells you.

Then it can be better parsed.

Double click on the debug node and look for the option:

I sent a picture in the PM I sent you how to do it.

But that helps.

Hang on while I edit the code.

One of the messages is wrong as it is offf and not off.
That will cause problems for detecting both doors being closed.

I too am now also stuck as I am not allowed to post more than 3 replies.