DB V2 - bar chart to track daily number of starts?

Thanks for your hints. Still getting new bars for each payload. Will keep working on this.
I might be missing something obvious.

please show us what data you are sending to the bar chart and how you have the bar chart configured.

payload I m sending in a function every time it starts as screengrab from debug:

Screengrab of current setting in chart below.

I have tried Action: replace and sGroup by: side by side
Th rest of the settings are for the persistence function quoted earlier in the thread, and it works
very well with some 20 or so line charts in use. Thanks!

ok, so not sure why you have set categoy or other key and TBH, Iam not even 100% certain what you want the bar chart to look like! A simple gauge or tile is better suited to a number - but I assume you are going to be seeing other "start counts" to this graph so wantto see side-by-side bars???

Any how. Since you are setting msg.topic to "pump drift" and msg.payload to a number, these should be reflected in the chart settings

DEMO:
chrome_J1JDikoWhm

Demo Flow (Use CTRL+I to import)

[{"id":"2ee2a2a2fc7ee074","type":"ui-chart","z":"5f26939bbac6427a","group":"2dbfd7f52fa47e21","name":"","label":"Machine Starts","order":1,"chartType":"bar","category":"topic","categoryType":"msg","xAxisLabel":"","xAxisProperty":"Item","xAxisPropertyType":"str","xAxisType":"category","xAxisFormat":"","xAxisFormatType":"auto","xmin":"","xmax":"","yAxisLabel":"","yAxisProperty":"payload","yAxisPropertyType":"msg","ymin":"","ymax":"","bins":10,"action":"append","stackSeries":false,"pointShape":"circle","pointRadius":4,"showLegend":true,"removeOlder":1,"removeOlderUnit":"3600","removeOlderPoints":"","colors":["#0095ff","#ff0000","#ff7f0e","#2ca02c","#a347e1","#d62728","#ff9896","#9467bd","#c5b0d5"],"textColor":["#666666"],"textColorDefault":true,"gridColor":["#e5e5e5"],"gridColorDefault":true,"width":6,"height":8,"className":"","interpolation":"linear","x":980,"y":2140,"wires":[[]]},{"id":"730f34909d968281","type":"inject","z":"5f26939bbac6427a","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"pump drift","payload":"1","payloadType":"num","x":700,"y":2140,"wires":[["2ee2a2a2fc7ee074"]]},{"id":"b07642db58a288f7","type":"inject","z":"5f26939bbac6427a","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"pump drift","payload":"10","payloadType":"num","x":700,"y":2180,"wires":[["2ee2a2a2fc7ee074"]]},{"id":"038e12a4d6444a8e","type":"inject","z":"5f26939bbac6427a","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"pump drift","payload":"25","payloadType":"num","x":700,"y":2220,"wires":[["2ee2a2a2fc7ee074"]]},{"id":"2b6aa492c5587370","type":"inject","z":"5f26939bbac6427a","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"waffle sprocket","payload":"7","payloadType":"num","x":720,"y":2300,"wires":[["2ee2a2a2fc7ee074"]]},{"id":"45dec71397199616","type":"inject","z":"5f26939bbac6427a","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"waffle sprocket","payload":"22","payloadType":"num","x":730,"y":2340,"wires":[["2ee2a2a2fc7ee074"]]},{"id":"50231827103ae223","type":"inject","z":"5f26939bbac6427a","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"waffle sprocket","payload":"29","payloadType":"num","x":730,"y":2380,"wires":[["2ee2a2a2fc7ee074"]]},{"id":"2dbfd7f52fa47e21","type":"ui-group","name":"Group Name","page":"8f5aa1455c92c632","width":6,"height":1,"order":1,"showTitle":true,"className":"","visible":"true","disabled":"false","groupType":"default"},{"id":"8f5aa1455c92c632","type":"ui-page","name":"Page Name","ui":"a171c8195c1b8e57","path":"/page3","icon":"home","layout":"grid","theme":"9d8bfd7e0d216779","breakpoints":[{"name":"Default","px":"0","cols":"3"},{"name":"Tablet","px":"576","cols":"6"},{"name":"Small Desktop","px":"768","cols":"9"},{"name":"Desktop","px":"1024","cols":"12"}],"order":1,"className":"","visible":"true","disabled":"false"},{"id":"a171c8195c1b8e57","type":"ui-base","name":"My Dashboard","path":"/dashboard","includeClientData":true,"acceptsClientConfig":["ui-control","ui-gauge","ui-chart","ui-form","ui-text-input","ui-file-input","ui-button","ui-button-group","ui-dropdown","ui-radio-group","ui-slider","ui-switch","ui-text","ui-table","ui-markdown","ui-notification","ui-template"],"showPathInSidebar":false,"navigationStyle":"icon","titleBarStyle":"fixed"},{"id":"9d8bfd7e0d216779","type":"ui-theme","name":"Default Theme","colors":{"surface":"#ffffff","primary":"#15617e","bgPage":"#eeeeee","groupBg":"#ffffff","groupOutline":"#cccccc"},"sizes":{"pagePadding":"12px","groupGap":"12px","groupBorderRadius":"4px","widgetGap":"12px"}},{"id":"b55042f65505932d","type":"global-config","env":[],"modules":{"@flowfuse/node-red-dashboard":"1.23.0"}}]

