Compare commits

...

1 Commits

Author SHA1 Message Date
znetsixe
ac3bdd76eb changes 2025-05-26 17:44:56 +02:00
9 changed files with 12167 additions and 0 deletions

3242
config/aeration.json Normal file

File diff suppressed because it is too large Load Diff

2091
config/machine.json Normal file

File diff suppressed because it is too large Load Diff

2609
config/machineGroup.json Normal file

File diff suppressed because it is too large Load Diff

973
config/measurement.json Normal file
View File

@@ -0,0 +1,973 @@
{
"annotations": {
"list": [
{
"builtIn": 1,
"datasource": {
"type": "grafana",
"uid": "-- Grafana --"
},
"enable": true,
"hide": true,
"iconColor": "rgba(0,255,255,1)",
"name": "Annotations & Alerts",
"type": "dashboard"
}
]
},
"editable": true,
"fiscalYearStartMonth": 0,
"graphTooltip": 0,
"id": 14,
"links": [],
"panels": [
{
"collapsed": false,
"gridPos": {
"h": 1,
"w": 24,
"x": 0,
"y": 0
},
"id": 1,
"panels": [],
"title": "Machine Overview",
"type": "row"
},
{
"datasource": {
"default": true,
"type": "influxdb",
"uid": "cdzg44tv250jkd"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "thresholds"
},
"custom": {
"align": "auto",
"cellOptions": {
"type": "auto"
},
"inspect": false
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
}
]
},
"unit": "none"
},
"overrides": []
},
"gridPos": {
"h": 4,
"w": 6,
"x": 0,
"y": 1
},
"id": 2,
"options": {
"cellHeight": "sm",
"footer": {
"countRows": false,
"fields": "",
"reducer": [
"sum"
],
"show": false
},
"frameIndex": 2,
"showHeader": false
},
"pluginVersion": "11.2.0",
"targets": [
{
"datasource": {
"type": "influxdb",
"uid": "cdzg44tv250jkd"
},
"query": "from(bucket: \"${bucket}\")\n |> range(start: -1y) // Adjust time range as needed\n |> filter(fn: (r) => r._measurement == \"${measurement}\" and r._field == \"state\")\n |> sort(columns: [\"_time\"], desc: true)\n |> limit(n: 1)",
"refId": "A"
}
],
"title": "Machine State",
"type": "table"
},
{
"datasource": {
"default": true,
"type": "influxdb",
"uid": "cdzg44tv250jkd"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "thresholds"
},
"custom": {
"align": "auto",
"cellOptions": {
"type": "auto"
},
"inspect": false
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 4,
"w": 6,
"x": 6,
"y": 1
},
"id": 4,
"options": {
"cellHeight": "sm",
"footer": {
"countRows": false,
"fields": "",
"reducer": [
"sum"
],
"show": false
},
"frameIndex": 1,
"showHeader": false
},
"pluginVersion": "11.2.0",
"targets": [
{
"datasource": {
"type": "influxdb",
"uid": "cdzg44tv250jkd"
},
"query": "from(bucket: \"${bucket}\")\n |> range(start: -1y) // Adjust time range as needed\n |> filter(fn: (r) => r._measurement == \"${measurement}\" and r._field == \"mode\")\n |> sort(columns: [\"_time\"], desc: true)\n |> limit(n: 1)",
"refId": "A"
}
],
"title": "Machine Mode",
"type": "table"
},
{
"collapsed": false,
"gridPos": {
"h": 1,
"w": 24,
"x": 0,
"y": 5
},
"id": 5,
"panels": [],
"title": "Measurements",
"type": "row"
},
{
"datasource": {
"default": true,
"type": "influxdb",
"uid": "cdzg44tv250jkd"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisBorderShow": false,
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"barWidthFactor": 0.6,
"drawStyle": "line",
"fillOpacity": 0,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"insertNulls": false,
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "auto",
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"decimals": 2,
"displayName": "pressure",
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 5,
"w": 6,
"x": 0,
"y": 6
},
"id": 6,
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"pluginVersion": "11.2.0",
"targets": [
{
"datasource": {
"type": "influxdb",
"uid": "cdzg44tv250jkd"
},
"query": "from(bucket: \"${bucket}\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn:(r) => r._measurement==\"${measurement}\" and r._field==\"upstream_measured_pressure\" or r._field==\"upstream_predicted_pressure\")\n |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: true)",
"refId": "A"
}
],
"title": "Upstream pressure",
"type": "timeseries"
},
{
"datasource": {
"default": true,
"type": "influxdb",
"uid": "cdzg44tv250jkd"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisBorderShow": false,
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"barWidthFactor": 0.6,
"drawStyle": "line",
"fillOpacity": 0,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"insertNulls": false,
"lineInterpolation": "stepAfter",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "auto",
"spanNulls": true,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"decimals": 2,
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 10,
"w": 5,
"x": 6,
"y": 6
},
"id": 15,
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"pluginVersion": "11.2.0",
"targets": [
{
"datasource": {
"type": "influxdb",
"uid": "cdzg44tv250jkd"
},
"query": "from(bucket: \"${bucket}\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn:(r) => r._measurement==\"${measurement}\" and r._field==\"differential_measured_pressure\" )\n |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: true)",
"refId": "A"
}
],
"title": "Differential pressure",
"type": "timeseries"
},
{
"datasource": {
"default": true,
"type": "influxdb",
"uid": "cdzg44tv250jkd"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisBorderShow": false,
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"barWidthFactor": 0.6,
"drawStyle": "line",
"fillOpacity": 0,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"insertNulls": false,
"lineInterpolation": "stepAfter",
"lineStyle": {
"fill": "solid"
},
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "auto",
"spanNulls": true,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"decimals": 2,
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 10,
"w": 6,
"x": 11,
"y": 6
},
"id": 16,
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"pluginVersion": "11.2.0",
"targets": [
{
"datasource": {
"type": "influxdb",
"uid": "cdzg44tv250jkd"
},
"query": "from(bucket: \"${bucket}\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn:(r) => r._measurement==\"${measurement}\" and ( r._field==\"upstream_measured_power\" or r._field==\"upstream_predicted_power\" ) )\n |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: true)",
"refId": "A"
}
],
"title": "Power",
"type": "timeseries"
},
{
"datasource": {
"default": true,
"type": "influxdb",
"uid": "cdzg44tv250jkd"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisBorderShow": false,
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"barWidthFactor": 0.6,
"drawStyle": "line",
"fillOpacity": 0,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"insertNulls": false,
"lineInterpolation": "stepAfter",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "auto",
"spanNulls": true,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"decimals": 2,
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 10,
"w": 7,
"x": 17,
"y": 6
},
"id": 14,
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"pluginVersion": "11.2.0",
"targets": [
{
"datasource": {
"type": "influxdb",
"uid": "cdzg44tv250jkd"
},
"query": "from(bucket: \"${bucket}\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn:(r) => r._measurement==\"${measurement}\" and ( r._field==\"downstream_measured_flow\" or r._field==\"downstream_predicted_flow\") )\n |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: true)",
"refId": "A"
}
],
"title": "Flow",
"type": "timeseries"
},
{
"datasource": {
"default": true,
"type": "influxdb",
"uid": "cdzg44tv250jkd"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "fixed"
},
"custom": {
"axisBorderShow": false,
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"barWidthFactor": 0.6,
"drawStyle": "line",
"fillOpacity": 0,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"insertNulls": false,
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "auto",
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "dashed"
}
},
"decimals": 2,
"displayName": "pressure",
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 1500
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 5,
"w": 6,
"x": 0,
"y": 11
},
"id": 7,
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"pluginVersion": "11.2.0",
"targets": [
{
"datasource": {
"type": "influxdb",
"uid": "cdzg44tv250jkd"
},
"query": "from(bucket: \"${bucket}\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn:(r) => r._measurement==\"${measurement}\" and r._field==\"downstream_measured_pressure\" or r._field==\"downstream_predicted_pressure\")\n |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: true)",
"refId": "A"
}
],
"title": "Downstream pressure",
"type": "timeseries"
},
{
"datasource": {
"default": true,
"type": "influxdb",
"uid": "cdzg44tv250jkd"
},
"description": "Flow / Power",
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisBorderShow": false,
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"barWidthFactor": 0.6,
"drawStyle": "line",
"fillOpacity": 0,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"insertNulls": false,
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "auto",
"spanNulls": true,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"decimals": 2,
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 13,
"w": 24,
"x": 0,
"y": 16
},
"id": 17,
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"pluginVersion": "11.2.0",
"targets": [
{
"datasource": {
"type": "influxdb",
"uid": "cdzg44tv250jkd"
},
"query": "from(bucket: \"${bucket}\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn:(r) => r._measurement==\"${measurement}\" and ( r._field==\"downstream_measured_efficiency\" or r._field==\"downstream_predicted_efficiency\") )\n |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: true)",
"refId": "A"
}
],
"title": "Efficiency",
"type": "timeseries"
},
{
"collapsed": false,
"gridPos": {
"h": 1,
"w": 24,
"x": 0,
"y": 29
},
"id": 10,
"panels": [],
"title": "Machine control",
"type": "row"
},
{
"datasource": {
"default": true,
"type": "influxdb",
"uid": "cdzg44tv250jkd"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisBorderShow": false,
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"barWidthFactor": 0.6,
"drawStyle": "line",
"fillOpacity": 0,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"insertNulls": false,
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "auto",
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"decimals": 2,
"fieldMinMax": false,
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 10
},
{
"color": "#EAB839",
"value": 20
},
{
"color": "yellow",
"value": 80
},
{
"color": "red",
"value": 90
}
]
}
},
"overrides": [
{
"__systemRef": "hideSeriesFrom",
"matcher": {
"id": "byNames",
"options": {
"mode": "exclude",
"names": [
"position {geoLocation_x=\"0\", geoLocation_y=\"0\", geoLocation_z=\"0\", id=\"b1226bbc99239bc5\", model=\"H05K-S03R+HGM1X-X280KO\", name=\"m1\", role=\"RotationalDeviceController\", softwareType=\"machine\", subType=\"centrifugal pumps\", supplier=\"hydrostal\", type=\"generic\", unit=\"m3/h\", uuid=\"null\"}"
],
"prefix": "All except:",
"readOnly": true
}
},
"properties": []
}
]
},
"gridPos": {
"h": 11,
"w": 24,
"x": 0,
"y": 30
},
"id": 13,
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"pluginVersion": "8.0.0",
"targets": [
{
"datasource": {
"type": "influxdb",
"uid": "cdzg44tv250jkd"
},
"query": "from(bucket: \"${bucket}\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn:(r) => r._measurement==\"${measurement}\" and r._field==\"ctrl\")\n |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: true)",
"refId": "A"
}
],
"title": "Control",
"type": "timeseries"
}
],
"refresh": "10s",
"schemaVersion": 39,
"tags": [],
"templating": {
"list": [
{
"current": {
"selected": false,
"text": "influxdb",
"value": "influxdb"
},
"hide": 0,
"includeAll": false,
"label": "dbase",
"multi": false,
"name": "dbase",
"options": [
{
"selected": true,
"text": "influxdb",
"value": "influxdb"
}
],
"query": "influxdb",
"queryValue": "",
"skipUrlSync": false,
"type": "custom"
},
{
"current": {
"selected": false,
"text": "template",
"value": "template"
},
"hide": 0,
"includeAll": false,
"label": "",
"multi": false,
"name": "measurement",
"options": [
{
"selected": true,
"text": "template",
"value": "template"
}
],
"query": "template",
"queryValue": "",
"skipUrlSync": false,
"type": "custom"
},
{
"current": {
"selected": false,
"text": "template",
"value": "template"
},
"hide": 0,
"includeAll": false,
"multi": false,
"name": "bucket",
"options": [
{
"selected": true,
"text": "template",
"value": "template"
}
],
"query": "template",
"skipUrlSync": false,
"type": "custom"
}
]
},
"time": {
"from": "now-15m",
"to": "now"
},
"timepicker": {
"refresh_intervals": [
"10s",
"30s",
"1m",
"5m",
"15m",
"30m",
"1h"
]
},
"timezone": "browser",
"title": "template",
"uid": "test",
"version": 8,
"weekStart": ""
}

