Help with multiple inputs to a function node!


So sorry for the noob question, pretty new in node red, but please help!
I have 2 incoming items which reports stats, I would like to compare these 2 values and then outputs the msg.payload to a single string say ON or OFF or ignore them all.
I've already try this, not working at all:

[{"id":"dd00e371.7f4ac","type":"inject","z":"fda12dd7.39757","name":"","topic":"idbool","payload":"true","payloadType":"bool","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":220,"y":360,"wires":[["87ba35ea.b9a818"]]},{"id":"493f2d3f.5e3114","type":"inject","z":"fda12dd7.39757","name":"","topic":"idswitch","payload":"OFF","payloadType":"str","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":210,"y":400,"wires":[["87ba35ea.b9a818"]]},{"id":"49b26c1c.bf3264","type":"function","z":"fda12dd7.39757","name":"","func":"if (msg.idbool === true && msg.idswitch == \"OFF\") {\n    msg.payload = \"It's OK\";\n}\n\nif (msg.idbool === false && msg.idswitch == \"ON\") {\n    msg.payload = \"it's not OK\";\n}\n\nelse msg.payload = null;\nreturn msg;","outputs":1,"noerr":0,"x":530,"y":380,"wires":[["18cbf592.71f02a"]]},{"id":"18cbf592.71f02a","type":"debug","z":"fda12dd7.39757","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","x":530,"y":420,"wires":[]},{"id":"87ba35ea.b9a818","type":"join","z":"fda12dd7.39757","name":"","mode":"custom","build":"object","property":"payload","propertyType":"msg","key":"topic","joiner":"\\n","joinerType":"str","accumulate":true,"timeout":"","count":"2","reduceRight":false,"reduceExp":"","reduceInit":"","reduceInitType":"","reduceFixup":"","x":390,"y":380,"wires":[["49b26c1c.bf3264"]]}]

The data are not "persist", could you guys please take a look at how to do it? Many thanks!



see - Store a value in a function for a start



Thanks man, already read the doc, I know there must need to write "flow.set" kind of thing but I just so noob and don't understand how to write the codes T_T



There are lots of tutorials for javascript programming, which will give you a basic understanding, as well as several introction tutorials to Node-RED available on line a little googling should help you get started




