Change node value issue

Hello and Good evening
I have a change node with 6 items
I pass it a string "10" and change the output to Input 1 triggered
I pass it a string "20" and change the output to Input 2 triggered
I pass it "100" and it gets confused it should display Input 10 triggered
Does the change node not look for absolute values?

Thank you in anticipation
Andrew

Hi, it is quite unclear what you are doing from the text description.

Can you please create a demo flow, export it (select the nodes, press CTRL-E, copy to clipboard) and post it here. Include inject nodes with the values (rather than some external data source we have no access to)

See this post for how to post a flow - How to share code or flow json

"100" is not "10", try 'contains 10'

Ok done thank you

I'm sorry you feel that way. I certainly wasn't being rude and I did politely ask "Can you please create a demo flow". This would help me understand your issue better (so I could help).

5 Likes

Or at least show us how you have configured the Change node.

Hello Andrew :grinning_face:

I think it does look for absolute values, but after transforming "100" to "Input 10 triggered", it moves to the next comparison where the payload still matches "10".

Have you considered regex or jsonata in your change node?

A regular expression can distinguish between "10" and "100" by specifying the start and end of the string eg ^100$

As for jsonata you can convert the input string to a number, divide it by 10 and concatenate with string prefix and suffix.

If you need a worked example to experiment with, let me know.

Hi Steve
My apologies for my curt response, I am teaching my grandson to code and he has joined various forums to gain help. So many of them don't take into account he is only 10 years old and many responses end up embarrassing him. I fully agree with people having to show there workings to prove they are at least making an effort, The issue with my flow is it has a couple of home made nodes and a couple of weird functions and thought it would be pointless.
In closing thank you for your help

Regards
Andrew

I don't understand. The issue you asked about was specific to the operation of the Change node, not anything else in the flow. Have you determined that the Change node is operating as it should?

Good morning
Many thanks for the prompt response, I sometimes think i have mastered node-red and then fall over with the simplest of issues. I would be very grateful if you could briefly explain using both options regex and json. I did use if statements in my function but it ran to slow that's why i went with the change node

Kind Regards
Andrew

My data via UDP is
The first character is the input ID
The second character is its state 0 = open - 1 = closed

00 = Input 0 triggered
01 = Input 0 reset

10 = Input 1 triggered
11 = Input 1 reset

20 = Input 2 triggered
21 = Input 2 reset

etc etc
the issue in the change node occurs here

100 = Input 10 triggered
101 = Input 10 reset

Please show us what that looks like in a Debug node, just so that we can make sure we understand correctly.

Unless you are processing thousands of messages a second then there will be no measurable difference between a function node and a change node. Personally I would use a Function node.

This is exactly the sort of problem that I am not ashamed get ChatGPT to help with. I asked it:

Please provide javascript code for a node-red function node that converts the string "xxx0" into "Input xxx triggered, "xxx1" into "Input xxx reset", where the string xxx is variable.

It provided the following function

// Get input string from msg.payload
let input = msg.payload;

// Use a regular expression to extract the variable part and the digit
let match = input.match(/^(.+)([01])$/);

if (match) {
    let base = match[1];
    let state = match[2];

    if (state === "0") {
        msg.payload = `Input ${base} triggered`;

    } else {
        msg.payload = `Input ${base} reset`;
    }
} else {
    // Handle unexpected input
    msg.payload = `Unrecognized input format: ${input}`;
}

return msg;

Which looks pretty good to me, though I would be sure to test it carefully before accepting it.

I then asked it to replicate that in a Change node and it suggested using JSONata to set msg.payload to:

$$.payload ~ /^(.+)(0)$/ ? "Input " & $match(1) & " triggered" : $$.payload ~ /^(.+)(1)$/ ? "Input " & $match(1) & " reset" : $$.payload

Which may well be correct, but I would not be prepared to use that I my knowledge of JSONata is not sufficient that I can understand what it does with any confidence.

Regular expressions are slow.
JSONata is slow

So I am surprised the function was slow - perhaps there was string manipulation the your original function? (string manipulations are slow)

Here is a mock up of how I might handle this - may be of use?

chrome_xrnAFCn0TJ

[{"id":"1b0d37fb2471123d","type":"inject","z":"b1993d2a213b2bb0","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"00","payloadType":"str","x":1510,"y":1020,"wires":[["d7b3f0f7e6adb49e"]]},{"id":"d7b3f0f7e6adb49e","type":"junction","z":"b1993d2a213b2bb0","x":1640,"y":1060,"wires":[["d6823181f9c5a68c"]]},{"id":"11958afbcbb212b3","type":"inject","z":"b1993d2a213b2bb0","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"101","payloadType":"str","x":1510,"y":1260,"wires":[["d7b3f0f7e6adb49e"]]},{"id":"4f746604fc72f5ee","type":"inject","z":"b1993d2a213b2bb0","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"100","payloadType":"str","x":1510,"y":1220,"wires":[["d7b3f0f7e6adb49e"]]},{"id":"000c164834baf795","type":"inject","z":"b1993d2a213b2bb0","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"01","payloadType":"str","x":1510,"y":1060,"wires":[["d7b3f0f7e6adb49e"]]},{"id":"9c4c4cf31f1c799f","type":"inject","z":"b1993d2a213b2bb0","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"50","payloadType":"str","x":1510,"y":1120,"wires":[["d7b3f0f7e6adb49e"]]},{"id":"47e5392a2d4704e8","type":"inject","z":"b1993d2a213b2bb0","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"51","payloadType":"str","x":1510,"y":1160,"wires":[["d7b3f0f7e6adb49e"]]},{"id":"a34440712cb2d85b","type":"inject","z":"b1993d2a213b2bb0","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"990","payloadType":"str","x":1510,"y":1320,"wires":[["d7b3f0f7e6adb49e"]]},{"id":"290c661b2db3d393","type":"inject","z":"b1993d2a213b2bb0","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"991","payloadType":"str","x":1510,"y":1360,"wires":[["d7b3f0f7e6adb49e"]]},{"id":"d6823181f9c5a68c","type":"function","z":"b1993d2a213b2bb0","name":"split item and flag","func":"const parts = msg.payload.split('')\nconst flagLookup = {\n    \"0\": \"triggered\",\n    \"1\": \"reset\",\n}\nmsg.payload = {\n    item: parts.slice(0, -1).join(''),\n    flag: flagLookup[parts.pop()] || 'unknown'\n}\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":1770,"y":1060,"wires":[["4fdee4fb2e42921c"]]},{"id":"4fdee4fb2e42921c","type":"debug","z":"b1993d2a213b2bb0","name":"","active":true,"tosidebar":true,"console":false,"tostatus":true,"complete":"true","targetType":"full","statusVal":"payload","statusType":"auto","x":1770,"y":1120,"wires":[]}]

