Hi there,
I am trying to save chart settings to Json file
and calling that chart setting also payload data to show in chart
below is flow
[
{
"id": "026d68caf9ab771a",
"type": "group",
"z": "9f7b1754540bc18d",
"name": "Example",
"style": {
"label": true
},
"nodes": [
"302c0dc43009022d",
"5d705ad581f69853",
"028b2478f42b1e48",
"cc53d48af51b0bbd",
"b95cb7a5cad37d3d",
"332f43786d0c6d61",
"f4847914f639d384",
"66ad913ee70fac20",
"1697c436e0f389d1",
"1bb337592fa7ba62",
"8ade43c9e768f5dc",
"19b4b1d6fb485513",
"94e67937ea9fe050",
"54f124111665f03c",
"b4dc095894adb6d4"
],
"x": 74,
"y": 599,
"w": 572,
"h": 382
},
{
"id": "302c0dc43009022d",
"type": "uibuilder",
"z": "9f7b1754540bc18d",
"g": "026d68caf9ab771a",
"name": "",
"topic": "",
"url": "chart",
"okToGo": true,
"fwdInMessages": true,
"allowScripts": false,
"allowStyles": false,
"copyIndex": true,
"templateFolder": "blank",
"extTemplate": "",
"showfolder": false,
"reload": true,
"sourceFolder": "src",
"deployedVersion": "7.0.4",
"showMsgUib": false,
"title": "",
"descr": "",
"editurl": "vscode://vscode-remote/ssh-remote+192.170.26.29/home/pi/.node-red/uibuilder/vue/?windowId=_blank",
"x": 220,
"y": 760,
"wires": [
[
"5d705ad581f69853",
"66ad913ee70fac20"
],
[]
]
},
{
"id": "5d705ad581f69853",
"type": "switch",
"z": "9f7b1754540bc18d",
"g": "026d68caf9ab771a",
"name": "Switch on topic",
"property": "topic",
"propertyType": "msg",
"rules": [
{
"t": "eq",
"v": "loadSettings",
"vt": "str"
},
{
"t": "eq",
"v": "data",
"vt": "str"
}
],
"checkall": "false",
"repair": false,
"outputs": 2,
"x": 200,
"y": 820,
"wires": [
[
"028b2478f42b1e48"
],
[
"332f43786d0c6d61"
]
]
},
{
"id": "028b2478f42b1e48",
"type": "file in",
"z": "9f7b1754540bc18d",
"g": "026d68caf9ab771a",
"name": "Read Config File",
"filename": "/home/pi/data/chart_configurations.json",
"filenameType": "str",
"format": "utf8",
"encoding": "none",
"x": 450,
"y": 780,
"wires": [
[
"cc53d48af51b0bbd"
]
]
},
{
"id": "cc53d48af51b0bbd",
"type": "json",
"z": "9f7b1754540bc18d",
"g": "026d68caf9ab771a",
"name": "Parse JSON",
"property": "payload",
"action": "",
"pretty": false,
"x": 450,
"y": 840,
"wires": [
[
"b95cb7a5cad37d3d"
]
]
},
{
"id": "b95cb7a5cad37d3d",
"type": "change",
"z": "9f7b1754540bc18d",
"g": "026d68caf9ab771a",
"name": "Set topic to loadSettings",
"rules": [
{
"t": "set",
"p": "topic",
"pt": "msg",
"to": "loadSettings",
"tot": "str"
}
],
"x": 450,
"y": 900,
"wires": [
[
"1bb337592fa7ba62"
]
]
},
{
"id": "332f43786d0c6d61",
"type": "function",
"z": "9f7b1754540bc18d",
"g": "026d68caf9ab771a",
"name": "Generate Data",
"func": "msg.payload = {\n \"Front Temp\": Math.floor(Math.random() * 500),\n \"Middle Temp\": Math.floor(Math.random() * 500),\n \"Trial Temp\": Math.floor(Math.random() * 500),\n \"Die Temp\": Math.floor(Math.random() * 500),\n \"Back Temp\": Math.floor(Math.random() * 500)\n};\nreturn msg;",
"outputs": 1,
"noerr": 0,
"x": 180,
"y": 880,
"wires": [
[
"f4847914f639d384"
]
]
},
{
"id": "f4847914f639d384",
"type": "change",
"z": "9f7b1754540bc18d",
"g": "026d68caf9ab771a",
"name": "Set topic to data",
"rules": [
{
"t": "set",
"p": "topic",
"pt": "msg",
"to": "data",
"tot": "str"
}
],
"x": 200,
"y": 940,
"wires": [
[
"1bb337592fa7ba62"
]
]
},
{
"id": "66ad913ee70fac20",
"type": "debug",
"z": "9f7b1754540bc18d",
"g": "026d68caf9ab771a",
"name": "debug 10",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "false",
"statusVal": "",
"statusType": "auto",
"x": 480,
"y": 720,
"wires": []
},
{
"id": "1697c436e0f389d1",
"type": "link in",
"z": "9f7b1754540bc18d",
"g": "026d68caf9ab771a",
"name": "link in 1",
"links": [
"1bb337592fa7ba62"
],
"x": 115,
"y": 760,
"wires": [
[
"302c0dc43009022d"
]
]
},
{
"id": "1bb337592fa7ba62",
"type": "link out",
"z": "9f7b1754540bc18d",
"g": "026d68caf9ab771a",
"name": "link out 1",
"mode": "link",
"links": [
"1697c436e0f389d1"
],
"x": 605,
"y": 940,
"wires": []
},
{
"id": "8ade43c9e768f5dc",
"type": "comment",
"z": "9f7b1754540bc18d",
"g": "026d68caf9ab771a",
"name": "index.html",
"info": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>Temperature Charts</title>\n <link href=\"https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css\" rel=\"stylesheet\">\n <link href=\"./index.css\" rel=\"stylesheet\">\n <script src=\"https://cdn.jsdelivr.net/npm/chart.js\"></script> <!-- Include Chart.js -->\n <script defer src=\"../uibuilder/uibuilder.iife.min.js\"></script>\n <script defer src=\"./index.js\"></script>\n</head>\n\n<body>\n <nav class=\"navbar navbar-expand-lg navbar-dark bg-dark\">\n <a class=\"navbar-brand\" href=\"#\">Temperature Charts Dashboard</a>\n </nav>\n\n <div class=\"container mt-4\">\n <div id=\"gauges-container\"></div>\n <div id=\"charts-container\" class=\"row\">\n <!-- Dynamic charts will be rendered here -->\n </div>\n </div>\n</body>\n\n</html>",
"x": 180,
"y": 640,
"wires": []
},
{
"id": "19b4b1d6fb485513",
"type": "comment",
"z": "9f7b1754540bc18d",
"g": "026d68caf9ab771a",
"name": "index.css",
"info": "h1 {\n text-align: center;\n}\n\n#charts-container {\n display: flex;\n flex-wrap: wrap;\n gap: 20px;\n justify-content: center;\n}\n\n.chart-card {\n border: 1px solid #ddd;\n border-radius: 8px;\n padding: 15px;\n box-shadow: 0px 2px 5px rgba(0, 0, 0, 0.1);\n background-color: #fff;\n flex: 1 1 calc(50% - 20px);\n margin: 10px;\n max-width: 600px;\n}\n\n@media (max-width: 768px) {\n .chart-card {\n flex: 1 1 100%;\n }\n}\n\ncanvas {\n width: 100% !important;\n height: auto !important;\n display: block;\n}\n\n.chart-card h2 {\n font-size: 18px;\n text-align: center;\n margin-bottom: 10px;\n}\n\n.chart-wrapper {\n padding: 20px;\n margin: 10px;\n border: 1px solid #ddd;\n border-radius: 10px;\n box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);\n background-color: #ffffff;\n height: 300px;\n width: 46%;\n}\n\n#charts-container {\n display: flex;\n flex-wrap: wrap;\n justify-content: center;\n gap: 20px;\n}\n\n/* Ensure canvas takes full height of the wrapper */\ncanvas {\n width: 100%;\n height: 100% !important;\n /* Override any other height properties */\n}",
"x": 180,
"y": 680,
"wires": []
},
{
"id": "94e67937ea9fe050",
"type": "comment",
"z": "9f7b1754540bc18d",
"g": "026d68caf9ab771a",
"name": "index.js",
"info": "// Initialize uibuilder\nuibuilder.start();\n\n// Hold chart configurations and data\nlet chartConfigurations = [];\nlet chartData = {};\nconst chartInstances = {}; // Store Chart.js instances keyed by chart ID\n\n\ndocument.addEventListener('DOMContentLoaded', () => {\n // Send the 'loadSettings' message to Node-RED when the page loads\n uibuilder.send({ topic: 'loadSettings' });\n});\n\nuibuilder.onChange('ioConnected', (connected) => {\n if (connected) {\n // Send a message to request loadSettings\n uibuilder.send({\n topic: 'loadSettings',\n payload: 'Requesting settings load on client connect'\n });\n }\n});\n\n// Handle messages from uibuilder\nuibuilder.onChange('msg', (msg) => {\n\n console.log('Message received:', msg);\n\n // Handle settings load message\n if (msg.topic === 'loadSettings') {\n // Check if settings payload is valid and initialize charts\n if (msg.payload && Array.isArray(msg.payload)) {\n chartConfigurations = msg.payload;\n initializeCharts();\n } else {\n console.error(\"Settings not found or not in expected format\");\n }\n }\n\n // Handle real-time data message\n if (msg.topic === 'data') {\n // Update charts with real-time data\n updateCharts(msg.payload);\n }\n});\n\n// Initialize charts based on settings loaded\nfunction initializeCharts() {\n const chartsContainer = document.getElementById('charts-container');\n if (!chartsContainer) {\n console.error('Charts container not found!');\n return;\n }\n\n chartsContainer.innerHTML = ''; // Clear any existing charts\n\n // Loop through the chart configurations to create charts dynamically\n if (Array.isArray(chartConfigurations) && chartConfigurations.length > 0) {\n chartConfigurations.forEach((config, configIndex) => {\n if (config.payloadNames && Array.isArray(config.payloadNames)) {\n config.payloadNames.forEach((payloadName, keyIndex) => {\n const chartId = `chart-${configIndex}-${keyIndex}`;\n const chartWrapper = document.createElement('div');\n chartWrapper.className = 'chart-wrapper';\n chartWrapper.style.width = '400px';\n chartWrapper.style.height = '300px';\n\n const chartCanvas = document.createElement('canvas');\n chartCanvas.id = chartId;\n chartWrapper.appendChild(chartCanvas);\n chartsContainer.appendChild(chartWrapper);\n\n const ctx = chartCanvas.getContext('2d');\n\n const dataset = {\n label: payloadName,\n data: chartData[payloadName] || [],\n borderColor: `rgba(${Math.random() * 255}, ${Math.random() * 255}, ${Math.random() * 255}, 1)`,\n backgroundColor: `rgba(${Math.random() * 255}, ${Math.random() * 255}, ${Math.random() * 255}, 0.2)`,\n borderWidth: 2,\n };\n\n chartInstances[chartId] = new Chart(ctx, {\n type: config.chartType || 'line',\n data: {\n labels: chartData[payloadName] ? chartData[payloadName].map((_, idx) => `Label ${idx + 1}`) : [],\n datasets: [dataset],\n },\n options: {\n responsive: true,\n maintainAspectRatio: false,\n plugins: {\n legend: {\n labels: { color: 'black' },\n },\n tooltip: {\n callbacks: {\n label: (tooltipItem) => {\n const point = tooltipItem.raw;\n return `${tooltipItem.dataset.label}: ${point}`;\n },\n },\n },\n },\n },\n });\n });\n }\n });\n } else {\n console.warn('No valid chart configurations found.');\n }\n}\n\n// Function to update chart (example, replace with your implementation)\nfunction updateChart(settings) {\n console.log('Updating chart with settings:', settings);\n // Chart.js or other chart library code to update the chart\n}\n\n// Update the charts dynamically with new data\nfunction updateCharts(payload) {\n console.log('Updating charts with payload:', payload);\n\n // Loop through the payload data and update the corresponding charts\n Object.keys(payload).forEach((key) => {\n chartConfigurations.forEach((config, configIndex) => {\n if (config.payloadNames && config.payloadNames.includes(key)) {\n const chartId = `chart-${configIndex}-${config.payloadNames.indexOf(key)}`;\n const chart = chartInstances[chartId];\n\n if (!chart) {\n console.warn(`Chart ${chartId} not found.`);\n return;\n }\n\n // Add a new data point with the timestamp\n const currentTime = new Date().toLocaleTimeString();\n chart.data.labels.push(currentTime);\n chart.data.datasets[0].data.push(payload[key]);\n\n // Limit to the last 10 data points\n if (chart.data.labels.length > 10) {\n chart.data.labels = chart.data.labels.slice(-10);\n chart.data.datasets[0].data = chart.data.datasets[0].data.slice(-10);\n }\n\n chart.update();\n }\n });\n });\n}\n",
"x": 310,
"y": 640,
"wires": []
},
{
"id": "54f124111665f03c",
"type": "comment",
"z": "9f7b1754540bc18d",
"g": "026d68caf9ab771a",
"name": "setting.html",
"info": "",
"x": 327,
"y": 679,
"wires": []
},
{
"id": "b4dc095894adb6d4",
"type": "comment",
"z": "9f7b1754540bc18d",
"g": "026d68caf9ab771a",
"name": "setting.js",
"info": "let chartConfigurations = []; // Store chart configurations globally\nlet lastPayloadKeys = []; // Cache the last payload keys\n\n// Listen for messages from Node-RED via uibuilder\nuibuilder.start();\n\n// Listen for messages from Node-RED to update dropdowns\nuibuilder.onChange('msg', (msg) => {\n if (msg.payload) {\n console.log('Payload received:', msg.payload);\n\n // Parse string payload to JSON if needed\n if (typeof msg.payload === 'string') {\n try {\n msg.payload = JSON.parse(msg.payload);\n } catch (e) {\n console.error('Failed to parse payload:', e);\n return;\n }\n }\n\n if (typeof msg.payload !== 'object') {\n console.error('Payload is not an object:', msg.payload);\n return;\n }\n\n const payloadKeys = Object.keys(msg.payload);\n\n // Only update the dropdown if the payload keys have changed\n if (!arraysEqual(lastPayloadKeys, payloadKeys)) {\n console.log('Updated payload keys:', payloadKeys);\n updateAllDropdowns(payloadKeys); // Update dropdowns with new keys\n lastPayloadKeys = payloadKeys; // Update cached keys\n }\n }\n});\n\n// Utility function to compare arrays\nfunction arraysEqual(arr1, arr2) {\n if (arr1.length !== arr2.length) return false;\n return arr1.every((value, index) => value === arr2[index]);\n}\n\n// Update all payload dropdowns dynamically\nfunction updateAllDropdowns(payloadKeys) {\n const dropdowns = document.querySelectorAll('.first-payload-dropdown');\n\n dropdowns.forEach(dropdown => {\n dropdown.innerHTML = '<option value=\"\">Select Payload Name</option>'; // Clear existing options\n\n payloadKeys.forEach((key) => {\n const option = document.createElement('option');\n option.value = key;\n option.textContent = key;\n dropdown.appendChild(option);\n });\n });\n}\n\n// Adds a new chart configuration card\nfunction addChartConfigCard(config = null) {\n const chartConfigContainer = document.getElementById('chart-config-container');\n const card = document.createElement('div');\n card.className = 'card p-3 col-md-4';\n\n const uniqueId = `config-${Date.now()}`;\n\n card.innerHTML = `\n <div class=\"form-group\">\n <label for=\"chartType-${uniqueId}\">Select Chart Type</label>\n <select id=\"chartType-${uniqueId}\" class=\"form-control chart-type\">\n <option value=\"line\">Line</option>\n <option value=\"bar\">Bar</option>\n <option value=\"radar\">Radar</option>\n <option value=\"doughnut\">Doughnut</option>\n <option value=\"pie\">Pie</option>\n <option value=\"polarArea\">Polar Area</option>\n <option value=\"bubble\">Bubble</option>\n <option value=\"scatter\">Scatter</option>\n </select>\n </div>\n <div class=\"form-group\">\n <label>Payload Names</label>\n <div class=\"payload-container\">\n <select class=\"form-control first-payload-dropdown\">\n <option value=\"\">Select Payload Name</option>\n </select>\n <button class=\"btn btn-primary btn-sm mt-2\" onclick=\"addPayloadName(this)\">Add Payload</button>\n </div>\n <div class=\"added-payloads mt-2\"></div>\n </div>\n <button class=\"btn btn-danger mt-2\" onclick=\"removeCard(this)\">Remove Chart</button>\n `;\n\n chartConfigContainer.appendChild(card);\n updateAllDropdowns(lastPayloadKeys); // Populate dropdown with the latest payload keys\n\n if (config) {\n const addedPayloads = card.querySelector('.added-payloads');\n config.payloadNames.forEach(name => addPayloadToList(addedPayloads, name));\n card.querySelector(`#chartType-${uniqueId}`).value = config.chartType;\n }\n}\n\n// Add selected payload name\nfunction addPayloadName(button) {\n const payloadContainer = button.closest('.payload-container');\n const firstDropdown = payloadContainer.querySelector('.first-payload-dropdown');\n const selectedPayloadName = firstDropdown.value;\n\n if (!selectedPayloadName) {\n alert('Please select a payload name first!');\n return;\n }\n\n const addedPayloads = payloadContainer.nextElementSibling;\n addPayloadToList(addedPayloads, selectedPayloadName);\n}\n\nfunction addPayloadToList(container, payloadName) {\n const existingNames = Array.from(container.querySelectorAll('span')).map(span => span.textContent.trim());\n\n if (existingNames.includes(payloadName)) {\n alert('Payload name already added.');\n return;\n }\n\n const payloadNameDiv = document.createElement('div');\n payloadNameDiv.className = 'd-flex align-items-center mb-2';\n\n payloadNameDiv.innerHTML = `\n <span class=\"mr-2\">${payloadName}</span>\n <button class=\"btn btn-danger btn-sm\" onclick=\"removePayloadName(this)\">Delete</button>\n `;\n\n container.appendChild(payloadNameDiv);\n}\n\nfunction removeCard(button) {\n button.parentElement.remove();\n}\n\nfunction removePayloadName(button) {\n button.parentElement.remove();\n}\n\n\n// Modify the collectConfigurations function to send the chart configurations to Node-RED\nfunction collectConfigurations() {\n chartConfigurations = [];\n const cards = document.querySelectorAll('#chart-config-container .card');\n\n cards.forEach(card => {\n const chartType = card.querySelector('.chart-type').value;\n const payloadNames = Array.from(card.querySelectorAll('.added-payloads div span')).map(span => span.textContent.trim());\n\n if (chartType && payloadNames.length > 0) {\n chartConfigurations.push({ chartType, payloadNames });\n }\n });\n\n // Send the chart configurations to Node-RED\n uibuilder.send({\n topic: 'saveChartConfigurations', // Distinct topic for chart settings\n payload: chartConfigurations, // Send only the configurations\n });\n\n saveConfigurationsToLocalStorage();\n alert('Settings saved successfully!');\n}\n\n\n\nfunction saveConfigurationsToLocalStorage() {\n localStorage.setItem('chartConfigurations', JSON.stringify(chartConfigurations));\n console.log('Configurations saved:', chartConfigurations);\n}\n\nfunction loadConfigurationsFromLocalStorage() {\n const savedConfigurations = localStorage.getItem('chartConfigurations');\n if (savedConfigurations) {\n chartConfigurations = JSON.parse(savedConfigurations);\n chartConfigurations.forEach(config => addChartConfigCard(config));\n }\n}\n\ndocument.addEventListener('DOMContentLoaded', () => {\n loadConfigurationsFromLocalStorage();\n\n const saveButton = document.getElementById('save-button');\n if (saveButton) {\n saveButton.addEventListener('click', collectConfigurations);\n }\n\n const addChartButton = document.getElementById('add-chart-button');\n if (addChartButton) {\n addChartButton.addEventListener('click', () => addChartConfigCard());\n }\n});\n\n",
"x": 460,
"y": 640,
"wires": []
}
]
I need help in this that when I connect inject it running like hell 1 second and therefore, I have two issues that it loads the settings in 1 second and also data but due to loading setting the data does not show so I need to make it load at once
could anyone help in this issue.