You are close to a solution... you have used the join node to put the two values you want to compare into a single msg object, with this structure:

    "payload": {
        "idbool": true,
        "idswitch": "OFF"

However, in your javascript function code, you are trying to test the value of msg.idbool instead of msg.payload.idbool (and the same with msg.payload.idswitch). Making that change and cleaning up your "if" statements, this should be close to what you want:

if (msg.payload.idbool === true && msg.payload.idswitch == "OFF") {
    msg.payload = "It's OK";
else if (msg.payload.idbool === false && msg.payload.idswitch == "ON") {
    msg.payload = "it's not OK";
else {
    return null;
return msg;

Generally you would not pass along a msg with the payload set to null -- by returning the null value inside the "else" statement, that causes node-red to NOT output a msg, unless one of the first two conditions is met.



Thank you so much man for helping a noob like me. It's working, however I would like to ask another noob question:

  • So there is no need to "persist" or "store" the previous/current stats for both the "incoming" node? (because of the join node?)
  • What if one of the "inject node"(will replaced with mqtt node) get updated? Does the new incoming stat can pass the function node and working? (again still no need to persist the previous stat?)
    So sorry for the noob and un-professional words, I just try to understand why they work and when they need to persist, Thanks


Persisting some data in node/flow/global context is one way to "hold" information for use at a later point in time, or within another node in the same Editor "tab" (using flow context), or any other node (using global context). Other techniques for doing the same thing are writing/reading a file, using a database, or publishing to an MQTT retained message queue. The big difference with context variables is that they are lost when node-red restarts.

In your flow, you have at least 2 separate processing paths -- a switch that is ON or OFF, and a sensor that is sending true/false readings (I guess). So in order to compare the "last" value from both paths, you are using the join node to manually combine them. That join node has no idea whether the value is coming from an inject node, an MQTT event, a twitter msg, a database query, etc. etc. So there is no problem adding an mqtt in node, as long as it is receiving the same topic string as the inject node is using.

So the "storing" of the last values is being handled internally by the join node -- no need for you to also persist it somewhere else, unless there are other nodes that need those values as well.

In general, it is safer to persist data in shared context variables as little as necessary -- some would say you should never use global or flow context, since you can run into namespace collisions (two nodes using the same variable name) -- which can lead to some hard-to-find bugs.

1 Like


Thank you sir, loud and clear answer to me! It's now working like a charm!



I was looking for that exact solution, so thank you!
However, I'm stuck with the "Join" node

I get "Cannot merge non-object types"

When I look at the debug of each of the inputs to the join node they appear to me to be objects:


I'm using those settings:



I don't know why it would show that error, but I notice that you have told it to combine each msg.payload, but the first one hasn't got a payload property.
I also note that you have not told it when to send on a message, with the settings you have you would need to send a message with msg.complete set.
I have never used the join node in merged object mode, and looking at the Info tab it isn't clear to me what that does. I normally use key/value mode which expects messages with different topics and passes on a message with key/value pairs in the payload, each key being the topic of the passed in message. That may not be what you want to do though.



"join" doesn't work if only one of the nodes changes

How can I make the join node to reuse the object the second time if it didn't change?



sorry for highjacking that thread, but I think it fits...

In the following flow, the everything on button only works once, after the "preset" state changed. I need the join node to work overtime since the input "preset" wont change everytime.

[{"id":"f5bb4a2d.bf9378","type":"function","z":"bd967441.f24678","name":"","func":"if ( === true && msg.everyON === true) {\nmsg.payload = true;\n   return [msg];\n} else {\n   return [null];\n}","outputs":1,"noerr":0,"x":970,"y":180,"wires":[["9492465c.2145"]]},{"id":"3a48fc9.35c8584","type":"mqtt in","z":"bd967441.f24678","name":"","topic":"Test","qos":"2","broker":"aa21acb8.2e7348","x":330,"y":120,"wires":[["72762b6e.0e8364"]]},{"id":"36a9ab32.03bee4","type":"mqtt out","z":"bd967441.f24678","name":"","topic":"Test","qos":"2","retain":"","broker":"aa21acb8.2e7348","x":790,"y":120,"wires":[]},{"id":"ce858d97.bcc4c8","type":"join","z":"bd967441.f24678","name":"","mode":"custom","build":"array","property":"","propertyType":"full","key":"topic","joiner":"\\n","joinerType":"str","accumulate":false,"timeout":"","count":"","reduceRight":false,"reduceExp":"","reduceInit":"","reduceInitType":"","reduceFixup":"","x":810,"y":180,"wires":[["f5bb4a2d.bf9378"]]},{"id":"d512897d.c5a0c","type":"ui_template","z":"bd967441.f24678","group":"7581546f.42c224","name":"Preset","order":2,"width":"5","height":"2","format":"<md-button\n    class=\"vibrate filled bigfont\"\n    ng-style=\"{background: !msg.payload ? 'grey' : 'green' }\"\n    ng-click=\"msg.topic = 'preset'; = !msg.payload;msg.payload = !msg.payload;send(msg)\"\n    > \n    Preset<br/>\n    </md-button>","storeOutMessages":false,"fwdInMessages":false,"templateScope":"local","x":590,"y":120,"wires":[["36a9ab32.03bee4","ce858d97.bcc4c8"]]},{"id":"72762b6e.0e8364","type":"json","z":"bd967441.f24678","name":"","pretty":false,"x":470,"y":120,"wires":[["d512897d.c5a0c"]]},{"id":"9dbf4373.38f538","type":"ui_toast","z":"bd967441.f24678","position":"dialog","displayTime":"3","highlight":"","outputs":1,"ok":"Yes","cancel":"No","topic":"","name":"","x":310,"y":180,"wires":[["99f13b9c.0700c"]],"icon":"node-red/light.png"},{"id":"a08f1ec1.23b7c","type":"ui_template","z":"bd967441.f24678","group":"7581546f.42c224","name":"Everything ON","order":1,"width":"6","height":"3","format":"<md-button\n    class=\"vibrate filled bigfont\"\n    ng-style=\"{background: 'grey'}\"\n    ng-click=\"msg.topic = ''; msg.payload = 'Everything ON?';send(msg)\"\n>\n    Everything ON\n</md-button>","storeOutMessages":false,"fwdInMessages":false,"templateScope":"local","x":140,"y":180,"wires":[["9dbf4373.38f538"]]},{"id":"99f13b9c.0700c","type":"function","z":"bd967441.f24678","name":"convert output based on actState","func":"if (msg.payload == \"Yes\") {\n   msg.everyON = true,\n   msg.topic = \"EverythingOn\",\n   msg.complete = true\n   return [msg]\n} else {\n   return [null];\n}","outputs":1,"noerr":0,"x":540,"y":180,"wires":[["ce858d97.bcc4c8"]]},{"id":"9492465c.2145","type":"debug","z":"bd967441.f24678","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":1120,"y":180,"wires":[]},{"id":"aa21acb8.2e7348","type":"mqtt-broker","z":"","broker":"localhost","port":"1883","clientid":"","usetls":false,"compatmode":true,"keepalive":"60","cleansession":true,"birthTopic":"","birthQos":"0","birthRetain":"false","birthPayload":"","willTopic":"","willQos":"0","willRetain":"false","willPayload":""},{"id":"7581546f.42c224","type":"ui_group","z":"","name":"Utilities","tab":"a577685a.d0bd48","order":3,"disp":true,"width":"6","collapse":false},{"id":"a577685a.d0bd48","type":"ui_tab","z":"","name":"Arbeitszimmer","icon":"dashboard","order":3}]

Thank you....



Does this mean you have fixed your earlier problem with the "Cannot merge non-object types" error? If so please say so. You haven't said what mode you are now using. If you are using key/value pair mode then if you set "After a number of message parts" to the number of inputs to receive before sending anything, and tick "and every subsequent message" then after waiting for the number of messages specified it will send a new combined message every time a new message is received.
Our posts crossed, I see you have now provided a flow. I will have a look.



Move the information you want into the payload (if it is not already there) and provide different topics to the messages on each wire. Then use key/value pair mode as I described in my last post.



thats what I did

but when I hit, in my case, "everything on" the second time, the joined object doesn't contain the "preset" object anymore. Only the "everythingon" topic.



Make sure you have not got msg.complete set, that sends a message and clears the memory.



(aaahhhh) but I need msg.complete since I only want to join both and get a result when one (everythingon) is updated, but with the state saved from the other input

It seemed to be a simple task: Do something based on another state (in this case a mqtt variable)...not so simple unless I'm missing something:

I want to turn on a bunch of things based on a preset. By pushing one button in the dashboard, node red should look want the preset says and then either set the output to "true" or not if the preset is false....
And that preset doesn't change every time



Don't pass msg.complete, but then put a Switch node after the join set to only pass messages with the everythingon topic. Since the message passed on has the topic of the message last received the Join node will send a message when either input is received but the switch will ignore the ones you don't want.



that works, thank you!

OT: Am I using node red for the wrong things? I'm having a hard time wrapping my had around something so simple like the task above which would be a simple IF statement in any other language



You would still need the logic to remember the two inputs from one call to the next, and of course you can always use a function node and write it in javascript.
Some would write the values to flow context but I don't like using flow or global context. It is too fraught with possibilities for confusion so I never use them.
I think I did put in a feature request some time ago suggesting an enhancement to the Join node that gave the choice of whether to clear the previous messages when msg.complete is set but it seems it hasn't got to the top of the priority pile.