Beginner questions on IF expressions and wildcard usage in function node

Yes, I just didnt mention it because it only makes sense in seclusion - typically a function node makes use node context - which is accessible only to its self. But a function node can also read/write (own)flow & global context

Node context, not path context.

Yes, I will be using node context, probably.

One of the steps will be "compare previous value to current" and for that I wanted to use node context, because the comparison of that value will always only be in that function node.

EDIT:
Small side question: must a msg always have a msg.payload? Or can I remove that and use custom msg properties only (like msg.Temperature)?

Yes, you can delete msg.payload - but TBH, it is not necessary - just ignore it.

Yes you can add msg.anythingYouWant :slight_smile:

PS: it is good practice to use camelCase in JS for your variable names (then you never forget if it starts with a capital letter etc)

Why are you doing this? Is it to only release a msg if it has changed since the last time? Or something else?

Right now the payload contains the value I want and I created a msg.Parameter which holds the information of which parameter that value belongs to, e.g. msg. Temperature.

I was thinking of renaming msg.payload to msg.Temperature. That way I have one less msg component and directly see the parameter.

I do not want to publish on every change. I want to only publish if the value changes by a relevant amount.
I do not want to know if a value changes from 1500 W to 1503 W. But I do want to know if a value changes from 1 W to 3 W. So I want to use thresholds and power deltas.
Values below 10 W should report 3 W ichanges Values above 1000 W should report only 50 W changes. Always compared to the last value that cause a publish, of course.

in which case, you should really use the built in nodes. e.g. the filter (RBE) node.

image

There are lots of core nodes designed to do all the things you are attempting to do in functions.

1 Like

Oh, I will definitely try that node first!

I am currently cutting down nodes and was wondering:
When do I need to use ; and when do I not?

It does not seem to matter whether I use one after DewPoint of not. But in other languages you have to end each command/statement with one.

    msg.DewPoint = msg.payload.DewPoint
    return msg;

It is mostly optional and a stylistic choice in JavaScript. I chose not to use them. Like i adhere to camelCase and other style options like 'single quotes' and space after comma etc. Once you have a style, it helps you find things and spot typos etc.

No, it is necessary. If there is no return, no msg is transmitted to the next node. That can be useful.

e.g.

if (everything is ok) {
   return msg // send to next node 
} else {
   // do nothing - dont send msg to next node
}

Is there a function that can delete a msg property? Of coure the change node could, but in a function. A command like set()

:point_up:

delete msg.payload

1 Like

duh, I did not read it as a command :smiley:

As Steve said you can use whatever property names you like, but we tend to use payload as the convention for where the ā€œinterestingā€ part of the data is, as that is where most core nodes expect it to be. So things like the filter/rbe node should ā€œjust workā€ without needing extra configuration if the data is in msg.payload

The problem with payload is that it no longer holds the information on the entity (Energy Power, Energy Total, Humidity, Temperature etc.). So I would need to store that information elsewhere.
The cleaner approach is to use a msg property named after the entity. I then know precisely at all times what that value belongs to.

Since the payload is then no longer interesting to me, I would delete it (just like "_msgid").

I just took a look at it and I do not think it is capable of handling what I need. I am not using a fixed threshold.
Here is what I once used in a simple tasmota approach (different but easily understandable syntax).

IF ((%value%>=var1) AND (var11!=var2)) powerdelta1 %var2%
ELSEIF ((%value%>=var3) AND (var11!=var4)) powerdelta1 %var4%
ELSEIF ((%value%>=var5) AND (var11!=var6)) powerdelta1 %var6%
ELSEIF ((%value%>=var7) AND (var11!=var8)) powerdelta1 %var8%
ELSEIF ((%value%<var7) AND (var11!=var10)) powerdelta1 %var10% ENDIF;
IF ((%value%>=var7) AND (var14!=600)) teleperiod 600
ELSEIF ((%value%<var7) AND (var14!=3600)) teleperiod 3600 ENDIF ENDON

%value% is the current sensor value, var11 is the previous value of the last reading, uneven numbered vars are the thresholds (e.g. if value > 100 W) and even numbered vars are the power delta (if value change > 3 W).

So the delta is dependent on the absolute value. I do not see this supported in the filter (RBE) node.

EDIT:
Just tried deleting

const matchingKeys = Object.keys(msg.payload).filter((element) => element.startsWith("ATC"))
matchingKeys.forEach((element) => node.send({
    topic: "tele/xiaomi/" + element,
    payload: msg.payload[element],
    Time: msg.payload.Time,
    delete msg._msgid
}))

but I get an error notification.
Here is an example msg object

