Table with 5 sensors data with fixed row

I have five different sensors with live measurements. I want the sensor ID to be sorted (colum A) and measure data in colum B, C, ...
With this setup, I get only one one row of data and the latest message overwrites the previous.

[{"id":"b9fcf9e5.8e1eb8","type":"ui_table","z":"161bc1d0.dbc88e","group":"1638a6ad.34a9f9","name":"EE","order":5,"width":"13","height":"3","columns":[],"outputs":0,"cts":false,"x":750,"y":360,"wires":[]},{"id":"a8b6cdb3.c73df","type":"function","z":"161bc1d0.dbc88e","name":"filter","func":"msg.payload = [\n    {\n    \"serial\": msg.payload.serialNo,\n    \"Timestamp\": new Date(msg.payload).toLocaleString(),\n    \"rssi\": msg.payload.rssi,\n    \"Manuf\": msg.payload.manufacturer,\n}\n];\nreturn msg;","outputs":1,"noerr":0,"x":350,"y":360,"wires":[["b9fcf9e5.8e1eb8"]]},{"id":"1638a6ad.34a9f9","type":"ui_group","z":"","name":"AmundTabell","tab":"7cda3603.64ae68","disp":true,"width":"13","collapse":false},{"id":"7cda3603.64ae68","type":"ui_tab","z":"","name":"Tabell_Test","icon":"dashboard","order":1,"disabled":false,"hidden":false}]

How can I have five rows (one for each sensors), sensor ID sorted colum A, and the data corresponds to the correct row and cell?

I you look at the Help tab for the node, it will show an example of multiple rows and columns.
to get the data from the five sensors you need to use a join node to combine the data.

Thanks zenofmud

I can so far live with sensordata that is not actually fixed to a row. Now the sensor data updating the table every 10 sec, thats OK right no. Later fixed rows :slight_smile: -)

But ... I use a TickCross symbol in one colomn. Now, both symbols are red color. I want the Tick to be green. I have searched the forum, but I still don't understand how to implement it.

I followded this one:
Youtube : How to create a data table in Node RED Dashboard - YouTube
... and it is working, but how to change to tick color?

Without seeing your actual flow all I can do is guess

Sorry ...
here it is:

[{"id":"6766777f.a1cc18","type":"function","z":"161bc1d0.dbc88e","name":"filter","func":"tableData2 = flow.get(\"savedData2\") || [];\n\ntableData2.unshift({\n    \"Node ID\": msg.payload.bridgeId,\n    //\"Tid\": new Date(msg.payload.timeStamp).toLocaleString(\"no-NO\"),\n    \"Tid\": Date(msg.payload.timeStamp).toLocaleString(),\n    \"RSSI\": msg.payload.rssi,\n    \"OK\": msg.payload.tickcross,\n    \"Manuf\": \"Vitir\"\n});\n\nif (tableData2.length >5) tableData2.pop();\n\nmsg.payload = tableData2;\n    \nflow.set(\"savedData2\", tableData2);\n\nreturn msg;","outputs":1,"noerr":0,"x":870,"y":80,"wires":[["d659ea4b.40b698"]]},{"id":"d659ea4b.40b698","type":"trigger","z":"161bc1d0.dbc88e","name":"","op1":"","op2":"","op1type":"pay","op2type":"payl","duration":"10","extend":false,"units":"s","reset":"","bytopic":"all","outputs":1,"x":1050,"y":80,"wires":[["4c56eee1.aeb4e"]]},{"id":"4c56eee1.aeb4e","type":"ui_table","z":"161bc1d0.dbc88e","group":"1638a6ad.34a9f9","name":"Bridges","order":2,"width":"10","height":"4","columns":[{"field":"Node ID","title":"Bridge ID","width":"","align":"left","formatter":"plaintext","formatterParams":{"target":"_blank"}},{"field":"Tid","title":"Time","width":"","align":"left","formatter":"plaintext","formatterParams":{"target":"_blank"}},{"field":"RSSI","title":"rssi","width":"","align":"center","formatter":"plaintext","formatterParams":{"target":"_blank"}},{"field":"OK","title":"Pass/Fail","width":"","align":"center","formatter":"tickCross","formatterParams":{"target":"_blank"}},{"field":"Manuf","title":"Manuf.","width":"","align":"center","formatter":"plaintext","formatterParams":{"target":"_blank"}}],"outputs":0,"cts":false,"x":1240,"y":80,"wires":[]},{"id":"9ed67d72.c0453","type":"json","z":"161bc1d0.dbc88e","name":"","property":"payload","action":"","pretty":false,"x":690,"y":80,"wires":[["6766777f.a1cc18"]]},{"id":"6b55e2f6.f9fa3c","type":"mqtt in","z":"161bc1d0.dbc88e","name":"","topic":"","qos":"2","datatype":"auto","x":500,"y":100,"wires":[["9ed67d72.c0453"]]},{"id":"1638a6ad.34a9f9","type":"ui_group","name":"Signalstyrke","tab":"7cda3603.64ae68","order":1,"disp":true,"width":"12","collapse":false},{"id":"7cda3603.64ae68","type":"ui_tab","name":"Tabell_Test","icon":"dashboard","order":1,"disabled":false,"hidden":false}]