2682
config/monster.json Normal file

File diff suppressed because it is too large Load Diff

149
dashboardapi.html Normal file
View File

@@ -0,0 +1,149 @@
<script type="module">
import * as menuUtils from "/generalFunctions/helper/menuUtils.js";
RED.nodes.registerType('dashboardapi', {
category: 'wbd typical',
color: '#4f8582',
defaults: {
name: { value: "" },
// New defaults for configuration:
logLevel: { value: "info" },
enableLog: { value: false },
host: { value: "" },
port: { value: 0 },
bearerToken: { value: "" }
},
inputs: 1,
outputs: 1,
inputLabels: "Usage see manual",
outputLabels: ["feedback"],
icon: "font-awesome/fa-line-chart",
label: function () {
// Show the name
return this.name || "dashboardapi";
},
oneditprepare: function () {
const node = this;
console.log("Edit Prepare");
const elements = {
// Basic fields
name: document.getElementById("node-input-name"),
number: document.getElementById("node-input-number"),
// Logging fields
logLevelSelect: document.getElementById("node-input-logLevel"),
logCheckbox: document.getElementById("node-input-enableLog"),
// Grafana connector fields
host: document.getElementById("node-input-host"),
port: document.getElementById("node-input-port"),
bearerToken: document.getElementById("node-input-bearerToken"),
};
try {
// UI elements
menuUtils.initBasicToggles(elements);
} catch (e) {
console.log("Error fetching project settings", e);
}
},
oneditsave: function () {
const node = this;
console.log(`------------ Saving changes to node ------------`);
//save basic properties
["name", "host", "port", "bearerToken"].forEach(
(field) => {
const element = document.getElementById(`node-input-${field}`);
if (element) {
node[field] = element.value || "";
}
}
);
const logLevelElement = document.getElementById("node-input-logLevel");
node.logLevel = logLevelElement ? logLevelElement.value || "info" : "info";
}
});
</script>
<!-- Main UI Template -->
<script type="text/html" data-template-name="dashboardapi">
<div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
<input
type="text"
id="node-input-name"
placeholder="name"
style="width:70%;"
/>
</div>
<div class="form-row">
<label for="node-input-host"><i class="fa fa-server"></i> Grafana Host</label>
<input type="text" id="node-input-host" placeholder="Host">
</div>
<div class="form-row">
<label for="node-input-port"><i class="fa fa-plug"></i> Grafana Port</label>
<input type="number" id="node-input-port" placeholder="Port">
</div>
<div class="form-row">
<label for="node-input-bearerToken"><i class="fa fa-key"></i> Bearer Token</label>
<input type="text" id="node-input-bearerToken" placeholder="Bearer Token">
</div>
<hr />
<!-- loglevel checkbox -->
<div class="form-row">
<label for="node-input-enableLog"
><i class="fa fa-cog"></i> Enable Log</label
>
<input
type="checkbox"
id="node-input-enableLog"
style="width:20px; vertical-align:baseline;"
/>
<span>Enable logging</span>
</div>
<div class="form-row" id="row-logLevel">
<label for="node-input-logLevel"><i class="fa fa-cog"></i> Log Level</label>
<select id="node-input-logLevel" style="width:60%;">
<option value="info">Info</option>
<option value="debug">Debug</option>
<option value="warn">Warn</option>
<option value="error">Error</option>
</select>
</div>
</script>
<script type="text/html" data-help-name="dashboardapi">
<p>
This node interacts with the Grafana API with the following capabilities:
Dashboard Management: Create, update, or delete dashboards.
Metrics Querying: Retrieve performance and operational data.
Alerts Management: Monitor and manage alerts.
It allows you to configure:
- Connection details (host, port, bearer token) for secure API access.
- Logging options, including the ability to enable logging and set the log level (info, debug, warn, error).
These features provide flexible and controlled interactions with the Grafana API.
</p>
</script>