What happens if you change the “Action” to replace? I would expect it to create a new data point if in “Append” mode, but the chart should be completely refreshed if in “Replace” mode . Are you using the function node logic that I had sent earlier?

Thanks, this demo flow works if I skip the mentioned persistence flow and send a counter number counting up for each star, which I already also have in place.

I assume it will not start a new bar at midnight though. But that is some progress. Thanks.

I think I could either use append and send a number:1 or a count-up with replace.
It would stil need to create a new bar at midnight. somehow. Thanks.

I did post a link to the function I am using for persistence.
Here it is again:

I appreciate all input and will digest it and see if I can make some progress.

Many thanks!

Why not just send your running total and set the topic to day name e.g. Sat then at midnight reset your counter and change topic to Sun ?

You could just leave it at that as it would overwrite the days when it wraps around, or you could wipe the chart and start again.

@houser - here is a flow that seems to work. I changed the function node (ui-chart only takes the data in msg.payload) and added some additional checks.

Basic Logic:

There are 3 inject nodes - each sends in the current timestamp and a machine name (not used in this flow but could be modified to allow you to plot multiple machines on the same chart) and a count of 10.

The 3 change nodes convert the timestamp into a readable date in the format “mm-dd”. The last 2 change nodes add 1 and 2 days to the current date (to show that if the date changes, there is a new '“bar” shown on the chart).

The function node compares the incoming date and if it is different than the old date, adds a record for a new date and assigns it the incoming “count”. If it is an existing date, it adds the new count to the old one.

The data is sent in msg.payload looks like this (each date has a different count). The flow also does away with the counter node as it stores the updated count in flow variables (you can see that in the context variables panel)

[{"date":"08-17","starts":10},{"date":"08-17","starts":20},{"date":"08-18","starts":20},{"date":"08-19","starts":50}]

This is what the chart looks like:

Here is the updated flow:

[{"id":"cdcb31eb2f25ce22","type":"inject","z":"d84b176e12749a08","name":"Machine Starts Day 1","props":[{"p":"timestamp","v":"","vt":"date"},{"p":"topic","vt":"str"},{"p":"count","v":"10","vt":"num"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"Machine 1","x":400,"y":160,"wires":[["a778b74aafec4062"]]},{"id":"a778b74aafec4062","type":"change","z":"d84b176e12749a08","name":"Today's Count","rules":[{"t":"set","p":"date","pt":"msg","to":"$moment(timestamp).tz(\"America/Chicago\").format(\"MM-DD\")","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":600.1286468505859,"y":159.88603401184082,"wires":[["f93aa849d2691722"]]},{"id":"f93aa849d2691722","type":"function","z":"d84b176e12749a08","name":"Get Chart Data","func":"var tempArray = flow.get('chartdata') || [];\nvar startDate = msg.date;\nvar oldDate = flow.get('oldDate');\nvar oldCount = flow.get('oldCount') || 0;\nvar count = msg.count;\n\n// If this is the first time, initialize array\nif (tempArray.length === 0) {\n    oldDate = startDate;\n    tempArray.push({ date: startDate, starts: 0 });\n    }\n\nvar lastIndex = tempArray.length - 1;\n\nif (startDate === oldDate) {\n    count = oldCount + count;\n    flow.set(\"oldCount\", count);\n    // Update last element\n    tempArray[lastIndex].date = startDate;\n    tempArray[lastIndex].starts = count;\n} else {\n    count = count;\n    flow.set(\"oldCount\", count);\n    flow.set(\"oldDate\", startDate);\n    // Add new element\n    tempArray.push({ date: startDate, starts: count});\n}\n\n// Save to flow and msg\nflow.set(\"chartdata\", tempArray);\nmsg.payload = tempArray;\nmsg.series = startDate;\nmsg.count = count;\n\n\nreturn msg;\n","outputs":1,"timeout":"","noerr":0,"initialize":"// Code added here will be run once\n// whenever the node is started.\nvar count = flow.get('count1');","finalize":"","libs":[],"x":897.0000305175781,"y":296,"wires":[["c7d0f3708a5187da","d66e4f7c17fc116d"]]},{"id":"dd40a0c12802d59f","type":"change","z":"d84b176e12749a08","name":"Day + 1","rules":[{"t":"set","p":"date","pt":"msg","to":"$moment(timestamp).tz(\"America/Chicago\").add(1, \"days\").format(\"MM-DD\")","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":588.2573089599609,"y":223.772066116333,"wires":[["f93aa849d2691722"]]},{"id":"dcc2741547a1f307","type":"change","z":"d84b176e12749a08","name":"Day + 2","rules":[{"t":"set","p":"date","pt":"msg","to":"$moment(timestamp).tz(\"America/Chicago\").add(2, \"days\").format(\"MM-DD\")","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":591.25732421875,"y":290.7720489501953,"wires":[["f93aa849d2691722"]]},{"id":"c7d0f3708a5187da","type":"debug","z":"d84b176e12749a08","name":"Debug Chart Data","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":1110.6545295715332,"y":296.16357421875,"wires":[]},{"id":"d66e4f7c17fc116d","type":"ui-chart","z":"d84b176e12749a08","group":"fd6045295841c382","name":"Count STarts","label":"Machine Starts","order":3,"chartType":"bar","category":"Daily Machine Starts","categoryType":"str","xAxisLabel":"Date","xAxisProperty":"date","xAxisPropertyType":"property","xAxisType":"category","xAxisFormat":"","xAxisFormatType":"auto","xmin":"","xmax":"","yAxisLabel":"Starts","yAxisProperty":"starts","yAxisPropertyType":"property","ymin":"","ymax":"","bins":"","action":"replace","stackSeries":false,"pointShape":"circle","pointRadius":4,"showLegend":true,"removeOlder":1,"removeOlderUnit":"3600","removeOlderPoints":"","colors":["#0095ff","#ff0000","#ff7f0e","#2ca02c","#98df8a","#d62728","#ff9896","#9467bd","#c5b0d5"],"textColor":["#ffffff"],"textColorDefault":false,"gridColor":["#e5e5e5"],"gridColorDefault":true,"width":6,"height":8,"className":"","interpolation":"linear","x":906.1286468505859,"y":368.8860034942627,"wires":[[]]},{"id":"407676baf1fbb4af","type":"inject","z":"d84b176e12749a08","name":"machine starts (Day 2)","props":[{"p":"timestamp","v":"","vt":"date"},{"p":"topic","vt":"str"},{"p":"count","v":"10","vt":"num"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"Machine 1","x":396.12864685058594,"y":222.88603401184082,"wires":[["dd40a0c12802d59f"]]},{"id":"d383e08133f2cd78","type":"inject","z":"d84b176e12749a08","name":"machine starts (Day 3)","props":[{"p":"timestamp","v":"","vt":"date"},{"p":"topic","vt":"str"},{"p":"count","v":"10","vt":"num"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"Machine 1","x":399.128662109375,"y":289.8860168457031,"wires":[["dcc2741547a1f307"]]},{"id":"1fc05c88906e8a89","type":"change","z":"d84b176e12749a08","name":"Delete Flow Variables","rules":[{"t":"delete","p":"oldCount","pt":"flow"},{"t":"delete","p":"oldDate","pt":"flow"},{"t":"delete","p":"chartdata","pt":"flow"}],"action":"","property":"","from":"","to":"","reg":false,"x":608.6580657958984,"y":433.1378173828125,"wires":[["d66e4f7c17fc116d"]]},{"id":"dcd1f46bcbe83c0f","type":"inject","z":"d84b176e12749a08","name":"Clear Data","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"[]","payloadType":"json","x":384.71690368652344,"y":432.4154357910156,"wires":[["1fc05c88906e8a89"]]},{"id":"fd6045295841c382","type":"ui-group","name":"db2 bar wip share","page":"ccc312f7c9d7a917","width":"30","height":1,"order":1,"showTitle":true,"className":"","visible":"true","disabled":"false","groupType":"default"},{"id":"ccc312f7c9d7a917","type":"ui-page","name":"db2 bar wip share","ui":"de5759a313e7ad79","path":"/page26","icon":" ","layout":"grid","theme":"3a38f453283ba387","breakpoints":[{"name":"Default","px":"0","cols":"9"},{"name":"Tablet","px":"576","cols":"9"},{"name":"Small Desktop","px":"768","cols":"9"},{"name":"Desktop","px":"1024","cols":"12"}],"order":22,"className":"","visible":"true","disabled":"false"},{"id":"de5759a313e7ad79","type":"ui-base","name":"Node-RED Dashboard DB2","path":"/dashboard","appIcon":"","includeClientData":false,"acceptsClientConfig":["ui-notification","ui-control"],"showPathInSidebar":false,"headerContent":"dashboard","navigationStyle":"fixed","titleBarStyle":"default","showReconnectNotification":true,"notificationDisplayTime":5,"showDisconnectNotification":true,"allowInstall":true},{"id":"3a38f453283ba387","type":"ui-theme","name":"dark jtm","colors":{"surface":"#000000","primary":"#919191","bgPage":"#000000","groupBg":"#000000","groupOutline":"#000000"},"sizes":
{"pagePadding":"1px","groupGap":"1px","groupBorderRadius":"1px","widgetGap":"1px","density":"comfortable"}}]

Hope this helps. I have left out your “persistence” sub-flow but you could connect that to the output of the function node.

1 Like

Many thanks @rakgupta
Will try this asap!

I got curious how this would work so decided to try it out. It was beyond my javascript capabilities but with ChatGPT’s help it worked! Putting it out here in case you need it

[{"id":"cdcb31eb2f25ce22","type":"inject","z":"d84b176e12749a08","name":"Machine 1 Day 1","props":[{"p":"timestamp","v":"","vt":"date"},{"p":"topic","vt":"str"},{"p":"count","v":"10","vt":"num"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"Machine 1","x":360.8345642089844,"y":120.00000286102295,"wires":[["a778b74aafec4062"]]},{"id":"a778b74aafec4062","type":"change","z":"d84b176e12749a08","name":"Day 1","rules":[{"t":"set","p":"date","pt":"msg","to":"$moment(timestamp).tz(\"America/Chicago\").format(\"MM-DD\")","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":574.1286926269531,"y":163.88603496551514,"wires":[["f93aa849d2691722","fbac3545753cd5a2"]]},{"id":"d0cd9e4acb65a21f","type":"inject","z":"d84b176e12749a08","name":"Machine 2 Day 1","props":[{"p":"timestamp","v":"","vt":"date"},{"p":"topic","vt":"str"},{"p":"count","v":"10","vt":"num"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"Machine 2","x":360.8345642089844,"y":160.94485759735107,"wires":[["a778b74aafec4062"]]},{"id":"525c671130d800e0","type":"inject","z":"d84b176e12749a08","name":"Machine 3 Day 1","props":[{"p":"timestamp","v":"","vt":"date"},{"p":"topic","vt":"str"},{"p":"count","v":"10","vt":"num"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"Machine 3","x":360.8345642089844,"y":206.94485759735107,"wires":[["a778b74aafec4062"]]},{"id":"f93aa849d2691722","type":"function","z":"d84b176e12749a08","name":"Get Chart Data","func":"// --- Retrieve or initialize stored data ---\nlet chartData = flow.get('chartdata') || [];\n\n// Optional reset\nif (msg.reset || msg.topic === 'reset') {\n    chartData = [];\n}\n\n// Extract incoming message\nconst date = String(msg.date);\nconst machine = String(msg.topic);  // machine name\nconst count = Number(msg.count) || 0;\n\n// --- Update or insert record ---\nlet found = false;\nfor (let row of chartData) {\n    if (row.date === date && row.machine === machine) {\n        row.starts += count;  // accumulate count\n        found = true;\n        break;\n    }\n}\n\nif (!found) {\n    chartData.push({ date: date, machine: machine, starts: count });\n}\n\n// Save updated data in flow\nflow.set('chartdata', chartData);\n\n// --- Output flat array for Dashboard 2 ---\nmsg.payload = chartData;\n\n// Optional debug\nmsg.chartData = chartData;\n\nreturn msg;\n","outputs":1,"timeout":"","noerr":0,"initialize":"// Code added here will be run once\n// whenever the node is started.\nvar count = flow.get('count1');","finalize":"","libs":[],"x":808.0000915527344,"y":291.00000286102295,"wires":[["c7d0f3708a5187da","d66e4f7c17fc116d"]]},{"id":"fbac3545753cd5a2","type":"debug","z":"d84b176e12749a08","name":"Day 1 debug","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":762.6543884277344,"y":144.1635972910156,"wires":[]},{"id":"dd40a0c12802d59f","type":"change","z":"d84b176e12749a08","name":"Day 2","rules":[{"t":"set","p":"date","pt":"msg","to":"$moment(timestamp).tz(\"America/Chicago\").add(1, \"days\").format(\"MM-DD\")","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":574.1286926269531,"y":296.7720670700073,"wires":[["f93aa849d2691722"]]},{"id":"dcc2741547a1f307","type":"change","z":"d84b176e12749a08","name":"Day 3","rules":[{"t":"set","p":"date","pt":"msg","to":"$moment(timestamp).tz(\"America/Chicago\").add(2, \"days\").format(\"MM-DD\")","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":574.1286926269531,"y":435.77204990386963,"wires":[["f93aa849d2691722"]]},{"id":"c7d0f3708a5187da","type":"debug","z":"d84b176e12749a08","name":"Debug Chart Data","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":865.6545715332031,"y":235.16357707977295,"wires":[]},{"id":"d66e4f7c17fc116d","type":"ui-chart","z":"d84b176e12749a08","group":"fd6045295841c382","name":"Count STarts","label":"Machine Starts","order":3,"chartType":"bar","category":"machine","categoryType":"property","xAxisLabel":"Date","xAxisProperty":"date","xAxisPropertyType":"property","xAxisType":"category","xAxisFormat":"","xAxisFormatType":"auto","xmin":"","xmax":"","yAxisLabel":"Starts","yAxisProperty":"starts","yAxisPropertyType":"property","ymin":"","ymax":"","bins":"","action":"replace","stackSeries":false,"pointShape":"circle","pointRadius":4,"showLegend":true,"removeOlder":1,"removeOlderUnit":"3600","removeOlderPoints":"","colors":["#0095ff","#ff0000","#ff7f0e","#2ca02c","#98df8a","#d62728","#ff9896","#9467bd","#c5b0d5"],"textColor":["#ffffff"],"textColorDefault":false,"gridColor":["#e5e5e5"],"gridColorDefault":true,"width":6,"height":8,"className":"","interpolation":"linear","x":1096.1286926269531,"y":291.8860197067261,"wires":[[]]},{"id":"407676baf1fbb4af","type":"inject","z":"d84b176e12749a08","name":"Machine 1 (Day 2)","props":[{"p":"timestamp","v":"","vt":"date"},{"p":"topic","vt":"str"},{"p":"count","v":"10","vt":"num"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"Machine 1","x":370.8345642089844,"y":257.88605308532715,"wires":[["dd40a0c12802d59f"]]},{"id":"a2fe8cb06e81a09b","type":"inject","z":"d84b176e12749a08","name":"Machine 2 (Day 2)","props":[{"p":"timestamp","v":"","vt":"date"},{"p":"topic","vt":"str"},{"p":"count","v":"10","vt":"num"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"Machine 2","x":370.8345642089844,"y":295.9448575973511,"wires":[["dd40a0c12802d59f"]]},{"id":"92e078556e16bb49","type":"inject","z":"d84b176e12749a08","name":"Machine 3 (Day 2)","props":[{"p":"timestamp","v":"","vt":"date"},{"p":"topic","vt":"str"},{"p":"count","v":"10","vt":"num"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"Machine 2","x":370.8345642089844,"y":331.9448575973511,"wires":[["dd40a0c12802d59f"]]},{"id":"e027ea0db2f5ae23","type":"inject","z":"d84b176e12749a08","name":"Machine 1 (Day 3)","props":[{"p":"timestamp","v":"","vt":"date"},{"p":"topic","vt":"str"},{"p":"count","v":"10","vt":"num"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"Machine 1","x":370.8345642089844,"y":392.9448575973511,"wires":[["dcc2741547a1f307"]]},{"id":"dfe39f75f1fddffe","type":"inject","z":"d84b176e12749a08","name":"Machine 2 (Day 3)","props":[{"p":"timestamp","v":"","vt":"date"},{"p":"topic","vt":"str"},{"p":"count","v":"10","vt":"num"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"Machine 2","x":370.8345642089844,"y":434.00366592407227,"wires":[["dcc2741547a1f307"]]},{"id":"8fb8c001a9735507","type":"inject","z":"d84b176e12749a08","name":"Machine 3 (Day 3)","props":[{"p":"timestamp","v":"","vt":"date"},{"p":"topic","vt":"str"},{"p":"count","v":"10","vt":"num"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"Machine 2","x":370.8345642089844,"y":476.00366497039795,"wires":[["dcc2741547a1f307"]]},{"id":"1fc05c88906e8a89","type":"change","z":"d84b176e12749a08","name":"Delete Flow Variables","rules":[{"t":"delete","p":"oldCount","pt":"flow"},{"t":"delete","p":"oldDate","pt":"flow"},{"t":"delete","p":"chartdata","pt":"flow"}],"action":"","property":"","from":"","to":"","reg":false,"x":956.6582336425781,"y":434.13782024383545,"wires":[["d66e4f7c17fc116d"]]},{"id":"dcd1f46bcbe83c0f","type":"inject","z":"d84b176e12749a08","name":"Clear Data","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"[]","payloadType":"json","x":759.7170104980469,"y":434.4154386520386,"wires":[["1fc05c88906e8a89"]]},{"id":"fd6045295841c382","type":"ui-group","name":"db2 bar wip share","page":"ccc312f7c9d7a917","width":"30","height":1,"order":1,"showTitle":true,"className":"","visible":"true","disabled":"false","groupType":"default"},{"id":"ccc312f7c9d7a917","type":"ui-page","name":"db2 bar wip share","ui":"de5759a313e7ad79","path":"/page26","icon":" ","layout":"grid","theme":"3a38f453283ba387","breakpoints":[{"name":"Default","px":"0","cols":"9"},{"name":"Tablet","px":"576","cols":"9"},{"name":"Small Desktop","px":"768","cols":"9"},{"name":"Desktop","px":"1024","cols":"12"}],"order":22,"className":"","visible":"true","disabled":"false"},{"id":"de5759a313e7ad79","type":"ui-base","name":"Node-RED Dashboard DB2","path":"/dashboard","appIcon":"","includeClientData":false,"acceptsClientConfig":["ui-notification","ui-control"],"showPathInSidebar":false,"headerContent":"dashboard","navigationStyle":"fixed","titleBarStyle":"default","showReconnectNotification":true,"notificationDisplayTime":5,"showDisconnectNotification":true,"allowInstall":true},{"id":"3a38f453283ba387","type":"ui-theme","name":"dark jtm","colors":{"surface":"#000000","primary":"#919191","bgPage":"#000000","groupBg":"#000000","groupOutline":"#000000"},"sizes":{"pagePadding":"1px","groupGap":"1px","groupBorderRadius":"1px","widgetGap":"1px","density":"comfortable"}}]

This is what the chart looks like:

1 Like

Thanks @rakgupta
Not sure I get why you have day 1, day 2 and day 3 as inputs/ inject nodes?
They also all have the same logic?

I was under the impression that the start payloads would go into the one and same function node
and would build the chart structure per day from there.
If you have a sec to explain as I might be missing something in your thinking?

Yes - that was just to generate the sample data and show that if the incoming date is a different day, there will be a separate bar. All inject nodes only send in the current timestamp. In the Day 1 change node, the timestamp is converted into a human readable output using a jsonata expression ($moment(timestamp).tz("America/Chicago").format("MM-DD")The incoming timestamp is in msg.timestamp and is converted into my local time zone (America/Chicago) and the format is “MM-DD”). In the Day 2 and Day 3 change node, the expression adds +1 or +2 to the current date ($moment(timestamp).tz("America/Chicago").add(1, "days").format("MM-DD")thereby generating a new date and a new “bar”.

I would expect that you would only send in one msg.something and connect it directly to the function node or connect it to the first change node (if you want to do the date translation). I am not sure what the incoming date format is, so you would need to either modify the change node expression (or just leave it as is and $moment should be able to translate it as long as it is a valid date format). You would also need to adjust your timezone settings in here (or you could leave out the change node entirely and send in the appropriate date). When the date changes in the incoming data (at midnight), the function node would automatically create a new bar.

1 Like

I see. You are very kind to explain it so well.
So for your flow to work 100%, we need to create a function that sends the same
as the inject nodes in your example flow each time a machine starts?
When or if you have time, could you show us such a function that goes into the
change node that sets the moment timezone and date format for the function?
Basically the image of your inject node below I guess, or should it be different?
I would assume the count should be a one for each start for the summing to be correct?
In my example flow I had a 10 for other flow reasons so please ignore that.

To use the function node as is, you would need to send in two values. msg.date = machine start date, and msg.count = 1 (to add to the number of starts for the day). As long as you format the date as you would want it to appear on the chart (e.g. 2025-08-17 or 8/17/2025 or 17/8/2025 or Aug-17 etc.) you can just send in those two things for the chart to work. When the date changes, it will create a new bar.

If you are getting the date from an external system and you need to format it, then you could use the change node to transform the date into a format that you need to show in the chart (or you could add javascript to the existing function node to change the date).

If you decide to use the change node, please note that moment.js (the javascript library used) is no longer in development but still works with Node-RED. I don’t know what the thinking is for long term support from the Node-RED team but there are alternate ways to do date formatting (native javascript in a function node, There is also an “contrib” node (node-red-contrib-dayjs (node) - Node-RED) that can be used to do date translations as well.

Hope this helps. Let me know if you need any other clarifications.

1 Like

This seems to work perfectly as expected.
The only thing is that your latest version appears to not be compatible with my subflow for persistence. Your previous version worked with it.
So not your issue, but would appreciate a comment if/when you might have a look at it?
This is great, thanks again!

I’m not sure what you were sending into the persistence subflow before, but it looks like the sub flow doesn’t handle arrays? Unless you need multiple machines on the same chart, just use the first example flow.

Take a look at the msg.payload that is the input into ui-chart in both flows. I think your function node in the subflow is doing some of the work that is already done and is output in an array format.

Thanks.
The persistence subflow simply accepts whatever you would be sending to the chart.
It stores all of it and then loads it at a reboot and is working as-is.

I am now basically using your latest version so have deleted my work in progress from this thread and may upload a new version when it is a little more polished but not much need for my tweaks here at this point.
The missing output was for V1 charts which I am no longer using. Will delete it to avoid confusion.

1 Like

Thanks for all this @rakgupta
I have spent a lot of time with this integrating it into one of my installs and it seems to work great.

Two simple questions if you have a minute?

  1. If we connect a debug node to your example inject nodes, the payloads from all of them comes out as "undefined". Is that intentional? Should it not be an empty payload?
    I have part of your flow in example below but would appreciate a comment on how I inject the payload from my flow, in green grouped
[
    {
        "id": "756d5428a028502a",
        "type": "inject",
        "z": "9fe2d559c38a9dbf",
        "name": "Machine 1 Day 1",
        "props": [
            {
                "p": "timestamp",
                "v": "",
                "vt": "date"
            },
            {
                "p": "topic",
                "vt": "str"
            },
            {
                "p": "count",
                "v": "1",
                "vt": "num"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "Machine 1",
        "x": 820,
        "y": 220,
        "wires": [
            [
                "5325fc667de19560"
            ]
        ]
    },
    {
        "id": "5325fc667de19560",
        "type": "change",
        "z": "9fe2d559c38a9dbf",
        "name": "swe tz",
        "rules": [
            {
                "t": "set",
                "p": "date",
                "pt": "msg",
                "to": "$moment(timestamp).tz(\"Europe/Stockholm\").format(\"DD/MM\")",
                "tot": "jsonata"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 790,
        "y": 260,
        "wires": [
            [
                "3e0a509bda662d6b",
                "33e478b24bd555ac"
            ]
        ]
    },
    {
        "id": "3e0a509bda662d6b",
        "type": "function",
        "z": "9fe2d559c38a9dbf",
        "name": "Get Chart Data",
        "func": "// --- Retrieve or initialize stored data ---\nlet chartData = flow.get('chartdata') || [];\n\n// Optional reset\nif (msg.reset || msg.topic === 'reset') {\n    chartData = [];\n}\n\n// Extract incoming message\nconst date = String(msg.date);\nconst machine = String(msg.topic);  // machine name\nconst count = Number(msg.count) || 0;\n\n// --- Update or insert record ---\nlet found = false;\nfor (let row of chartData) {\n    if (row.date === date && row.machine === machine) {\n        row.starts += count;  // accumulate count\n        found = true;\n        break;\n    }\n}\n\nif (!found) {\n    chartData.push({ date: date, machine: machine, starts: count });\n}\n\n// Save updated data in flow\nflow.set('chartdata', chartData);\n\n// --- Output flat array for Dashboard 2 ---\nmsg.payload = chartData;\n\n// Optional debug\nmsg.chartData = chartData;\n\nreturn msg;\n",
        "outputs": 1,
        "timeout": "",
        "noerr": 0,
        "initialize": "// Code added here will be run once\n// whenever the node is started.\nvar count = flow.get('count1');",
        "finalize": "",
        "libs": [],
        "x": 1060,
        "y": 260,
        "wires": [
            [
                "54561adbaa113fe4",
                "a256c00fdcf4d1cb"
            ]
        ]
    },
    {
        "id": "54561adbaa113fe4",
        "type": "ui-chart",
        "z": "9fe2d559c38a9dbf",
        "group": "4b35283f553328ba",
        "name": "bar chart",
        "label": "statis",
        "order": 5,
        "chartType": "bar",
        "category": "machine",
        "categoryType": "property",
        "xAxisLabel": "date",
        "xAxisProperty": "date",
        "xAxisPropertyType": "property",
        "xAxisType": "category",
        "xAxisFormat": "",
        "xAxisFormatType": "auto",
        "xmin": "",
        "xmax": "",
        "yAxisLabel": "starts",
        "yAxisProperty": "starts",
        "yAxisPropertyType": "property",
        "ymin": "",
        "ymax": "",
        "bins": "",
        "action": "replace",
        "stackSeries": false,
        "pointShape": "circle",
        "pointRadius": 4,
        "showLegend": true,
        "removeOlder": 1,
        "removeOlderUnit": "3600",
        "removeOlderPoints": "",
        "colors": [
            "#0095ff",
            "#ff0000",
            "#ff7f0e",
            "#2ca02c",
            "#98df8a",
            "#d62728",
            "#ff9896",
            "#9467bd",
            "#c5b0d5"
        ],
        "textColor": [
            "#ffffff"
        ],
        "textColorDefault": false,
        "gridColor": [
            "#e5e5e5"
        ],
        "gridColorDefault": true,
        "width": "30",
        "height": "7",
        "className": "",
        "interpolation": "linear",
        "x": 1040,
        "y": 220,
        "wires": [
            []
        ]
    },
    {
        "id": "33e478b24bd555ac",
        "type": "debug",
        "z": "9fe2d559c38a9dbf",
        "name": "debug 2",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": true,
        "complete": "payload",
        "targetType": "msg",
        "statusVal": "payload",
        "statusType": "auto",
        "x": 1000,
        "y": 320,
        "wires": []
    },
    {
        "id": "a256c00fdcf4d1cb",
        "type": "debug",
        "z": "9fe2d559c38a9dbf",
        "name": "debug 3",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": true,
        "complete": "payload",
        "targetType": "msg",
        "statusVal": "payload",
        "statusType": "auto",
        "x": 1300,
        "y": 320,
        "wires": []
    },
    {
        "id": "7af65ca51ad7ac18",
        "type": "change",
        "z": "9fe2d559c38a9dbf",
        "name": "Delete Flow Variables",
        "rules": [
            {
                "t": "delete",
                "p": "oldCount",
                "pt": "flow"
            },
            {
                "t": "delete",
                "p": "oldDate",
                "pt": "flow"
            },
            {
                "t": "delete",
                "p": "chartdata",
                "pt": "flow"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 1080,
        "y": 180,
        "wires": [
            [
                "54561adbaa113fe4"
            ]
        ]
    },
    {
        "id": "740fdd9c14d49892",
        "type": "inject",
        "z": "9fe2d559c38a9dbf",
        "name": "Clear Data",
        "props": [
            {
                "p": "payload"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "[]",
        "payloadType": "json",
        "x": 1040,
        "y": 140,
        "wires": [
            [
                "7af65ca51ad7ac18"
            ]
        ]
    },
    {
        "id": "cdbc9c5993dbb52d",
        "type": "group",
        "z": "9fe2d559c38a9dbf",
        "name": "",
        "style": {
            "fill": "#c8e7a7",
            "label": true
        },
        "nodes": [
            "db84da884d2b1288",
            "41d2ebca61dafa36",
            "9feb399a4e39cfd1",
            "9a7786a9c5146b4f",
            "7b8a0dc8fb957c92"
        ],
        "x": 134,
        "y": 179,
        "w": 512,
        "h": 162
    },
    {
        "id": "db84da884d2b1288",
        "type": "ui-button",
        "z": "9fe2d559c38a9dbf",
        "g": "cdbc9c5993dbb52d",
        "group": "4b35283f553328ba",
        "name": "timestamp only",
        "label": "timestamp only",
        "order": 0,
        "width": 0,
        "height": 0,
        "emulateClick": true,
        "tooltip": "",
        "color": "",
        "bgcolor": "",
        "className": "",
        "icon": "",
        "iconPosition": "left",
        "payload": "",
        "payloadType": "str",
        "topic": "topic",
        "topicType": "msg",
        "buttonColor": "",
        "textColor": "",
        "iconColor": "",
        "enableClick": true,
        "enablePointerdown": false,
        "pointerdownPayload": "",
        "pointerdownPayloadType": "date",
        "enablePointerup": false,
        "pointerupPayload": "",
        "pointerupPayloadType": "str",
        "x": 260,
        "y": 260,
        "wires": [
            [
                "41d2ebca61dafa36"
            ]
        ]
    },
    {
        "id": "41d2ebca61dafa36",
        "type": "change",
        "z": "9fe2d559c38a9dbf",
        "g": "cdbc9c5993dbb52d",
        "name": "set all",
        "rules": [
            {
                "t": "set",
                "p": "topic",
                "pt": "msg",
                "to": "Machine 1",
                "tot": "str"
            },
            {
                "t": "set",
                "p": "count",
                "pt": "msg",
                "to": "1",
                "tot": "num"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 470,
        "y": 260,
        "wires": [
            [
                "5325fc667de19560",
                "7b8a0dc8fb957c92"
            ]
        ]
    },
    {
        "id": "9feb399a4e39cfd1",
        "type": "inject",
        "z": "9fe2d559c38a9dbf",
        "g": "cdbc9c5993dbb52d",
        "name": "inject empty payload",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "",
        "payloadType": "str",
        "x": 520,
        "y": 220,
        "wires": [
            [
                "41d2ebca61dafa36"
            ]
        ]
    },
    {
        "id": "9a7786a9c5146b4f",
        "type": "inject",
        "z": "9fe2d559c38a9dbf",
        "g": "cdbc9c5993dbb52d",
        "name": "inject any payload",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "",
        "payloadType": "str",
        "x": 270,
        "y": 220,
        "wires": [
            [
                "db84da884d2b1288"
            ]
        ]
    },
    {
        "id": "7b8a0dc8fb957c92",
        "type": "debug",
        "z": "9fe2d559c38a9dbf",
        "g": "cdbc9c5993dbb52d",
        "name": "debug 1",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": true,
        "complete": "payload",
        "targetType": "msg",
        "statusVal": "payload",
        "statusType": "auto",
        "x": 480,
        "y": 300,
        "wires": []
    },
    {
        "id": "4b35283f553328ba",
        "type": "ui-group",
        "name": "dev for discourse 2",
        "page": "9ca25813509297b7",
        "width": 6,
        "height": 1,
        "order": -1,
        "showTitle": true,
        "className": "",
        "visible": "true",
        "disabled": "false",
        "groupType": "default"
    },
    {
        "id": "9ca25813509297b7",
        "type": "ui-page",
        "name": "dev for discourse  page 2",
        "ui": "388b8b8310e87685",
        "path": "/page25",
        "icon": "home",
        "layout": "grid",
        "theme": "3a38f453283ba387",
        "breakpoints": [
            {
                "name": "Default",
                "px": "0",
                "cols": "9"
            },
            {
                "name": "Tablet",
                "px": "576",
                "cols": "9"
            },
            {
                "name": "Small Desktop",
                "px": "768",
                "cols": "9"
            },
            {
                "name": "Desktop",
                "px": "1024",
                "cols": "12"
            }
        ],
        "order": -1,
        "className": "",
        "visible": "true",
        "disabled": "false"
    },
    {
        "id": "388b8b8310e87685",
        "type": "ui-base",
        "name": " /",
        "path": "/dashboard",
        "appIcon": "",
        "includeClientData": true,
        "acceptsClientConfig": [
            "ui-notification",
            "ui-control"
        ],
        "showPathInSidebar": false,
        "headerContent": "none",
        "navigationStyle": "default",
        "titleBarStyle": "default",
        "showReconnectNotification": false,
        "notificationDisplayTime": 5,
        "showDisconnectNotification": false,
        "allowInstall": false
    },
    {
        "id": "3a38f453283ba387",
        "type": "ui-theme",
        "name": "dark jtm",
        "colors": {
            "surface": "#000000",
            "primary": "#919191",
            "bgPage": "#000000",
            "groupBg": "#000000",
            "groupOutline": "#000000"
        },
        "sizes": {
            "pagePadding": "1px",
            "groupGap": "1px",
            "groupBorderRadius": "1px",
            "widgetGap": "1px",
            "density": "comfortable"
        }
    },
    {
        "id": "7dcc8827a342e7be",
        "type": "global-config",
        "env": [],
        "modules": {
            "@flowfuse/node-red-dashboard": "1.26.0"
        }
    }
]
  1. Unless I am mistaken, it looks like this chart flow is now also persistent over restarts, which is great. It updates in GUI after first new payload after a restart.
    ! have some time ago edited my settings.js file and set "Enable Persistent Context Store" to local as code below.
    Does this mean that this is also being used by your flow now?
    Grateful for any comments.
    contextStorage: {
        default: "file",
        memoryOnly: { module: 'memory' },
        file: { module: 'localfilesystem' }
    },