{"topic":"tele/xiaomi/ATC4e211d","payload":{"mac":"a4c1384e211d","Temperature":33.6,"Humidity":31.9,"DewPoint":14.6,"Btn":1,"Battery":80,"RSSI":-67},"Time":"2023-09-05T18:04:02","_msgid":"33304bf813b225db","Temperature":33.6}

Sure not a problem. Whatever works for you.
Though you canā€™t remove _msgid as if a node finds it missing it will recreate one on the next send.

Ah, _msgid was added by Node-RED? I thought it was something tasmota added and I never noticed :smiley:

Yes, itā€™s there for msg tracing. Usually it gets added at the start of a flow (first node) and can then be tracked through to its exit. There are times when thatā€™s not possible , eg if a function node creates a new sequence of msgs, and they also get added whenever they are missing :slight_smile:

If this board only deals with the Bluetooth devices you can change the topic for just this device.
eg ATC instead of tele - in Tasmota cmd window just type Prefix3 ATC

See topic-definition

A new MQTT in node can then subscribe to the Xiaomi Mijia / eq3 messages only. Then there is no processing required in NR to separate out these message types.

And to extract the information more easily "per" device just split and use a switch node to remove any parts without a mac address property. Based on the example message you posted earlier, here is a simple flow to show how.

[{"id":"4edc0ded06967bce","type":"inject","z":"be14baae.1728","name":"","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"{\"Time\":\"2023-09-03T14:48:50\",\"ATC0d5c0c\":{\"mac\":\"a4c1380d5c0c\",\"Temperature\":21.6,\"Humidity\":66.2,\"DewPoint\":15,\"Btn\":0,\"Battery\":37,\"RSSI\":-68},\"ATC8f1b4f\":{\"mac\":\"a4c1388f1b4f\",\"Temperature\":21.9,\"Humidity\":62.9,\"DewPoint\":14.5,\"Btn\":0,\"Battery\":69,\"RSSI\":-62},\"TempUnit\":\"C\"}","payloadType":"json","x":245,"y":3660,"wires":[["49aeb16edeb19263"]]},{"id":"8f93c04cbdd9d362","type":"debug","z":"be14baae.1728","name":"debug 330","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":715,"y":3660,"wires":[]},{"id":"49aeb16edeb19263","type":"split","z":"be14baae.1728","name":"","splt":"\\n","spltType":"str","arraySplt":1,"arraySpltType":"len","stream":false,"addname":"topic","x":385,"y":3660,"wires":[["188759e44de3ee9e"]]},{"id":"188759e44de3ee9e","type":"switch","z":"be14baae.1728","name":"","property":"payload","propertyType":"msg","rules":[{"t":"hask","v":"mac","vt":"str"}],"checkall":"true","repair":false,"outputs":1,"x":545,"y":3660,"wires":[["8f93c04cbdd9d362"]]}]

Each message contains all the relevant information with a topic unique to the device, you can then direct this as required based on the topic.

Color me impressed. You either use tasmota or really did your homework :clap:

The device deals with all bluetooth devices and a card reader. So using the prefix would only result in being able to distinguish between that particular ESP32 board and the other (tasmota) devices. For that I would not need to change the prefix because the esp32 board has its own topic anyway (based on mac).

There is a feature called GroupTopicthat should group devices into a dedicated GroupTopic. I used it once but then did not re-enable it because group topics are limited to 4 group topics of which one is for tasmota and the other three can be customized.

To prevent running out of group topics, I decided to add the extra step in Node-RED rather than on the tasmota devices.
In case someone is wondering: using more ESP32 boards is not the solution either. They will all receive the BLE data form all broadcasting BLE devices and then process in parallel. I would need to filter the BLE devices on each ESP32 board. Again, more work than one node in Node-RED :slight_smile:

There is a solution that I have been using in the past for the actual filtering on the ESP32 board: Berry.
The scripting language Berry lets me perform these filtering/powerdelta tasks on the board in tasmota. I then published the processed data on a dedicated topic per device.
However, Berry is well documented but hardly used. So despite the support by the tasmota team being excellent, it is difficullt to learn and read up on.
Plus, again, Berry far more work than Node-RED.

So, right now, idea is to keep 99 % of all work in Node-RED and do as little as possible on the tasmota devices :slight_smile:

So, following some of the suggestions in this thread, I have now cut down my nodes (no, still using some functions where I could use normal nodes :wink: ) and named a bit cleaner. Square barckets are changes to the msg).

EDIT:
Is there a way I can connect a debug node to any of the outputs just to check if the flow variables were initialized? Or would I need to write a function that creates a msg containing the variable and hook that up to a debug node?

EDIT2:
Is it possible to sort the properties in a msg? This is not so vital, but nicer and I am curious :slight_smile: