NEWS
[gelöst] Influxdb Daten im Chart Node Red dartstellen
-
Hallo zusammen,
ich bin schon seit einiger Zeit hier im Forum beim Lesen. Einiges habe ich schon in meinem Smart Home realisieren können, die meisten Sachen mit Iobroker direkt. Einige mit Node Red. Finde Node Red als eine schöne und schlanke Erweiterung in Verbindung mit Iobroker womit man grafische Oberfläche sehr einfach darstellen lässt.
Seit einigen Tagen komme ich mit meinem Problemchen nicht weiter. Ich loge einige States im Iobroker mit Influxdb. Relativ einfach lassen sich diese Daten mit Hilfe von Grafana als Grafen realisieren. Ich möchte aber die Grafen direkt im Dash vom Node Red darstellen. Ohne Umweg mit dem Influxdb ist es auch sehr einfach direkt realisierbar. Die Daten werden so nur bis zum nächsten Start von Node Red bzw. Iobroker. Nach dem Neustart sind die Grafen im Node Red leer.
Jetzt zum eigentlichen Problem. Mit dem Influxdb Node lassen sich die gelogte Daten aus Iobroker auslesen und zwar z.B. mit folgendem Query:"SELECT mean("value") FROM "mqtt.0.ESP_Easy.Stromtaehler.Stundenvariable" WHERE time >= 1539900000000ms GROUP BY time(1m) fill(null)".
Die Debug Ausgabe kommt in folgender Form:
19.10.2018, 22:46:50node: 7ae85892.4ef0c8
msg.payload : array[1367]
array[1367]
[0 … 9]
0: object
time: "2018-10-18T22:00:00.000Z"
mean: 0.09340659340661261
1: object
time: "2018-10-18T22:01:00.000Z"
mean: null
…....
Diese Array in eine Form umzumodeln, damit diese vom Chart in Dash dargestellt werden kriege ich einfach nicht hin. Kann mir einer helfen?
-
Also, das ist nicht ganz trivial.
Du musst die Daten erstmal so aufbreiten, dass das chart-node damit umgehen kann. Die nötigen Infos findest du hier:
https://github.com/node-red/node-red-da … /Charts.md
Am Einfachsten läßt sich das mit einem JSONata-Ausdruck im change node machen. Hier ist mal ein Flow, wo ich dass für 4 Temperaturen gemacht habe.
! ````
[
{
"id": "7133e515.2b0734",
"type": "tab",
"label": "dash board chart",
"disabled": false,
"info": ""
},
{
"id": "93e089db.78f3f8",
"type": "inject",
"z": "7133e515.2b0734",
"name": "eyery 10 minutes",
"topic": "",
"payload": "",
"payloadType": "date",
"repeat": "600",
"crontab": "",
"once": false,
"onceDelay": "",
"x": 130,
"y": 100,
"wires": [
[
"dea5b93e.8a8658"
]
]
},
{
"id": "218bb197.4f76e6",
"type": "comment",
"z": "7133e515.2b0734",
"name": "WG",
"info": "",
"x": 305,
"y": 221,
"wires": []
},
{
"id": "d3e2b200.235638",
"type": "comment",
"z": "7133e515.2b0734",
"name": "Garten",
"info": "",
"x": 305,
"y": 61,
"wires": []
},
{
"id": "8c8422cd.9290d8",
"type": "comment",
"z": "7133e515.2b0734",
"name": "Keller",
"info": "",
"x": 299.5,
"y": 402,
"wires": []
},
{
"id": "b8cb26f0.59e388",
"type": "comment",
"z": "7133e515.2b0734",
"name": "Wohnzimmer",
"info": "",
"x": 320.5,
"y": 577,
"wires": []
},
{
"id": "c89bbe6b.dd6ce8",
"type": "change",
"z": "7133e515.2b0734",
"name": "",
"rules": [
{
"t": "set",
"p": "temperatureSerie[0]",
"pt": "flow",
"to": "payload",
"tot": "msg"
}
],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
"x": 373.5,
"y": 149,
"wires": [
[
"5f801909.c5e6b"
]
]
},
{
"id": "879792d5.5b98",
"type": "change",
"z": "7133e515.2b0734",
"name": "",
"rules": [
{
"t": "set",
"p": "temperatureSerie[1]",
"pt": "flow",
"to": "payload",
"tot": "msg"
}
],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
"x": 373,
"y": 318,
"wires": [
[
"e247246f.d64cb"
]
]
},
{
"id": "786feec4.4976b8",
"type": "change",
"z": "7133e515.2b0734",
"name": "",
"rules": [
{
"t": "set",
"p": "temperatureSerie[2]",
"pt": "flow",
"to": "payload",
"tot": "msg"
}
],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
"x": 374,
"y": 484,
"wires": [
[
"b4631622.8abc9"
]
]
},
{
"id": "74dc1066.4b312",
"type": "change",
"z": "7133e515.2b0734",
"name": "",
"rules": [
{
"t": "set",
"p": "temperatureSerie[3]",
"pt": "flow",
"to": "payload",
"tot": "msg"
}
],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
"x": 366,
"y": 667,
"wires": [
[
"a924f14a.9912a"
]
]
},
{
"id": "a924f14a.9912a",
"type": "change",
"z": "7133e515.2b0734",
"name": "prepare line chart data structure",
"rules": [
{
"t": "set",
"p": "payload",
"pt": "msg",
"to": "(\t $ts := $flowContext('temperatureSerie');\t $series := $ts.**.columns[1];\t $data := $ts.[results.series.$map(values,\t function($v) \t {\t {\t "x": $v[0],\t "y": $v[1]\t }\t }\t )];\t [\t {\t "series": $series,\t "data": $data\t }\t ]\t)",
"tot": "jsonata"
}
],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
"x": 370,
"y": 747,
"wires": [
[
"5df4b9d.5033148",
"4183e9bc.2eb678"
]
]
},
{
"id": "5df4b9d.5033148",
"type": "debug",
"z": "7133e515.2b0734",
"name": "neu",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "payload",
"x": 630,
"y": 740,
"wires": []
},
{
"id": "6747cee.2c3013",
"type": "comment",
"z": "7133e515.2b0734",
"name": "Version Query by Query",
"info": "",
"x": 115,
"y": 61,
"wires": []
},
{
"id": "dea5b93e.8a8658",
"type": "influxdb in",
"z": "7133e515.2b0734",
"influxdb": "100c1f29.be4979",
"name": "WG SELECT Query (Raw)",
"query": "SELECT mean("value") AS "Garten" FROM "global"."Garten Temperatur" WHERE time >= now() - 30d GROUP BY time(30m) fill(previous)",
"rawOutput": true,
"precision": "ms",
"retentionPolicy": "",
"x": 365,
"y": 101,
"wires": [
[
"c89bbe6b.dd6ce8"
]
]
},
{
"id": "5f801909.c5e6b",
"type": "influxdb in",
"z": "7133e515.2b0734",
"influxdb": "100c1f29.be4979",
"name": "Garten SELECT Query (Raw)",
"query": "SELECT mean("value") AS "Wintergarten" FROM "global"."Wintergarten Temperatur" WHERE time >= now() - 30d GROUP BY time(30m) fill(previous)",
"rawOutput": true,
"precision": "ms",
"retentionPolicy": "",
"x": 376,
"y": 269,
"wires": [
[
"879792d5.5b98"
]
]
},
{
"id": "e247246f.d64cb",
"type": "influxdb in",
"z": "7133e515.2b0734",
"influxdb": "100c1f29.be4979",
"name": "Keller SELECT Query (Raw)",
"query": "SELECT mean("value") AS "Keller" FROM "Keller Temperatur" WHERE time >= now() - 30d GROUP BY time(30m) fill(previous)",
"rawOutput": true,
"precision": "ms",
"retentionPolicy": "",
"x": 375,
"y": 440,
"wires": [
[
"786feec4.4976b8"
]
]
},
{
"id": "b4631622.8abc9",
"type": "influxdb in",
"z": "7133e515.2b0734",
"influxdb": "100c1f29.be4979",
"name": "WZ SELECT Query (Raw)",
"query": "SELECT mean("value") AS "Wohnzimmer" FROM "global"."Wohnzimmer Temperatur" WHERE time >= now() - 30d GROUP BY time(30m) fill(previous)",
"rawOutput": true,
"precision": "ms",
"retentionPolicy": "",
"x": 355,
"y": 615,
"wires": [
[
"74dc1066.4b312"
]
]
},
{
"id": "4183e9bc.2eb678",
"type": "ui_chart",
"z": "7133e515.2b0734",
"name": "myTemp Chart N",
"group": "6ab47f3d.6e0238",
"order": 2,
"width": "12",
"height": "5",
"label": "Temperatur at home",
"chartType": "line",
"legend": "true",
"xformat": "auto",
"interpolate": "linear",
"nodata": "hole Daten",
"dot": false,
"ymin": "",
"ymax": "",
"removeOlder": "100",
"removeOlderPoints": "3",
"removeOlderUnit": "604800",
"cutout": 0,
"useOneColor": false,
"colors": [
"#1F77B4",
"#AEC7E8",
"#FF7F0E",
"#2CA02C",
"#98DF8A",
"#D62728",
"#FF9896",
"#9467BD",
"#C5B0D5"
],
"useOldStyle": false,
"x": 670,
"y": 800,
"wires": [
[],
[]
]
},
{
"id": "100c1f29.be4979",
"type": "influxdb",
"z": "7133e515.2b0734",
"hostname": "raspidb.local",
"port": "8086",
"protocol": "http",
"database": "iobroker",
"name": "iobroker",
"usetls": false,
"tls": "8474f464.9c575"
},
{
"id": "6ab47f3d.6e0238",
"type": "ui_group",
"z": "",
"name": "iobroker",
"tab": "45199b8a.5c68cc",
"order": 2,
"disp": true,
"width": "12",
"collapse": false
},
{
"id": "8474f464.9c575",
"type": "tls-config",
"z": "",
"name": "local-tls",
"cert": "",
"key": "",
"ca": "",
"certname": "",
"keyname": "",
"caname": "",
"verifyservercert": false
},
{
"id": "45199b8a.5c68cc",
"type": "ui_tab",
"z": "",
"name": "Klima",
"icon": "dashboard",
"order": 2
}
]![3999_temp.png](/assets/uploads/files/3999_temp.png) Du musst natürlich den Influx-Server und die jeweilige Query in den influx-nodes anpassen. Ich empfehle bei den Loggingeinstellungen der entsprechenden Datenpunkte einen Alias zu verwenden. Da hast du's nicht nur bei der Query einfacher, sondern auch keinen Stress, wenn du mal einen Sensor austauschen willst. Aber das nur am Rande. Zu Erklärung: - Im Beispiel wird die Temp der Sensoren alle 10 min nacheinander aus der influxDB gelesen. - Das Ergebnis wird jeweils als Element im flow Array flow.temperatureSerie[] zwischengespeichert. - Nach der letzen Query wird in einem weiteren change-node die nötige Struktur für das chart-node aufbereitet. In dem JSONata-Ausdruck steckt die eigentliche Magie;-) Hast du weniger oder mehr Lines(Datenpunkte) in deinem chart, kannst du einfach die entsprechenden Paare Query/change-node weglassen bzw. erweitern. Der Flow läuft auch mit einen einzigen Paar. Bei mehr Datenpunkten im chart ist eine Flow-Version, die per loop durch die Datenpunkte geht und eine generische Query verwendet flexibler. Da läßt sich dann auch einfach das Timeintervall per Dashboard einstellen. Aber das Beispiel lass ich mal lieber. Da solltest du schon einigermaßen fit in node-red sein.
-
Hallo rewenode. Vielen Dank für die ausführliche Antwort. Gestern hatte ich keine Zeit, heute morgen habe ich dein Flow mit minimalen Änderungen nachgebaut und bin somit zum gewünschten Ergebnis wesentlich näher gekommen. Chart zeigt keine Fehler beim Eingang mehr und schleift fleißig die Daten zum Ausgang, die ich auch mit Debug Node abfangen kann. In dem Chart auf Dashboard werden leider keine Daten ausgegeben.
~~![](</s><URL url=)http://up.picr.de/34126229og.jpg" />
So sieht mein Flow aus.
~~![](</s><URL url=)http://up.picr.de/34126183eo.jpg" />
! [
! {
! "id": "7879efc9.25b55",
! "type": "tab",
! "label": "Flow 2",
! "disabled": false,
! "info": ""
! },
! {
! "id": "2db50346.c8055c",
! "type": "change",
! "z": "7879efc9.25b55",
! "name": "prepare line chart data structure",
! "rules": [
! {
! "t": "set",
! "p": "payload",
! "pt": "msg",
! "to": "(\t $ts := $flowContext('temperatureSerie');\t $series := $ts.**.columns[1];\t $data := $ts.[results.series.$map(values,\t function($v) \t {\t {\t "x": $v[0],\t "y": $v[1]\t }\t }\t )];\t [\t {\t "series": ["Temperature"],\t "data": $data,\t "labels": ["Temperature"]\t }\t ]\t)",
! "tot": "jsonata"
! }
! ],
! "action": "",
! "property": "",
! "from": "",
! "to": "",
! "reg": false,
! "x": 410,
! "y": 300,
! "wires": [
! [
! "d6c2f60c.2a8718"
! ]
! ]
! },
! {
! "id": "d6c2f60c.2a8718",
! "type": "ui_chart",
! "z": "7879efc9.25b55",
! "name": "",
! "group": "c4c2504b.34298",
! "order": 0,
! "width": 0,
! "height": 0,
! "label": "chart",
! "chartType": "bar",
! "legend": "false",
! "xformat": "HH:mm:ss",
! "interpolate": "linear",
! "nodata": "",
! "dot": false,
! "ymin": "",
! "ymax": "",
! "removeOlder": 1,
! "removeOlderPoints": "50",
! "removeOlderUnit": "3600",
! "cutout": 0,
! "useOneColor": false,
! "colors": [
! "#1f77b4",
! "#aec7e8",
! "#ff7f0e",
! "#2ca02c",
! "#98df8a",
! "#d62728",
! "#ff9896",
! "#9467bd",
! "#c5b0d5"
! ],
! "useOldStyle": false,
! "x": 770,
! "y": 300,
! "wires": [
! [
! "31232770.05fcc8"
! ],
! []
! ]
! },
! {
! "id": "1e083aed.f9fcc5",
! "type": "inject",
! "z": "7879efc9.25b55",
! "name": "eyery 10 sekunden",
! "topic": "",
! "payload": "",
! "payloadType": "date",
! "repeat": "600",
! "crontab": "",
! "once": false,
! "onceDelay": "",
! "x": 180,
! "y": 80,
! "wires": [
! [
! "ccfc8af1.8efa38"
! ]
! ]
! },
! {
! "id": "ccfc8af1.8efa38",
! "type": "influxdb in",
! "z": "7879efc9.25b55",
! "influxdb": "fd0ab2dd.7087e",
! "name": "WG SELECT Query (Raw)",
! "query": "SELECT mean("value") FROM "mqtt.0.ESP_Easy.Stromtaehler.Stundenvariable" WHERE time >= 1539986400000ms GROUP BY time(100m) fill(previous)",
! "rawOutput": true,
! "precision": "s",
! "retentionPolicy": "",
! "x": 445,
! "y": 74,
! "wires": [
! [
! "78a63c19.5d2c94",
! "470ed2d2.e1b2ac"
! ]
! ]
! },
! {
! "id": "78a63c19.5d2c94",
! "type": "change",
! "z": "7879efc9.25b55",
! "name": "",
! "rules": [
! {
! "t": "set",
! "p": "temperatureSerie[0]",
! "pt": "flow",
! "to": "payload",
! "tot": "msg"
! }
! ],
! "action": "",
! "property": "",
! "from": "",
! "to": "",
! "reg": false,
! "x": 440,
! "y": 140,
! "wires": [
! [
! "2db50346.c8055c"
! ]
! ]
! },
! {
! "id": "470ed2d2.e1b2ac",
! "type": "debug",
! "z": "7879efc9.25b55",
! "name": "",
! "active": true,
! "tosidebar": true,
! "console": false,
! "tostatus": false,
! "complete": "false",
! "x": 740,
! "y": 133,
! "wires": []
! },
! {
! "id": "234c10d7.52aab",
! "type": "debug",
! "z": "7879efc9.25b55",
! "name": "neu",
! "active": true,
! "tosidebar": true,
! "console": false,
! "tostatus": false,
! "complete": "payload",
! "x": 670,
! "y": 360,
! "wires": []
! },
! {
! "id": "31232770.05fcc8",
! "type": "debug",
! "z": "7879efc9.25b55",
! "name": "",
! "active": true,
! "tosidebar": true,
! "console": false,
! "tostatus": false,
! "complete": "false",
! "x": 870,
! "y": 220,
! "wires": []
! },
! {
! "id": "c4c2504b.34298",
! "type": "ui_group",
! "z": "",
! "name": "iobroker",
! "tab": "b8639845.7705e8",
! "order": 2,
! "disp": true,
! "width": "12",
! "collapse": false
! },
! {
! "id": "fd0ab2dd.7087e",
! "type": "influxdb",
! "z": "",
! "hostname": "127.0.0.1",
! "port": "8086",
! "protocol": "http",
! "database": "iobroker",
! "name": "iobroker",
! "usetls": false,
! "tls": "d9ecd6bf.d21798"
! },
! {
! "id": "b8639845.7705e8",
! "type": "ui_tab",
! "z": "",
! "name": "Klima",
! "icon": "dashboard",
! "order": 2
! },
! {
! "id": "d9ecd6bf.d21798",
! "type": "tls-config",
! "z": "",
! "name": "local-tls",
! "cert": "",
! "key": "",
! "ca": "",
! "certname": "",
! "keyname": "",
! "caname": "",
! "verifyservercert": false
! }
! ]~~~~ -
Na das sieht doch schon mal gut aus.
Ich konnte 2 Probleme feststellen.
1. JSONata Ausdruck
Da gibt es ein Problem, dass ich auch nicht Kopf hatte. So wie ich dir den Flow gegeben habe funktioniert er tatsächlich nur bei min. 2 Lines.
Grund:
Bei JSONata werden Ergebnisse in Arrays gepackt, wenn man sie in eckige Klammern einschließt. AAABER nur, wenn das Ergebnis auch mehrere Werte enthält! Sonst wird kein Array erstellt. Soll das Ergebnis dennoch in einem Array landen (Array mit einem Wert im Array) muss man doppelte eckige Klammern verwenden.
Findest Du alles hier: http://docs.jsonata.org/
Die Stelle ist im Bild markiert.
2. Du hattest dein chart-node auf Bargraph eingestellt. Die benötigte JSON-Struktur für Bargraph sieht aber anders aus:
https://github.com/node-red/node-red-da … pie-charts
korrigierte JSONata/chart-nodes
! ````
[
{
"id": "8e6ead0f.949b7",
"type": "change",
"z": "87a09c8f.74c548",
"name": "prepare line chart data structure",
"rules": [
{
"t": "set",
"p": "payload",
"pt": "msg",
"to": "(\t $ts := $flowContext('temperatureSerie');\t $series := $ts.**.columns[1];\t $data := $ts.[[\t results.series.$map(\t values,\t function($v) \t {\t {\t "x": $v[0],\t "y": $v[1]\t }\t \t }\t \t )\t ]];\t [\t {\t "series": ["Temperature"],\t "data": $data,\t "labels": ["Temperature"]\t \t }\t \t ]\t\t)",
"tot": "jsonata"
}
],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
"x": 330,
"y": 300,
"wires": [
[
"13f5bcda.75303b"
]
]
},
{
"id": "13f5bcda.75303b",
"type": "ui_chart",
"z": "87a09c8f.74c548",
"name": "",
"group": "e50096a7.63d9d",
"order": 0,
"width": 0,
"height": 0,
"label": "chart",
"chartType": "line",
"legend": "false",
"xformat": "auto",
"interpolate": "linear",
"nodata": "",
"dot": false,
"ymin": "",
"ymax": "",
"removeOlder": "10",
"removeOlderPoints": "50",
"removeOlderUnit": "604800",
"cutout": 0,
"useOneColor": false,
"colors": [
"#1f77b4",
"#aec7e8",
"#ff7f0e",
"#2ca02c",
"#98df8a",
"#d62728",
"#ff9896",
"#9467bd",
"#c5b0d5"
],
"useOldStyle": false,
"x": 570,
"y": 300,
"wires": [
[],
[]
]
},
{
"id": "e50096a7.63d9d",
"type": "ui_group",
"z": "",
"name": "iobroker",
"tab": "b8631bfd.eb5d1",
"order": 2,
"disp": true,
"width": "12",
"collapse": false
},
{
"id": "b8631bfd.eb5d1",
"type": "ui_tab",
"z": "",
"name": "Test",
"icon": "dashboard",
"order": 2
}
]ACHTUNG Solltest Du den Flow später doch auf mehrere Lines erweitern, musst du die doppelte eckige Klammer warscheinlich wieder weglassen.
-
Super, Danke dir!!! Hat sofort funktioniert.
-
Prima.
Nachtrag:
Mir ist noch eingefallen, wie der JSONata Ausdruck so formuliert werden kann, dass er sowohl mit einer als auch mit mehreren lines klar kommt:
( $ts := $flowContext('temperatureSerie'); $series := $ts.**.columns[1]; $data := $ts.[ results.series.$map( values, function($v) { { "x": $v[0], "y": $round($v[1] = null ? 0 : $v[1],2) } } ) ]; [ { "series": [[$series]], "data": $data } ] )
Dazu musst du nur````
[[$series]] statt $serie