without regex:

  const val = parseInt(inputStr, 10);
  const input = Math.floor(val / 10);
  const event = val % 10 === 0 ? "triggered" : "reset";
[{"id":"b6142f1f57586b26","type":"inject","z":"a29990d4d3ce3eb1","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"10","payloadType":"str","x":310,"y":760,"wires":[["bd51e8de662f200a"]]},{"id":"59a373c4b209ba42","type":"inject","z":"a29990d4d3ce3eb1","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"11","payloadType":"str","x":310,"y":800,"wires":[["bd51e8de662f200a"]]},{"id":"bb679be7f8dc6be2","type":"inject","z":"a29990d4d3ce3eb1","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"100","payloadType":"str","x":310,"y":860,"wires":[["bd51e8de662f200a"]]},{"id":"e93b31e23942b15c","type":"inject","z":"a29990d4d3ce3eb1","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"101","payloadType":"str","x":310,"y":900,"wires":[["bd51e8de662f200a"]]},{"id":"28325d5514584096","type":"debug","z":"a29990d4d3ce3eb1","name":"debug 58","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":820,"y":840,"wires":[]},{"id":"bd51e8de662f200a","type":"function","z":"a29990d4d3ce3eb1","name":"parseInputEvent","func":"function parseInputEvent(inputStr) {\n  const val = parseInt(inputStr, 10);\n  const input = Math.floor(val / 10);\n  const event = val % 10 === 0 ? \"triggered\" : \"reset\";\n\n  if (input >= 1 && input <= 10) {\n    node.status({fill:\"green\",shape:\"dot\",text:`input ${input}: ${event}`});\n    return { input, event };\n  } else {\n    node.status({fill:\"red\",shape:\"dot\",text:\"Out of Range\"});\n    return null; // Out of range\n  }\n}\n\nmsg.payload = parseInputEvent(msg.payload)\n\nreturn msg","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":600,"y":840,"wires":[["28325d5514584096"]]}]

You changed the requirement!

Looking just at the last character I can set msg.suffix to be either " reset" or " triggered" using a ternary expression (<if something> ? <value> : <otherwise>)

And shedding the last character:


I get "Input 1" ... "Input 10"

Concatenating them

[{"id":"0c6c144a4b5511a5","type":"change","z":"8e65d60a7b0b8156","name":"","rules":[{"t":"set","p":"suffix","pt":"msg","to":"$substring(payload, -1)  =  \"1\" ? \" reset\" : \" triggered\"","tot":"jsonata"},{"t":"set","p":"prefix","pt":"msg","to":"\"Input \" & $substring(payload, 0, $length(payload) -1)","tot":"jsonata"},{"t":"set","p":"payload","pt":"msg","to":"prefix & suffix","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":280,"y":80,"wires":[["3a2f3178a32dd4f3"]]},{"id":"963aa609d3bae5c8","type":"inject","z":"8e65d60a7b0b8156","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"10","payloadType":"str","x":110,"y":60,"wires":[["0c6c144a4b5511a5"]]},{"id":"82b48836e0465f64","type":"inject","z":"8e65d60a7b0b8156","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"101","payloadType":"str","x":110,"y":100,"wires":[["0c6c144a4b5511a5"]]},{"id":"3a2f3178a32dd4f3","type":"debug","z":"8e65d60a7b0b8156","name":"debug 8","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":460,"y":80,"wires":[]}]

You may be able to combine these into a single line in the change node.

ps, given the different requirement, I'd be inclined to use a function as @Steve-Mcl does

But neither will make any difference in practice unless thousands of messages a second are being processed.

Your solution is much neater however.

I think that might be the case as UDP is mentioned - but its just a hunch.

Steve's function is neat and no doubt much faster than string manipulations.
It took me a minute or two to understand though and I would not relish explaining it to a 10 yo programmer.
(I wonder how @dynamicdave would code this requirement for youngsters)

So a function which might be more approachable for a beginner, which does manipulate strings:

let suffix // have to declare this variable outside the if block

const lastcharacter = msg.payload.slice(-1)
if (lastcharacter === "0") {  // NB triple = sign so the left & right sides must be same datatype - string
    suffix = " triggered"
}  
else {
    suffix = " reset"
} 

const alltherest = msg.payload.slice(0, -1) // From the 1st character to last but 1

msg.payload = "Input " + alltherest + suffix  // Javascript knows to concatenate not add if one of the values is a string

return msg;
1 Like

I think your submission is "spot-on" and makes complete sense.
I wouldn't have any issues explaining it to my IoT students.

@fogmajor
That being so Andrew, perhaps you would be kind enough to edit your post above using the pencil icon below the post.

Regards

Paul
Moderator