108
dashboardapi.js Normal file
View File

@@ -0,0 +1,108 @@
module.exports = function (RED) {
function dashboardapi(config) {
// create node
RED.nodes.createNode(this, config);
//call this => node so whenver you want to call a node function type node and the function behind it
var node = this;
try {
//fetch obj
const Dashboardapi = require("./dependencies/dashboardapi/dashboardapi_class");
//load user defined config in the node-red UI
const dConfig = {
general: {
name: config.name,
id: node.id,
logging: {
logLevel: config.logLevel,
enabled: config.enableLog,
},
},
grafanaConnector: {
host: config.host,
port: config.port,
bearerToken: config.bearerToken,
},
};
//make new measurement on creation to work with.
const d = new Dashboardapi(dConfig);
// put m on node memory as source
node.source = d;
function updateNodeStatus(val) {
if (val && val.grafanaResponse) {
// Check for a successful response from the Grafana API call
if (val.grafanaResponse.status === 200) {
node.status({
fill: "green",
shape: "dot",
text: "Grafana API: Success",
});
node.log("Grafana API call completed successfully.");
} else {
node.status({
fill: "red",
shape: "ring",
text: "Grafana API: Error",
});
node.error(
"Grafana API call failed with status: " +
val.grafanaResponse.status
);
}
}
}
//-------------------------------------------------------------------->>what to do on input
node.on("input", async function (msg, send, done) {
try {
switch(msg.topic) {
//on start make dashboard
case 'registerChild':
const childId = msg.payload;
const childObj = RED.nodes.getNode(childId);
if (!childObj || !childObj.source) {
throw new Error("Missing or invalid child node");
}
const child = childObj.source;
msg.payload = await d.generateDashB(child.config);
msg.topic = "create";
msg.headers = {
'Accept': 'application/json',
'Content-Type': 'application/json',
'Authorization': 'Bearer glsa_gI7fOMEd844p1gZt9iaDeEFpeYtejRj7_cf1c41f8'// + config.bearerToken
};
console.log(`Child registered: ${childId}`);
send(msg);
break;
}
done();
} catch (err) {
node.status({ fill: "red", shape: "ring", text: "Bad request data" });
node.error("Bad request data: " + err.message, msg);
done(err);
}
});
// tidy up any async code here - shutdown connections and so on.
node.on("close", function () {
});
} catch (e) {
console.log(e);
}
}
RED.nodes.registerType("dashboardapi", dashboardapi);
};

