Parsing nested object with wildcard

I have a series of incoming MQTT messages which I use a JSON node to convert to an object. When I only had a few devices I used a Switch node to separate each device but it's getting unwieldy.
A sample message is
"{"ZbReceived":{"Laser":{"Device":"0x1FFC","Name":"Laser","Power":0,"Endpoint":1,"LinkQuality":39}}}"
The problem I have is that 'Laser' varies for each device so I also get
"{"ZbReceived":{"Front":{"Device":"0x8664","Name":"Front","Illuminance":31630,"Endpoint":2,"LinkQuality":39}}}"
I need to extract the Name which becomes part of the topic of an outgoing MQTT message.
Power, Illuminance, etc I can get by using a Switch node to handle that part of the MQTT outgoing message.
Is there a way to 'wildcard' into the object to get the Name? I've tried msg.payload[1].Name which doesn't work. I may be missing something blindingly obvious...

Try msg.payload.ZbReceived.Name
read this page it show how to get the paths from the debug pain.
https://nodered.org/docs/user-guide/editor/sidebar/debug

Also the mqtt node will output a object if you tell it to in the config, no need to use the json node.

[edit never mind miss read your examples as they are poorly formatted. Here is how to extract the name with a change node and JSONata expression

[{"id":"c398fe78.b3b838","type":"inject","z":"c74669a0.6a34f8","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"{\"ZbReceived\":{\"Laser\":{\"Device\":\"0x1FFC\",\"Name\":\"Laser\",\"Power\":0,\"Endpoint\":1,\"LinkQuality\":39}}}","payloadType":"json","x":150,"y":340,"wires":[["1a4b3800.16c8"]]},{"id":"1a4b3800.16c8","type":"change","z":"c74669a0.6a34f8","name":"","rules":[{"t":"set","p":"payload.Name","pt":"msg","to":"payload.ZbReceived.*.Name","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":380,"y":340,"wires":[["49677333.de49dc"]]},{"id":"49677333.de49dc","type":"debug","z":"c74669a0.6a34f8","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":560,"y":340,"wires":[]}]

After the change node msg.payload.Name will hold the name.]

1 Like

Thanks. It's a pity that the wildcard won't work in a function nade

In a function node you can get the name

msg.payload.ZbReceived[Object.keys(msg.payload.ZbReceived)[0]].Name

Great gone from

to

Think can slim down further using the function block version

Regards

Philip Knowles

I would think that you could get down to just 3 or 4 nodes, The mqtt node can output a json so there should be no need for the 2 json nodes, and everything else (switch, template) could be done in a single function node.

... at the expense of readability of the flow...

That is a personnel choice, what is readable and easy for one may not be for others.

Yes the Change node you suggested would only work with a JSON node and my later Change node would only work if I converted back. Using a Function node should enable me to dispense with a lot – but that’s for another day.

I can now add ZigBee devices without having to hard code them so that’s a vast improvement

Thanks

Having to hard code each new ZigBee device for ‘readability’ sake is a recipe for introducing errors. Now I can add a new device and name it in the bridge and don’t need to touch node-red to get the results that my control system can use.

Thanks @E1cid.

I’ve now got it down to an MQTT in node a Function node and MQTT out node

Is there a neater way than this though for each of the parameters? I’m basically checking whether each of the 6 keys exists or not but it looks ‘clunky’

if (msg.payload.ZbReceived[Object.keys(msg.payload.ZbReceived)[0]].Humidity != null) {

payload = msg.payload.ZbReceived[Object.keys(msg.payload.ZbReceived)[0]].Humidity;

topic = "/Humidity"

}

You should be able to get down to 3 - 4 nodes. Post flow for more advise, attach 3 inject nodes with json examples of 3 different device input payloads, that way I have a clear understanding

Sorry you misunderstood

This is what I have it down to

784982F619D449B28F54ACFC08733E4E.png

The function block is the bit that’s clunky

var name = msg.payload.ZbReceived[Object.keys(msg.payload.ZbReceived)[0]].Name;

var payload = "";

var topic = "";

if (msg.payload.ZbReceived[Object.keys(msg.payload.ZbReceived)[0]].Humidity != null) {

payload = msg.payload.ZbReceived[Object.keys(msg.payload.ZbReceived)[0]].Humidity;

topic = "/Humidity"

}

if (msg.payload.ZbReceived[Object.keys(msg.payload.ZbReceived)[0]].Power != null) {

payload = msg.payload.ZbReceived[Object.keys(msg.payload.ZbReceived)[0]].Power;

topic = "/Power"

}

if (msg.payload.ZbReceived[Object.keys(msg.payload.ZbReceived)[0]].Temperature != null) {

payload = msg.payload.ZbReceived[Object.keys(msg.payload.ZbReceived)[0]].Temperature;

topic = "/Temperature"

}

if (msg.payload.ZbReceived[Object.keys(msg.payload.ZbReceived)[0]].Battery != null) {

payload = msg.payload.ZbReceived[Object.keys(msg.payload.ZbReceived)[0]].Battery;

topic = "/Battery"

}

if (msg.payload.ZbReceived[Object.keys(msg.payload.ZbReceived)[0]].Illuminance != null) {

payload = msg.payload.ZbReceived[Object.keys(msg.payload.ZbReceived)[0]].Illuminance;

topic = "/Illuminance"

}

if (msg.payload.ZbReceived[Object.keys(msg.payload.ZbReceived)[0]].Occupancy != null) {

payload = msg.payload.ZbReceived[Object.keys(msg.payload.ZbReceived)[0]].Occupancy;

topic = "/Occupancy"

}

msg.topic = "tele/ZigBee/" + name + topic;

msg.payload = payload

return msg;

Is there a better way to check if a key exists in the payload? I could, for example, do

temp = msg.payload.ZbReceived[Object.keys(msg.payload.ZbReceived)[0]].Humidity;

if (temp != null) {

payload = temp;

topic = “Humidity”;

}

I’m not sure whether that is more efficient

There is only ever 1 key in the payload and I’m checking for every key each time. If I could find which key was in the payload I could then just read the key and do the rest.

No I understood, I wanted the inject nodes and your code so i could make an test flow. As it easier to import a flow with the injects, rather than construct from scratch. I need the injects as i do not have your data from the mqtt in node.
[edit best when pasting code is to put between backticks, as easier to copy and also more readable
e.g.
```
code
```
]

This is an example of the inputs

tele/ZigBee/SENSOR = {"ZbReceived":{"YardLight":{"Device":"0x419B","Name":"YardLight","Power":0,"Endpoint":1,"LinkQuality":13}}}

tele/ZigBee/SENSOR = {"ZbReceived":{"Front":{"Device":"0x8664","Name":"Front","Occupancy":1,"Endpoint":2,"LinkQuality":26}}}

tele/ZigBee/SENSOR = {"ZbReceived":{"WCFan":{"Device":"0xE193","Name":"WCFan","Power":0,"Endpoint":1,"LinkQuality":13}}}

tele/ZigBee/SENSOR =

{"ZbReceived":{"YardLight":{"Device":"0x419B","Name":"YardLight","Power":1,"Endpoint":1,"LinkQuality":10}}}

tele/ZigBee/SENSOR = {"ZbReceived":{"Front":{"Device":"0x8664","Name":"Front","Illuminance":46117,"Endpoint":2,"LinkQuality":21}}}

tele/ZigBee/SENSOR = {"ZbReceived":{"OutsideLight":{"Device":"0x7EA3","Name":"OutsideLight","Power":0,"Endpoint":1,"LinkQuality":86}}}

tele/ZigBee/SENSOR = {"ZbReceived":{"Front":{"Device":"0x8664","Name":"Front","Illuminance":46126,"Endpoint":2,"LinkQuality":21}}}

tele/ZigBee/SENSOR = {"ZbReceived":{"Utility":{"Device":"0x4779","Name":"Utility","Humidity":64.11,"Endpoint":1,"LinkQuality":68}}}

tele/ZigBee/SENSOR = {"ZbReceived":{"Front":{"Device":"0x8664","Name":"Front","Occupancy":0,"Endpoint":2,"LinkQuality":26}}}

tele/ZigBee/SENSOR = {"ZbReceived":{"Kitchen":{"Device":"0xDB86","Name":"Kitchen","Temperature":16.88,"Endpoint":1,"LinkQuality":81}}}

tele/ZigBee/SENSOR = {"ZbReceived":{"Kitchen":{"Device":"0xDB86","Name":"Kitchen","Humidity":60.41,"Endpoint":1,"LinkQuality":81}}}

You are not making life easy for people helping you, It would be nice to make life easier on helpers

1 by posting code properly formatted as explained
2 when some one asks for some thing do your best to supply the info or if you don't understand ask for clarification

here is a sample flow, it relies on the position of your value in the object, if this fails then you would need to hard code the object keys required in a lookup object.

[{"id":"4cf59f95.9e0ff","type":"inject","z":"c74669a0.6a34f8","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"{\"ZbReceived\":{\"Front\":{\"Device\":\"0x8664\",\"Name\":\"Front\",\"Illuminance\":31630,\"Endpoint\":2,\"LinkQuality\":39}}}","payloadType":"json","x":110,"y":1680,"wires":[["e0a1041f.97c47"]]},{"id":"e0a1041f.97c47","type":"function","z":"c74669a0.6a34f8","name":"","func":"var name = msg.payload.ZbReceived[Object.keys(msg.payload.ZbReceived)[0]].Name;\n\n\nmsg.topic = Object.keys(msg.payload.ZbReceived[name])[2]\n\nmsg.payload= msg.payload.ZbReceived[name][msg.topic]\nreturn msg;\n","outputs":1,"noerr":0,"initialize":"","finalize":"","x":330,"y":1700,"wires":[["896fbba5.052b98"]]},{"id":"896fbba5.052b98","type":"debug","z":"c74669a0.6a34f8","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":570,"y":1640,"wires":[]},{"id":"d04ac886.9fe248","type":"inject","z":"c74669a0.6a34f8","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"{\"ZbReceived\":{\"Laser\":{\"Device\":\"0x1FFC\",\"Name\":\"Laser\",\"Power\":0,\"Endpoint\":1,\"LinkQuality\":39}}}","payloadType":"json","x":100,"y":1740,"wires":[["e0a1041f.97c47"]]}]

notice how this is formatted and easy to copy

var name = msg.payload.ZbReceived[Object.keys(msg.payload.ZbReceived)[0]].Name;
msg.topic = Object.keys(msg.payload.ZbReceived[name])[2]
msg.payload= msg.payload.ZbReceived[name][msg.topic]
return msg;

Here is an example of a lookup. I set the lookup to context using the first inject to add more edit the object in the inject node. You could use dashboard to edit the lookup when adding devices. You could also move the lookup object into the function node if you wish.

[{"id":"4cf59f95.9e0ff","type":"inject","z":"c74669a0.6a34f8","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"{\"ZbReceived\":{\"Front\":{\"Device\":\"0x8664\",\"Name\":\"Front\",\"Illuminance\":31630,\"Endpoint\":2,\"LinkQuality\":39}}}","payloadType":"json","x":110,"y":1700,"wires":[["e0a1041f.97c47"]]},{"id":"e0a1041f.97c47","type":"function","z":"c74669a0.6a34f8","name":"","func":"var name = msg.payload.ZbReceived[Object.keys(msg.payload.ZbReceived)[0]].Name;\nmsg.topic = flow.get(\"ZbLookup.\" + name)\nmsg.payload= msg.payload.ZbReceived[name][msg.topic]\nreturn msg;\n","outputs":1,"noerr":0,"initialize":"","finalize":"","x":330,"y":1700,"wires":[["896fbba5.052b98"]]},{"id":"896fbba5.052b98","type":"debug","z":"c74669a0.6a34f8","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":570,"y":1640,"wires":[]},{"id":"d04ac886.9fe248","type":"inject","z":"c74669a0.6a34f8","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"{\"ZbReceived\":{\"Laser\":{\"Device\":\"0x1FFC\",\"Name\":\"Laser\",\"Power\":0,\"Endpoint\":1,\"LinkQuality\":39}}}","payloadType":"json","x":110,"y":1740,"wires":[["e0a1041f.97c47"]]},{"id":"a8a393f0.d214a","type":"inject","z":"c74669a0.6a34f8","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"{\"Front\":\"Illuminance\",\"Laser\":\"Power\"}","payloadType":"json","x":130,"y":1620,"wires":[["81080981.b21ca8"]]},{"id":"81080981.b21ca8","type":"change","z":"c74669a0.6a34f8","name":"","rules":[{"t":"set","p":"ZbLookup","pt":"flow","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":310,"y":1620,"wires":[[]]}]

click first inject to initialise the lookup object in context.
p.s. there is no error/data checking you will need to add some.

I appreciate your help. What you have shown me so far has significantly improved what I had but I was at the stage of trying to improve the JS in the function block not the rest of the flow. I was also replying in email rather than in the post so the formatting wasn't being applied.
What you sent was the final bit of the puzzle so it now is MQTT in node, a Function node (with 5 lines of code) and an MQTT out node.

So that's a brilliant result. Thank you

[{"id":"7b536af5.137f14","type":"function","z":"138f28a6.34a6d7","name":"","func":"var name = msg.payload.ZbReceived[Object.keys(msg.payload.ZbReceived)[0]].Name;\nvar topic = Object.keys(msg.payload.ZbReceived[name])[2]\nmsg.payload= msg.payload.ZbReceived[name][topic];\n topic = \"tele/ZigBee/\" + name +\"/\" + topic;\nmsg.topic = topic;\nreturn msg;\n","outputs":1,"noerr":0,"initialize":"","finalize":"","x":220,"y":540,"wires":[["3314335e.6b673c"]]},{"id":"37f31f7a.4a422","type":"mqtt in","z":"138f28a6.34a6d7","name":"ZigBee","topic":"tele/ZigBee/SENSOR","qos":"2","datatype":"json","broker":"bcb3a837.b64948","x":50,"y":540,"wires":[["7b536af5.137f14"]]},{"id":"3314335e.6b673c","type":"mqtt out","z":"138f28a6.34a6d7","name":"ZigBee","topic":"","qos":"","retain":"","broker":"bcb3a837.b64948","x":440,"y":540,"wires":[]},{"id":"bcb3a837.b64948","type":"mqtt-broker","name":"Mosquitto","broker":"192.168.0.211","port":"1883","clientid":"Sensors","usetls":false,"compatmode":true,"keepalive":"60","cleansession":true,"birthTopic":"","birthQos":"2","birthRetain":"false","birthPayload":"","closeTopic":"","closeQos":"0","closePayload":"","willTopic":"","willQos":"0","willPayload":""}]

But you where requiring help and i asked for certain things to help you and make it easy on myself. Please bear this in mind in future. I want to help but feel that others should make the help i offer easier. What you should do is create an example flow with inject inputs with corrrect data to simulate the mqtt node, or other nodes. Supplying data this will make other want to help as they do not have to construct anything to see your issue.

Any way I am glad you have got to where you want. Please do not tyake offense to my remarks I was only trying to point out how to make helpers lif easier.

1 Like

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