I can't 'import' your code. Have a look at this link to see how to do it.

In order to make code readable and usable it is necessary to surround your code with three backticks (also known as a left quote or backquote ```)

``` 
   code goes here 
```

You can edit and correct your post by clicking the pencil :pencil2: icon.

See this post for more details - How to share code or flow json

1 Like

Please add a debug node (set to display the complete msg object) to the output of the mqtt-in node then copy and paste two of the outputs of the debug node so we have some test data.

There’s a great page in the docs (Working with messages : Node-RED) that will explain how to use the debug panel to find the right path to any data item.

Pay particular attention to the part about the buttons that appear under your mouse pointer when you over hover a debug message property in the sidebar.

BX00Cy7yHi

7.3.2023, 13:28:16node: 15abe96c.a49c67
mrfdatarsp : msg.payload : array[5]
array[5]
0: object
Node ID: "C6E607CE"
Tid: "Tue Mar 07 2023 12:28:16 GMT+0000 (Coordinated Universal Time)"
RSSI: -71
TickCross: 0
Manuf: "MM"
1: object
2: object
3: object
4: object

When Tickcross = 0, the symbol should be RED, Tickcross = 1 means GREEN symbol.

[{"id":"6766777f.a1cc18","type":"function","z":"161bc1d0.dbc88e","name":"filter","func":"tableData2 = flow.get(\"savedData2\") || [];\n\ntableData2.unshift({\n    \"Node ID\": msg.payload.bridgeId,\n    //\"Tid\": new Date(msg.payload.timeStamp).toLocaleString(\"no-NO\"),\n    \"Tid\": Date(msg.payload.timeStamp).toLocaleString(),\n    \"RSSI\": msg.payload.rssi,\n    \"TickCross\": msg.payload.tickcross,\n    \"Manuf\": \"MM\"\n});\n\nif (tableData2.length >5) tableData2.pop();\n\nmsg.payload = tableData2;\n    \nflow.set(\"savedData2\", tableData2);\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","x":850,"y":80,"wires":[["d659ea4b.40b698"]]},{"id":"d659ea4b.40b698","type":"trigger","z":"161bc1d0.dbc88e","name":"","op1":"","op2":"","op1type":"pay","op2type":"payl","duration":"10","extend":false,"overrideDelay":false,"units":"s","reset":"","bytopic":"all","topic":"topic","outputs":1,"x":1030,"y":60,"wires":[["4c56eee1.aeb4e"]]},{"id":"4c56eee1.aeb4e","type":"ui_table","z":"161bc1d0.dbc88e","group":"1638a6ad.34a9f9","name":"Bridges","order":2,"width":"10","height":"4","columns":[{"field":"Node ID","title":"Bridge ID","width":"","align":"left","formatter":"plaintext","formatterParams":{"target":"_blank"}},{"field":"Tid","title":"Time","width":"","align":"left","formatter":"plaintext","formatterParams":{"target":"_blank"}},{"field":"RSSI","title":"rssi","width":"","align":"center","formatter":"plaintext","formatterParams":{"target":"_blank"}},{"field":"TickCross","title":"Pass/Fail","width":"","align":"center","formatter":"tickCross","formatterParams":{"target":"_blank"}},{"field":"Manuf","title":"Manuf.","width":"","align":"center","formatter":"plaintext","formatterParams":{"target":"_blank"}}],"outputs":0,"cts":false,"x":1220,"y":60,"wires":[]},{"id":"9ed67d72.c0453","type":"json","z":"161bc1d0.dbc88e","name":"","property":"payload","action":"","pretty":false,"x":690,"y":80,"wires":[["6766777f.a1cc18"]]},{"id":"6126129a.4c810c","type":"mqtt in","z":"161bc1d0.dbc88e","name":"","topic":"","qos":"2","datatype":"auto","broker":"581bb373.5011bc","x":500,"y":80,"wires":[["9ed67d72.c0453"]]},{"id":"1638a6ad.34a9f9","type":"ui_group","name":"Signalstyrke","tab":"7cda3603.64ae68","order":1,"disp":true,"width":"12","collapse":false},{"id":"581bb373.5011bc","type":"mqtt-broker","name":"","broker":"","port":"15548","clientid":"","usetls":false,"compatmode":false,"keepalive":"60","cleansession":true,"birthTopic":"","birthQos":"0","birthPayload":"","closeTopic":"","closeQos":"0","closePayload":"","willTopic":"","willQos":"0","willPayload":""},{"id":"7cda3603.64ae68","type":"ui_tab","name":"Tabell_Test","icon":"dashboard","order":1,"disabled":false,"hidden":false}]

The flow ... continious flow of incomming json objects, feed into a table of 5 rows

Here is how I populate a dashboard table with hostname and IP address of Linux devices alive on my network. It may be applicable to your list of sensors.

[{"id":"5669fc4cac1d0dfc","type":"group","z":"90fe1578c260fc2b","name":"Save info messages to context","style":{"label":true,"stroke":"#6f2fa0","fill":"#bfdbef","fill-opacity":"0.3"},"nodes":["23155ac8fea5b99d","7d726eaf71e047d0","3daf41fd05c6ea8b","7c174eceb569e8a8","c8519e227fe92b5b"],"x":14,"y":39,"w":772,"h":122},{"id":"23155ac8fea5b99d","type":"mqtt in","z":"90fe1578c260fc2b","g":"5669fc4cac1d0dfc","name":"","topic":"info/pi/#","qos":"2","datatype":"auto-detect","broker":"6acd88c889789c5c","nl":false,"rap":true,"rh":0,"inputs":0,"x":90,"y":100,"wires":[["3daf41fd05c6ea8b"]]},{"id":"7d726eaf71e047d0","type":"function","z":"90fe1578c260fc2b","g":"5669fc4cac1d0dfc","name":"Save to context","func":"var devices = flow.get(\"devices\") ?? {}   // default is an empty object\nconst ip = msg.payload.ip      // I want my table sorted by ip address so use it as key\nconst ts = new Date()          // Timestamp message arrival\ndevices[ip] = msg.payload      // Add message to object keyed by ip\ndevices[ip].timestamp = ts     // Timesamp so I can identify stale messages\nflow.set (\"devices\", devices)  // Save the updated context \n\nmsg.payload = devices[ip]      // For debug output\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":680,"y":80,"wires":[[]]},{"id":"3daf41fd05c6ea8b","type":"function","z":"90fe1578c260fc2b","g":"5669fc4cac1d0dfc","name":"Filter bad data","func":"msg.type = typeof(msg.payload)\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":260,"y":100,"wires":[["c8519e227fe92b5b"]]},{"id":"7c174eceb569e8a8","type":"debug","z":"90fe1578c260fc2b","g":"5669fc4cac1d0dfc","name":"Bad data","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":660,"y":120,"wires":[]},{"id":"c8519e227fe92b5b","type":"switch","z":"90fe1578c260fc2b","g":"5669fc4cac1d0dfc","name":"type is Object?","property":"type","propertyType":"msg","rules":[{"t":"eq","v":"object","vt":"str"},{"t":"else"}],"checkall":"true","repair":false,"outputs":2,"x":460,"y":100,"wires":[["7d726eaf71e047d0"],["7c174eceb569e8a8"]]},{"id":"6acd88c889789c5c","type":"mqtt-broker","name":"","broker":"192.168.1.11","port":"1883","clientid":"","autoConnect":true,"usetls":false,"protocolVersion":"4","keepalive":"60","cleansession":true,"birthTopic":"broker/birth","birthQos":"2","birthPayload":"\"Connected\"","birthMsg":{},"closeTopic":"broker/close","closeQos":"2","closePayload":"\"Disconnecting\"","closeMsg":{},"willTopic":"broker/dead","willQos":"2","willPayload":"\"Disconnected\"","willMsg":{},"userProps":"","sessionExpiry":""},{"id":"75db055c564dc258","type":"group","z":"90fe1578c260fc2b","name":"Delete context variable at midnight to reset device list ","style":{"label":true,"stroke":"#6f2fa0","fill":"#dbcbe7","fill-opacity":"0.3"},"nodes":["04899783273dec1c","51c3ae80e4b0caad"],"x":14,"y":319,"w":432,"h":82},{"id":"04899783273dec1c","type":"inject","z":"90fe1578c260fc2b","g":"75db055c564dc258","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"00 00 * * *","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":130,"y":360,"wires":[["51c3ae80e4b0caad"]]},{"id":"51c3ae80e4b0caad","type":"change","z":"90fe1578c260fc2b","g":"75db055c564dc258","name":"","rules":[{"t":"delete","p":"devices","pt":"flow"}],"action":"","property":"","from":"","to":"","reg":false,"x":330,"y":360,"wires":[[]]},{"id":"733fb7b343c1f04d","type":"group","z":"90fe1578c260fc2b","name":"Construct Table","style":{"label":true,"stroke":"#92d04f","fill":"#e3f3d3","fill-opacity":"0.3"},"nodes":["222ba04e6e9a9f73","060287df849594fd","28a4d3149680d07f"],"x":14,"y":199,"w":552,"h":82},{"id":"222ba04e6e9a9f73","type":"inject","z":"90fe1578c260fc2b","g":"733fb7b343c1f04d","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"20","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":130,"y":240,"wires":[["060287df849594fd"]]},{"id":"060287df849594fd","type":"function","z":"90fe1578c260fc2b","g":"733fb7b343c1f04d","name":"Get from context","func":"function sortObj(obj) {\n    return Object.keys(obj).sort().reduce(function (result, key) {\n        result[key] = obj[key];\n        return result;\n    }, {});\n}\n\nlet devices = flow.get(\"devices\") ?? {}        // get data from context\ndevices = sortObj(devices)                     // Sort it by key (IP address)\n\nlet arr = []                                   // Will pass an array of objects to the table\nconst now = new Date().getTime()\n\nfor (let ip in devices) {                                         // Loop through sensors\n    const age = (now - devices[ip].timestamp.getTime())/1000      // Age in seconds of the sensor report - can use to id stale records\n    let rec = {                                                   // Construct object for this sensor\n        \"ip\": ip,\n        \"hostname\": devices[ip].hostname,\n        // etc, etc\n    }\n    arr.push(rec)                                                 // Add it to the array\n}\n\nmsg.payload = arr;\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":320,"y":240,"wires":[["28a4d3149680d07f"]]},{"id":"28a4d3149680d07f","type":"ui_table","z":"90fe1578c260fc2b","g":"733fb7b343c1f04d","group":"a29767e44538ea22","name":"Table ","order":0,"width":"9","height":"6","columns":[],"outputs":0,"cts":false,"x":490,"y":240,"wires":[]},{"id":"a29767e44538ea22","type":"ui_group","name":"Pies","tab":"75458084fdb19d21","order":1,"disp":false,"width":"30","collapse":false,"className":""},{"id":"75458084fdb19d21","type":"ui_tab","name":"Status","icon":"dashboard","disabled":false,"hidden":false}]

I don't use a join node, each incoming mqtt message is added/updated discretely in a context variable.
My context variable is an object keyed by IP address rather than an array, so I can sort the data by IP.

Untitled 2

ps. I don't actually know if I need to sort the context variable, maybe it will automatically come out in alphabetical order of the key?

I have a couple questions about your flow and function node. As I understand it you are

  1. receiving a json string from the mqtt-in node and you run it thru the json node to convert it to an object.
    (do you know you can set the Output option in the mqtt-in node to
    auto-detect (parsed JSON object, string or buffer) or
    a parsed JSON object
    eliminating the need for the json node)
  2. you run it thru the json node to parse the JSON string into an object.
  3. now it enters the function node
    a) you grab the flow variable 'savedData2` (an array) and add another array entry populating it with data from msg.payload. you start with
"Node ID": msg.payload.bridgeId,

issue 1 - msg.payload is an array so you would need to array occurrence like

"Node ID": msg.payload[0].bridgeId,

but bridgeId is not in msg.payload but there is a Node ID so you would have to use

"Node ID": msg.payload[0]["Node ID"]

issue 2 - you have several case issue with the RSSI and tickcross. Case is important!

clean that all up and see what you get. And add debug nodes so you can see what is going into and out of nodes, it helps in debugging.

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