View File

@@ -0,0 +1,130 @@
{
"general": {
"name": {
"default": "dashboardapi",
"rules": {
"type": "string",
"description": "A human-readable name or label."
}
},
"id": {
"default": null,
"rules": {
"type": "string",
"nullable": true,
"description": "A unique identifier for this configuration. If not provided, defaults to null."
}
},
"unit": {
"default": "N/A",
"rules": {
"type": "string",
"description": "The default measurement unit for this configuration (e.g., 'meters', 'seconds', 'unitless')."
}
},
"logging": {
"logLevel": {
"default": "info",
"rules": {
"type": "enum",
"values": [
{
"value": "debug",
"description": "Log messages are printed for debugging purposes."
},
{
"value": "info",
"description": "Informational messages are printed."
},
{
"value": "warn",
"description": "Warning messages are printed."
},
{
"value": "error",
"description": "Error messages are printed."
}
]
}
},
"enabled": {
"default": true,
"rules": {
"type": "boolean",
"description": "Indicates whether logging is active. If true, log messages will be generated."
}
}
}
},
"functionality": {
"softwareType": {
"default": "dashboardapi",
"rules": {
"type": "string",
"description": "Specified software type for this configuration."
}
},
"role": {
"default": "auto ui generator",
"rules": {
"type": "string",
"description": "Indicates the role this configuration plays within the system."
}
}
},
"grafanaConnector": {
"host": {
"default": "localhost",
"rules": {
"type": "string",
"description": "The host name or IP address of the server."
}
},
"port": {
"default": 3000,
"rules": {
"type": "integer",
"description": "The port number on which the server is listening."
}
},
"protocol": {
"default": "http",
"rules": {
"type": "string",
"description": "The protocol used to communicate with the server."
}
},
"header": {
"default": {},
"rules": {
"type": "object",
"schema":{
"Accept": {
"default": "application/json",
"rules": {
"type": "string",
"description": "Specifies the expected response format for requests using this header."
}
},
"Content-Type":{
"default": "application/json",
"rules": {
"type": "string",
"description": "Specifies the MIME type of the content."
}
}
},
"description": "The header object to be sent with the request."
}
},
"bearerToken": {
"default": null,
"rules": {
"type": "string",
"nullable": true,
"description": "The bearer token to be sent with the request."
}
}
}
}

