Is this a bug - flow.set()

I have a network of ESP32 based devices which monitor the moisture levels in my Houseplants. Each device identifies itself to NodeRed via an MQTT message with the IP Address of the device as the Topic. I want to be able to know if any device has stopped working and am therefore storing the last time they polled in Context using the IP address as the id. UNfortunately this doesn't work. It can be exemplified by the following code.

[{"id":"894faba19e37b2c5","type":"function","z":"6569e3df316afd50","name":"function 18","func":"let currentTime = new Date().getTime();\nlet ip = \"192.120.1.23\";\nflow.set(ip,currentTime);\n\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":310,"y":620,"wires":[[]]}]

and this is the result in Flow

It certainly doesn't give a very pretty result.

A work-around might be
flow.set(ip.replace(/\./g, '_'),currentTime);

The reason that is happening is because we are allowed to specify a javascript object description as the id. For example, one could write flow.set("a.b", 42) and the a.b would be treated as meaning property b of object a. So by using "192.120.1.23" you are creating a multi-dimensional array named "192", where you are setting element 120 (which implies filling in empty values 0 to 119) to another array where element 1 contains the object "23" which has been set to the current time.

I am not sure if there is a solution. I will think about it.

This seems to work

flow.set(`"${ip}"`,currentTime)

By the way, you don't generally need semi-colons on the line ends in javascript any more.

Thanks for the suggestions.

My own workaround was to just use the last segment of the ip address and concatenate that to "IP". All my addresses are on the local network so I think that will work as well.

I'm 76 years old and started off life 52 years ago as a COBOL programmer where the semi-colon was absolutely sacrosanct. But now most of my non Javascript code is Arduino IDE where once again it rules so I do it out of habit now.

I thought that the original problem might have something to do with the Javascript object and I guess it is a problem when you have typeless variables. Its odd though to get data and data structure confused.

Incidentally you can achieve an alert when one of your ESPs goes offline by using a single trigger node, no code at all. :laughing:

Each ESP is using a unique msg.topic , it's IP address.
Send all the messages to a trigger node set to send nothing, wait for a certain time and then send the last message received:
image

The message sent on will be the last message from the ESP which has stopped sending, including the device IP.

1 Like

Um,

Why not use the LWT of MQTT?

LWT is sent if a device goes offline.

So you have a MQTT-IN node and it listens to the LWT topic.

Then if one of the devices goes offline, you get a message and it (the message) will tell you which device it is - bonus. :wink:

I know this is an older version of MQTT, but for the sake of sharing.

If you are interested, I can post some arduino code that sets the LWT topic.

2 Likes

Thank you for that suggestion I will give it a try. Although often I like to use tried and tested methods that are independent of the platform I'm using. Because my moisture sensors are battery powered I'm putting them into deep sleep between samples and the sample interval is 30 minutes. When the device wakes it polls NodeRed for its calibration data and returns the latest readings from the sensor. After handling the the data returned, the function node checks the time stamp for all the other sensors and sends me an email for any that require attention. I'm still working out in my own mind how often the e-mail should be resent if the issue is not resolved.

Sometimes my elderly brain finds the specification of some inbuilt functions too hard to decipher and it is easier to write a bit of code myself. I'm totally self taught in the last 3 years on everything I have learned about IoT, including javascript, Arduino, NodeRed, HomeAssistant , MQTT and the building of the devices that the routines written using these apps monitor and control

I'm not sure whether this is relevant as I am not using the NodeRed MQTT broker but thank you for your suggestion and please correct me if I'm wrong. You might also like to read my response to @jbudd 's suggestion above

Yeah, he and I replied at about the same time.

His way is also nice and does work with given nodes.

You set the LWT on the device. Not the broker.

I showed you my config to save me typing.

It seems that lots of folks use Node-red primarily as an environment for writing and running snippets of javascript and indeed it is very approachable for that (I have no idea how to write or run javascript outside of Node-red!)

Don't neglect all the other nodes though, it is very a capable language without having to write [much] code.

I think @Trying_to_learn's suggestion is considerably more elegant than mine.

1 Like

To expand on my LWT suggestion:

an exert from my code for my Arduino.

byte willQoS = 0;
boolean willRetain = false;
const char* willTopic = "EOM";
const char* willMessage = "GPS comms FAILURE";
byte bsQoS = 0;
boolean bsRetain = false;
const char* bsTopic = "SOM";
const char* bsMessage = "GPS comms UP";

I use the topic EOM and SOM.
(don't ask. I goofed it should be EOL and SOL)

So I Publish the message GPS comms FAILURE to the EOM topic.

I put a MQTT-IN node with the broker you use.
Set the topic to EOM - in my case - and then stick a debug node on it.

If the comms fails between my device MQTT publishes the declared message with the topic of EOM.

So......
You look for a message coming out of the MQTT-IN node set with topic EOM.
The message would tell you which device has failed.

With me?