A bug, or a misunderstanding on my end?

I'm new to Node-RED, but not new to programming, so I have a basic understanding of what should work. Since I'm new though, I thought I'd see what you people say before I report a bug. I have the this setup in a change node.

I added a lot of images to clarify my case, but sadly this forum has some weird, unnecessary limits for new users.

Inputs are expected to be in the form of [true/false]-[true/false].
In my opinion, this should output a boolean true for input "true-true", and a boolean false for all other three options (true-false, false-true, false-false). Basically it's an AND gate.

The "true-true" input works fine, a boolean true comes out. All other three options though do not result in a boolean false, but a string false:
image

The same thing happens when I tell it to output a number 1, it outputs a string containing the number 1:
image

[{"id":"5f4fb69f255a7afd","type":"change","z":"f6f2187d.f17ca8","name":"Make boolean","rules":[{"t":"change","p":"payload","pt":"msg","from":"true-true","fromt":"str","to":"true","tot":"bool"},{"t":"change","p":"payload","pt":"msg","from":".*false.*","fromt":"re","to":"false","tot":"bool"}],"action":"","property":"","from":"","to":"","reg":false,"x":2620,"y":900,"wires":[["f46e3f30e8f2459c"]]},{"id":"d25af463a7c58e7a","type":"inject","z":"f6f2187d.f17ca8","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"true-true","payloadType":"str","x":2420,"y":880,"wires":[["5f4fb69f255a7afd","a910c31af9a637b7"]]},{"id":"a0200cdbd74d6e7f","type":"inject","z":"f6f2187d.f17ca8","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"false-true","payloadType":"str","x":2420,"y":920,"wires":[["5f4fb69f255a7afd","a910c31af9a637b7"]]},{"id":"f46e3f30e8f2459c","type":"debug","z":"f6f2187d.f17ca8","name":"Output","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":2770,"y":900,"wires":[]},{"id":"a910c31af9a637b7","type":"debug","z":"f6f2187d.f17ca8","name":"Input","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":2590,"y":860,"wires":[]}]

I assume this is a bug of some sorts? It's some extremely confusing behavior to say the least.


Workaround
For those experiencing the same thing, I do have a workaround that works (as far as I've tested at least): Simply add another change item, to change the string 'false' to a boolean false.

I did change the change regex to boolean to change regex to string, so that when I come back to this after a long time, I don't look at this and go "hey that third item isn't required, it can be deleted", which would prevent my flow from working properly.

[{"id":"5f4fb69f255a7afd","type":"change","z":"f6f2187d.f17ca8","name":"Make boolean","rules":[{"t":"change","p":"payload","pt":"msg","from":"true-true","fromt":"str","to":"true","tot":"bool"},{"t":"change","p":"payload","pt":"msg","from":".*false.*","fromt":"re","to":"false","tot":"str"},{"t":"change","p":"payload","pt":"msg","from":"false","fromt":"str","to":"false","tot":"bool"}],"action":"","property":"","from":"","to":"","reg":false,"x":2620,"y":900,"wires":[["f46e3f30e8f2459c"]]},{"id":"d25af463a7c58e7a","type":"inject","z":"f6f2187d.f17ca8","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"true-true","payloadType":"str","x":2420,"y":880,"wires":[["5f4fb69f255a7afd","a910c31af9a637b7"]]},{"id":"a0200cdbd74d6e7f","type":"inject","z":"f6f2187d.f17ca8","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"false-true","payloadType":"str","x":2420,"y":920,"wires":[["5f4fb69f255a7afd","a910c31af9a637b7"]]},{"id":"f46e3f30e8f2459c","type":"debug","z":"f6f2187d.f17ca8","name":"Output","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":2770,"y":900,"wires":[]},{"id":"a910c31af9a637b7","type":"debug","z":"f6f2187d.f17ca8","name":"Input","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":2590,"y":860,"wires":[]}]

The change node behaviour seems weird indeed.

You could use a switch node to make it more explicit and easier to capture the various states/booleans.

[{"id":"4ed9a7c174f52592","type":"switch","z":"a772854bb5e8cd75","name":"","property":"payload","propertyType":"msg","rules":[{"t":"eq","v":"true-true","vt":"str"},{"t":"eq","v":"false-true","vt":"str"},{"t":"eq","v":"false-false","vt":"str"}],"checkall":"true","repair":false,"outputs":3,"x":400,"y":500,"wires":[["bf3318c45221474f"],["08c216eb16c3d9ca"],["08c216eb16c3d9ca"]]},{"id":"d6e2d51474e49040","type":"inject","z":"a772854bb5e8cd75","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"false-true","payloadType":"str","x":240,"y":500,"wires":[["4ed9a7c174f52592"]]},{"id":"06c7c12cad794696","type":"inject","z":"a772854bb5e8cd75","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"true-true","payloadType":"str","x":240,"y":460,"wires":[["4ed9a7c174f52592"]]},{"id":"08c216eb16c3d9ca","type":"change","z":"a772854bb5e8cd75","name":"false","rules":[{"t":"set","p":"payload","pt":"msg","to":"false","tot":"bool"}],"action":"","property":"","from":"","to":"","reg":false,"x":550,"y":520,"wires":[["477caf84bdc323f6"]]},{"id":"bf3318c45221474f","type":"change","z":"a772854bb5e8cd75","name":"true","rules":[{"t":"set","p":"payload","pt":"msg","to":"true","tot":"bool"}],"action":"","property":"","from":"","to":"","reg":false,"x":550,"y":480,"wires":[["b42a18f1d62c86c2"]]},{"id":"b42a18f1d62c86c2","type":"debug","z":"a772854bb5e8cd75","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":690,"y":480,"wires":[]},{"id":"477caf84bdc323f6","type":"debug","z":"a772854bb5e8cd75","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":690,"y":520,"wires":[]},{"id":"050b21f26a4ee5dd","type":"inject","z":"a772854bb5e8cd75","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"false-false","payloadType":"str","x":240,"y":540,"wires":[["4ed9a7c174f52592"]]}]

The change node in search mode is trying to replace parts of a string so trying to replace part of a string with a boolean doesn't make sense... you can't have a part string part boolean type of object. In regex mode It doesn't know that that pattern matches the whole string and so the type can be changed as well. But yes it is confusing.

That's a pretty nice solution, however I do prefer my own setup, since it is more compact.

Hmm, so I just did a test: Replace "xx" with "test", both strings. Input: "xxyyzz", output: "testyyzz". I did not realize that this is what the change node does. This confusion is further amplified by the fact that if the input is exactly the search value, it does automatically convert to any other type, except when you use regex.

So I guess it is not a bug. This does leave me with a follow up question:
Is there a way to use regex for the search, and still have it auto-convert? I tried ^.*xx.*$, so it would grab the entire input (testxxtest), but that doesn't work sadly.

Indeed - I am more surprised that the autoconvert happened in the first instance (full string match).

Possibly in the cases that its a complete replacement of the value, the type (boolean) is carried over to msg.payload where in the case its a partial string replacement it stays as string.

an alternative to Backman's Switch node could be to check for all exact match string cases
in the Change node ?

Example :

[{"id":"5f4fb69f255a7afd","type":"change","z":"54efb553244c241f","name":"Make boolean","rules":[{"t":"change","p":"payload","pt":"msg","from":"true-true","fromt":"str","to":"true","tot":"bool"},{"t":"change","p":"payload","pt":"msg","from":"false-true","fromt":"str","to":"false","tot":"bool"},{"t":"change","p":"payload","pt":"msg","from":"true-false","fromt":"str","to":"false","tot":"bool"},{"t":"change","p":"payload","pt":"msg","from":"false-false","fromt":"str","to":"false","tot":"bool"}],"action":"","property":"","from":"","to":"","reg":false,"x":720,"y":1260,"wires":[["f46e3f30e8f2459c"]]},{"id":"d25af463a7c58e7a","type":"inject","z":"54efb553244c241f","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"true-true","payloadType":"str","x":420,"y":1200,"wires":[["5f4fb69f255a7afd"]]},{"id":"a0200cdbd74d6e7f","type":"inject","z":"54efb553244c241f","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"false-true","payloadType":"str","x":420,"y":1240,"wires":[["5f4fb69f255a7afd"]]},{"id":"f46e3f30e8f2459c","type":"debug","z":"54efb553244c241f","name":"Output","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":930,"y":1260,"wires":[]},{"id":"a910c31af9a637b7","type":"debug","z":"54efb553244c241f","name":"Input","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":630,"y":1160,"wires":[]},{"id":"3d98639a7e26d784","type":"inject","z":"54efb553244c241f","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"true-false","payloadType":"str","x":420,"y":1280,"wires":[["5f4fb69f255a7afd"]]},{"id":"3d87b66a5f5f384d","type":"inject","z":"54efb553244c241f","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"false-false","payloadType":"str","x":420,"y":1320,"wires":[["5f4fb69f255a7afd"]]}]

Yes, that's what's happening, but the question then is why it doesn't do that when the regex matches the complete value. ^.*xx.*$ means: match from the very beginning, with 0 to unlimited random characters, then "xx", and then again 0 to unlimited random characters, and match until the very end. This always matches the entire string, as long as it contains "xx".

Why? In the UI there is an option to select the output variable type. To me that says without a shred of a doubt that you're actually replacing the variable, and not just replacing a part of a string. To me, it really seems to be a "completely replace the variable"-node, that has a "replace part of a string"-option, not the other way around.

That's a pretty nice solution, however I do prefer my own setup, since it is more compact.

Not sure what you mean with compact (you can hide the labels if that is what you mean), but then again, your setup does not work.

ALL seems very cluttered and unclean, why not use javascript or JSONata.

[{"id":"d25af463a7c58e7a","type":"inject","z":"c791cbc0.84f648","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"true-true","payloadType":"str","x":140,"y":3280,"wires":[["5f4fb69f255a7afd"]]},{"id":"5f4fb69f255a7afd","type":"change","z":"c791cbc0.84f648","name":"Make boolean jsonata","rules":[{"t":"set","p":"payload","pt":"msg","to":"$not($contains($$.payload, \"false\"))","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":420,"y":3320,"wires":[["f46e3f30e8f2459c"]]},{"id":"f46e3f30e8f2459c","type":"debug","z":"c791cbc0.84f648","name":"Output","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":680,"y":3300,"wires":[]},{"id":"a0200cdbd74d6e7f","type":"inject","z":"c791cbc0.84f648","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"false-true","payloadType":"str","x":150,"y":3320,"wires":[["5f4fb69f255a7afd"]]},{"id":"cced1885.8c6288","type":"inject","z":"c791cbc0.84f648","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"false-true-true","payloadType":"str","x":170,"y":3360,"wires":[["5f4fb69f255a7afd"]]},{"id":"dd806c66.44737","type":"inject","z":"c791cbc0.84f648","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"true-true","payloadType":"str","x":140,"y":3400,"wires":[["e8eaa3be.7a6118"]]},{"id":"598f8474.2a84d4","type":"inject","z":"c791cbc0.84f648","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"false-true","payloadType":"str","x":150,"y":3440,"wires":[["e8eaa3be.7a6118"]]},{"id":"bee70e3b.a04a2","type":"inject","z":"c791cbc0.84f648","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"false-true-true","payloadType":"str","x":170,"y":3480,"wires":[["e8eaa3be.7a6118"]]},{"id":"e8eaa3be.7a6118","type":"function","z":"c791cbc0.84f648","name":"make boolean Javascript","func":"msg.payload = !msg.payload.includes(\"false\")\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","x":430,"y":3420,"wires":[["f22f0989.030e4"]]},{"id":"f22f0989.030e4","type":"debug","z":"c791cbc0.84f648","name":"Output","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":660,"y":3420,"wires":[]}]
1 Like

I meant that my solution (which does work, check the 'Workaround' section in my original post) has 1 node, and yours has three. I prefer the compactness of only one node. That doesn't mean your option is bad, it's probably even clearer for some people, but it's just not what I prefer.

I disagree that my workaround is cluttered or unclean, but I must admit that your options are definitely a very nice one, especially for those who have coding experience.

A tip is to have a look at "node-red-contrib-boolean-logic-ultimate" when dealing with booleans.
https://flows.nodered.org/node/node-red-contrib-boolean-logic-ultimate

I appreciate the tip, but none of the boolean logic addons I tried have worked for me. I need an output on every 2 inputs, not on every single input. When using any boolean logic nodes, they output twice, because they get two inputs. Example:

Boolean logic nodes

  • Input 1: true - Output: nothing (that's fine)
  • Input 2: false - Output false (that's fine)
  • Input 1: false - Output false (that's not fine, I don't want this output)
  • Input 2: true - Output false (that's fine, this one I do want).

From this point on, an output will happen for every single input. I don't want that.

My case

  • Input 1: true - Output nothing
  • Input 2: true - Output true
  • Input 1: false - Output nothing
  • Input 2: true - Output false

I'ts a bit difficult to understand exactly what You want.

First is this what You want when the inputs is evaluated?
In1 In2 Out
0 0 0
1 0 0
1 1 1
0 1 0

Second
You only want to evaluate the output to change for every time You received both Input 1 and 2?

Is that correct?

Well, my initial question was simple: Why do I get a string output when I clearly should be getting a boolean output.

Check out this flow:

[{"id":"a715fa1be868c843","type":"inject","z":"0d7546f9dbe9c472","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":200,"y":360,"wires":[["f1c8fe3fb6b0b94a","6263e32abd9e52f7"]]},{"id":"f1c8fe3fb6b0b94a","type":"change","z":"0d7546f9dbe9c472","name":"","rules":[{"t":"set","p":"payload","pt":"msg","to":"true","tot":"bool"},{"t":"set","p":"topic","pt":"msg","to":"1","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":360,"y":340,"wires":[["4546ff9c54181161"]]},{"id":"6263e32abd9e52f7","type":"change","z":"0d7546f9dbe9c472","name":"","rules":[{"t":"set","p":"payload","pt":"msg","to":"false","tot":"bool"},{"t":"set","p":"topic","pt":"msg","to":"2","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":360,"y":380,"wires":[["4546ff9c54181161"]]},{"id":"4546ff9c54181161","type":"BooleanLogicUltimate","z":"0d7546f9dbe9c472","name":"","filtertrue":"both","persist":true,"sInitializeWith":"WaitForPayload","triggertopic":"trigger","outputtriggeredby":"all","inputCount":2,"topic":"result","restrictinputevaluation":true,"x":560,"y":360,"wires":[["22100f18cc9de61c"],[],[]]},{"id":"22100f18cc9de61c","type":"debug","z":"0d7546f9dbe9c472","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":770,"y":340,"wires":[]}]

Hit the inject button once. You get 1 output. Hit it again. Now you get 2 outputs, since the boolean operator already has a state for both inputs, so as soon as 1 changes, the output changes. For normal boolean operations, that's fine, but I don't want to consider any previous states.

What I currently have works fine for me though, and E1cid's suggestion may be even better, plus the original question as to why that strange behavior was happening got answered, so I guess this topic has been solved.

Is this what You want

[{"id":"93bf9b74a919620a","type":"group","z":"c75f720a.93555","style":{"stroke":"#999999","stroke-opacity":"1","fill":"none","fill-opacity":"1","label":true,"label-position":"nw","color":"#a4a4a4"},"nodes":["0a9e1598616b651e","67469ac6b90c24d6","6f4740eeb4c3f342","d463f5c5b4ca9c61","a2973792875b91aa","6f4c3aa4f75c114e","01b84b97eecdf51c","bb155074b6257dda"],"x":74,"y":1519,"w":892,"h":222},{"id":"0a9e1598616b651e","type":"inject","z":"c75f720a.93555","g":"93bf9b74a919620a","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"input1","payload":"true","payloadType":"bool","x":180,"y":1600,"wires":[["a2973792875b91aa"]]},{"id":"67469ac6b90c24d6","type":"inject","z":"c75f720a.93555","g":"93bf9b74a919620a","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"input1","payload":"false","payloadType":"bool","x":190,"y":1560,"wires":[["a2973792875b91aa"]]},{"id":"6f4740eeb4c3f342","type":"inject","z":"c75f720a.93555","g":"93bf9b74a919620a","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"input2","payload":"false","payloadType":"bool","x":190,"y":1660,"wires":[["a2973792875b91aa"]]},{"id":"d463f5c5b4ca9c61","type":"inject","z":"c75f720a.93555","g":"93bf9b74a919620a","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"input2","payload":"true","payloadType":"bool","x":180,"y":1700,"wires":[["a2973792875b91aa"]]},{"id":"a2973792875b91aa","type":"join","z":"c75f720a.93555","g":"93bf9b74a919620a","name":"","mode":"custom","build":"object","property":"payload","propertyType":"msg","key":"topic","joiner":"\\n","joinerType":"str","accumulate":false,"timeout":"","count":"2","reduceRight":false,"reduceExp":"","reduceInit":"","reduceInitType":"","reduceFixup":"","x":430,"y":1620,"wires":[["01b84b97eecdf51c","bb155074b6257dda"]]},{"id":"6f4c3aa4f75c114e","type":"debug","z":"c75f720a.93555","g":"93bf9b74a919620a","name":"dbg Outp","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":860,"y":1620,"wires":[]},{"id":"01b84b97eecdf51c","type":"debug","z":"c75f720a.93555","g":"93bf9b74a919620a","name":"dbg Join","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":640,"y":1660,"wires":[]},{"id":"bb155074b6257dda","type":"function","z":"c75f720a.93555","g":"93bf9b74a919620a","name":"Inp1 AND Inp2","func":"let newMsg = {};\n\nnewMsg.payload = msg.payload.input1 && msg.payload.input2;\n\nreturn newMsg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":660,"y":1620,"wires":[["6f4c3aa4f75c114e"]]}]

Well, that's what I had from the very beginning as a workaround, but with a function node instead of a change node, and it's also pretty much exactly what E1cid suggested.

If you want to avoid joining, in your c code, transmit both values together as a JSON string. E.g. { "input1":1, "input2":1 }

The MQTT in node can parse the JSON back into an object where both values, as actual numbers, will be accessible as properties of the payload as msg.payload.input1 and msg.payload.input2 which you can use directly in a function e.g.

msg.payload = msg.payload.input1 && msg.payload.input2;
return msg;
1 Like

This topic was automatically closed 60 days after the last reply. New replies are no longer allowed.