View File

@@ -0,0 +1,183 @@
/**
* @file dashboardapi_class.js
*
* Permission is hereby granted to any person obtaining a copy of this software
* and associated documentation files (the "Software"), to use it for personal
* or non-commercial purposes, with the following restrictions:
*
* 1. **No Copying or Redistribution**: The Software or any of its parts may not
* be copied, merged, distributed, sublicensed, or sold without explicit
* prior written permission from the author.
*
* 2. **Commercial Use**: Any use of the Software for commercial purposes requires
* a valid license, obtainable only with the explicit consent of the author.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE, AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES, OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT, OR OTHERWISE, ARISING FROM,
* OUT OF, OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* Ownership of this code remains solely with the original author. Unauthorized
* use of this Software is strictly prohibited.
*
* @summary A class for generating dynamic dashboards for system monitoring.
* @description The Dashboardapi class facilitates the creation and customization
* of Grafana dashboards using JSON templates. It dynamically updates templating values
* based on provided measurement and bucket configurations, enabling seamless integration
* within monitoring environments.
* @module dashboardapi_class
* @exports Dashboardapi
* @version 0.1.0
* @since 0.1.0
*
* Author:
* - Rene De Ren
* Email:
* - rene@thegoldenbasket.nl
*
* Future Enhancements:
* - Incorporate additional dynamic templating capabilities.
* - Add support for more customization options within dashboard configurations.
*/
//A class to interact and manipulate sampling machines
//load local dependencies
const EventEmitter = require('events');
const Logger = require('../../../generalFunctions/helper/logger');
//load all config modules
const defaultConfig = require('./dashboardapiConfig.json');
const ConfigUtils = require('../../../generalFunctions/helper/configUtils');
//load registration utility
const ChildRegistrationUtils = require('../../../generalFunctions/helper/childRegistrationUtils');
class Dashboardapi{
/*------------------- Construct and set vars -------------------*/
constructor(config={}){
// basic setup
this.emitter = new EventEmitter(); // Own EventEmitter
this.configUtils = new ConfigUtils(defaultConfig);
this.config = this.configUtils.initConfig(config);
this.child = {} ; // object to hold child information so we know on what to subscribe
// Init after config is set
this.logger = new Logger(this.config.general.logging.enabled, this.config.general.logging.logLevel, this.config.general.name);
}
//how to handle children like the pump on a machine group node? Do we duplicate the information or fetch the names of the registered children en connect them
//to the dashboard?
//this is a question that needs to be answered in the future.
/*------------------- FUNCTIONS -------------------*/
async getDashboard(softwareType){
try {
let response = await fetch(`http://localhost:1880/dashboardapi/config/${softwareType}.json`);
if (!response.ok) {
this.logger.error(`Failed to fetch dashboard template: ${response.statusText}`);
}
const jsonDashB = await response.json();
return jsonDashB;
} catch (error) {
this.logger.error(`Failed to fetch dashboard template: ${error.message}`);
}
}
//generate dashboards based on the childs connected
async generateDashB(config) {
//define file name
const softwareType = config.functionality.softwareType;
this.logger.debug(`Generating dashboard for ${softwareType}`);
const jsonDashB = await this.getDashboard(softwareType);
// Validate templating structure exists
if (!jsonDashB.templating ||
!Array.isArray(jsonDashB.templating.list) ||
jsonDashB.templating.list.length < 3) {
this.logger.error("Dashboard JSON templating structure is not as expected.");
throw new Error("Invalid dashboard template structure");
}
const bucketName = "lvl2";
const measurementName = config.general.name;
// Set properties for a new dashboard
jsonDashB.id = null;
jsonDashB.uid = null;
jsonDashB.title = measurementName;
// Update measurement placeholder (using index 1)
const measurementTpl = jsonDashB.templating.list[1];
measurementTpl.current.text = measurementName;
measurementTpl.current.value = measurementName;
if (Array.isArray(measurementTpl.options) && measurementTpl.options.length > 0) {
measurementTpl.options[0].text = measurementName;
measurementTpl.options[0].value = measurementName;
}
measurementTpl.query = measurementName;
// Update bucket placeholder (using index 2)
const bucketTpl = jsonDashB.templating.list[2];
bucketTpl.current.text = bucketName;
bucketTpl.current.value = bucketName;
if (Array.isArray(bucketTpl.options) && bucketTpl.options.length > 0) {
bucketTpl.options[0].text = bucketName;
bucketTpl.options[0].value = bucketName;
}
bucketTpl.query = bucketName;
const output = {
dashboard: jsonDashB,
folderId: 0,
overwrite: true
};
return output;
}
} // end of class
module.exports = Dashboardapi;
/*
//import a child
const Child = require('../../../measurement/dependencies/measurement/measurement');
//const Child = require('../../../rotatingMachine/dependencies/machine/machine');
console.log(`Creating child...`);
const child = new Child(config={
general:{
name:"PT1",
logging:{
enabled:true,
logLevel:"debug",
},
},
functionality:{
softwareType:"measurement",
},
asset:{
type:"sensor",
subType:"pressure",
},
});
const d = new Dashboardapi();
testGen();
function testGen(){
d.generateDashB(child.config).then((res)=>{
console.log(res);
});
}
//*/