@Buckskin - I got it working!! And ChatGPT (after 2 days) was finally able to simplify the code a bit.
Below is the flow in case it helps someone:
[{"id":"f8c7169724fa4baf","type":"inject","z":"3d460c91742c0584","name":"Updated Meter Data (w/payload1)","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"{\"labels\":[\"2025-07-15\",\"2025-07-16\",\"2025-07-17\",\"2025-07-18\",\"2025-07-19\",\"2025-07-20\",\"2025-07-21\",\"2025-07-22\",\"2025-07-23\",\"2025-07-24\",\"2025-07-25\",\"2025-07-26\",\"2025-07-27\",\"2025-07-28\",\"2025-07-29\",\"2025-07-30\",\"2025-07-31\",\"2025-08-01\"],\"teslaFromGrid\":[30.548,44.488,28.566,54.282,13.648,0.928,4.604,6.062,6.962,6.876,5.488,7.96,39.938,36.71,46.704,50.692,53.876,45.47],\"meterFromGrid\":[30.869,44.852,28.843,54.751,13.73,0.943,4.668,6.118,7.03,6.928,5.534,8.028,40.279,37.037,47.157,51.154,54.372,45.84],\"teslaToGrid\":[-0.11,-0.07,-0.045,-0.024,-16.551,-9.227,-4.552,-10.018,-4.244,-9.096,-2.726,-0.091,-0.063,-0.076,-0.074,-0.066,-0.057,-0.065],\"meterToGrid\":[-0.201,-0.154,-0.1,-0.068,-16.757,-9.393,-4.664,-10.154,-4.358,-9.246,-2.804,-0.162,-0.113,-0.122,-0.126,-0.083,-0.074,-0.07]}","payloadType":"json","x":384.8345642089844,"y":320.82720947265625,"wires":[["8e254e2bad333e5c"]]},{"id":"8e254e2bad333e5c","type":"ui-template","z":"3d460c91742c0584","group":"5cd7a2a79d14beb5","page":"","ui":"","name":"Clean Temlate for Testing","order":1,"width":"22","height":"10","head":"","format":"<template>\n <div class=\"base-container\">\n <div class=\"chart-container\"><canvas ref=\"meterchart0\" /></div>\n </div>\n</template>\n\n<script src=\"https://cdn.jsdelivr.net/npm/chart.js\"></script>\n\n<script>\n export default { \n data() {\n return {\n isChartLoaded: false,\n dataSets: [],\n }\n },\n\n watch: {\n msg: function () {\n if (this.isChartLoaded && this.msg.payload && this.msg.payload.labels) {\n this.onInput(this.msg.payload);\n }\n }\n },\n\n mounted() {\n let interval = setInterval(() => {\n if (window.Chart) {\n clearInterval(interval);\n this.draw();\n this.isChartLoaded = true;\n }\n }, 100);\n },\n\n methods: {\n draw() {\n const ctx = this.$refs.meterchart0;\n const chart = new Chart(ctx, {\n type: 'bar',\n data: {\n labels: [],\n datasets: [\n {\n label: 'Tesla - From Grid',\n data: [],\n backgroundColor: \"lightblue\",\n stack: 'tesla'\n },\n {\n label: 'Meter - From Grid',\n data: [],\n backgroundColor: \"red\",\n stack: 'meter'\n },\n {\n label: 'Tesla - To Grid',\n data: [],\n backgroundColor: \"orange\",\n stack: 'tesla'\n },\n {\n label: 'Meter - To Grid',\n data: [],\n backgroundColor: \"green\",\n stack: 'meter'\n }\n ]\n },\n options: {\n maintainAspectRatio: false,\n animation: false,\n responsive: true,\n plugins: {\n legend: {\n position: 'top',\n },\n title: {\n display: true,\n text: 'Tesla vs Meter - Injected data'\n }\n },\n scales: {\n x: {\n stacked: true,\n },\n y: {\n type: 'linear',\n display: true,\n position: 'left'\n }\n }\n },\n });\n\n this.chart = chart;\n },\n\n onInput(inputData) {\n if (!inputData || !Array.isArray(inputData.labels)) return;\n\n this.dataSets = this.chart.data.datasets;\n\n // Clear previous data\n this.dataSets.forEach(dataset => dataset.data = []);\n this.chart.data.labels = [];\n\n // Assign new data directly from arrays\n this.chart.data.labels = inputData.labels;\n this.dataSets[0].data = inputData.teslaFromGrid;\n this.dataSets[1].data = inputData.meterFromGrid;\n this.dataSets[2].data = inputData.teslaToGrid;\n this.dataSets[3].data = inputData.meterToGrid;\n\n this.updateChart();\n },\n\n updateChart() {\n if (this.chart) {\n this.dataSets.forEach((element, index) => {\n this.chart.data.datasets[index].data = element.data;\n });\n this.chart.update();\n }\n },\n\n clearChart() {\n if (this.dataSets.length > 0) {\n this.dataSets.forEach(element => element.data = []);\n this.updateChart();\n }\n }\n }\n }\n</script>\n\n<style>\n .base-container {\n width: 100%;\n height: 100%;\n display: flex;\n justify-content: center;\n align-items: center;\n container: chat / size;\n }\n\n .chart-container {\n position: relative;\n margin: auto;\n height: 100cqb;\n width: 100cqi;\n display: flex;\n justify-content: center;\n align-items: center;\n }\n</style>","storeOutMessages":true,"passthru":true,"resendOnRefresh":true,"templateScope":"local","className":"","x":670.6471252441406,"y":395.3216857910156,"wires":[["ea6162aff0119e33"]]},{"id":"8dc54484cd9c7e23","type":"inject","z":"3d460c91742c0584","name":"Updated Meter Data (w/payload2)","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"{\"labels\":[\"2025-07-15\",\"2025-07-16\",\"2025-07-17\",\"2025-07-18\",\"2025-07-19\",\"2025-07-20\",\"2025-07-21\",\"2025-07-22\",\"2025-07-23\",\"2025-07-24\",\"2025-07-25\",\"2025-07-26\",\"2025-07-27\",\"2025-07-28\",\"2025-07-29\",\"2025-07-30\",\"2025-07-31\",\"2025-08-01\"],\"teslaFromGrid\":[60.548,104.488,88.566,114.282,73.648,63.928,64.604,66.062,66.962,66.876,65.488,67.96,99.938,96.71,106.704,110.692,113.876,105.47],\"meterFromGrid\":[30.869,44.852,28.843,54.751,13.73,0.943,4.668,6.118,7.03,6.928,5.534,8.028,40.279,37.037,47.157,51.154,54.372,45.84],\"teslaToGrid\":[-0.11,-0.07,-0.045,-0.024,-16.551,-9.227,-4.552,-10.018,-4.244,-9.096,-2.726,-0.091,-0.063,-0.076,-0.074,-0.066,-0.057,-0.065],\"meterToGrid\":[-0.201,-0.154,-0.1,-0.068,-16.757,-9.393,-4.664,-10.154,-4.358,-9.246,-2.804,-0.162,-0.113,-0.122,-0.126,-0.083,-0.074,-0.07]}","payloadType":"json","x":379.8345642089844,"y":470.82720947265625,"wires":[["8e254e2bad333e5c"]]},{"id":"ea6162aff0119e33","type":"debug","z":"3d460c91742c0584","name":"Output debug","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":911.6471252441406,"y":396.8106384277344,"wires":[]},{"id":"5cd7a2a79d14beb5","type":"ui-group","name":"Tesla vs Meter - Testing","page":"8d56444ee1e5169e","width":"22","height":"10","order":1,"showTitle":true,"className":"","visible":"true","disabled":"false","groupType":"default"},{"id":"8d56444ee1e5169e","type":"ui-page","name":"New Page for Testing","ui":"de5759a313e7ad79","path":"/page21","icon":"home","layout":"grid","theme":"e4b50d7892c4eb32","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":22,"className":"","visible":"true","disabled":"false"},{"id":"de5759a313e7ad79","type":"ui-base","name":"Node-RED Dashboard","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":"e4b50d7892c4eb32","type":"ui-theme","name":"Default Theme","colors":{"surface":"#ffffff","primary":"#0094ce","bgPage":"#eeeeee","groupBg":"#ffffff","groupOutline":"#cccccc"},"sizes":{"pagePadding":"12px","groupGap":"12px","groupBorderRadius":"4px","widgetGap":"12px","density":"default"}}]
Below is the code for ui-template:
ui-template configuration:
<template>
<div class="base-container">
<div class="chart-container"><canvas ref="meterchart0" /></div>
</div>
</template>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script>
export default {
data() {
return {
isChartLoaded: false,
dataSets: [],
}
},
watch: {
msg: function () {
if (this.isChartLoaded && this.msg.payload && this.msg.payload.labels) {
this.onInput(this.msg.payload);
}
}
},
mounted() {
let interval = setInterval(() => {
if (window.Chart) {
clearInterval(interval);
this.draw();
this.isChartLoaded = true;
}
}, 100);
},
methods: {
draw() {
const ctx = this.$refs.meterchart0;
const chart = new Chart(ctx, {
type: 'bar',
data: {
labels: [],
datasets: [
{
label: 'Tesla - From Grid',
data: [],
backgroundColor: "lightblue",
stack: 'tesla'
},
{
label: 'Meter - From Grid',
data: [],
backgroundColor: "red",
stack: 'meter'
},
{
label: 'Tesla - To Grid',
data: [],
backgroundColor: "orange",
stack: 'tesla'
},
{
label: 'Meter - To Grid',
data: [],
backgroundColor: "green",
stack: 'meter'
}
]
},
options: {
maintainAspectRatio: false,
animation: false,
responsive: true,
plugins: {
legend: {
position: 'top',
},
title: {
display: true,
text: 'Tesla vs Meter - Injected data'
}
},
scales: {
x: {
stacked: true,
},
y: {
type: 'linear',
display: true,
position: 'left'
}
}
},
});
this.chart = chart;
},
onInput(inputData) {
if (!inputData || !Array.isArray(inputData.labels)) return;
this.dataSets = this.chart.data.datasets;
// Clear previous data
this.dataSets.forEach(dataset => dataset.data = []);
this.chart.data.labels = [];
// Assign new data directly from arrays
this.chart.data.labels = inputData.labels;
this.dataSets[0].data = inputData.teslaFromGrid;
this.dataSets[1].data = inputData.meterFromGrid;
this.dataSets[2].data = inputData.teslaToGrid;
this.dataSets[3].data = inputData.meterToGrid;
this.updateChart();
},
updateChart() {
if (this.chart) {
this.dataSets.forEach((element, index) => {
this.chart.data.datasets[index].data = element.data;
});
this.chart.update();
}
},
clearChart() {
if (this.dataSets.length > 0) {
this.dataSets.forEach(element => element.data = []);
this.updateChart();
}
}
}
}
</script>
<style>
.base-container {
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
container: chat / size;
}
.chart-container {
position: relative;
margin: auto;
height: 100cqb;
width: 100cqi;
display: flex;
justify-content: center;
align-items: center;
}
</style>
You can do almost the same in the chart node in DB2 but stacking is not an option and also, I have other charts that are a mix of line/bar charts, hence the dive into chart.js
I hope this helps someone. Again - thank you @Buckskin and @hotNipi . Could not have done this without your help