{"id":2119,"date":"2023-02-05T10:22:59","date_gmt":"2023-02-05T09:22:59","guid":{"rendered":"https:\/\/www.rustimation.eu\/?p=2119"},"modified":"2025-09-22T09:30:20","modified_gmt":"2025-09-22T07:30:20","slug":"viessmann-api-und-node-red-teil-6b","status":"publish","type":"post","link":"https:\/\/www.rustimation.eu\/index.php\/viessmann-api-und-node-red-teil-6b\/","title":{"rendered":"Viessmann API und Node-Red &#8211; Teil 6b &#8211; Influx Diagramme"},"content":{"rendered":"<p>Um es vorweg zu sagen, ich besch\u00e4ftige mich in diesem Artikel mit Influx zur Erzeugung von Diagrammen<strong> ausschlie\u00dflich in Node-Red<\/strong>. Es gibt auch andere, leistungsf\u00e4higere Diagramml\u00f6sungen wie Grafana. Die dabei zu durchlaufende Lernkurve ist allerdings sehr steil und au\u00dferdem haben wir anschlie\u00dfend ein weiteres System an der Hand, so dass wir zwischen Diagrammen und Einstellungen immer hin- und herspringen m\u00fcssen. Ich finde, Analysen und Befehle auf einer einzigen User-Interfaceschicht besser. Auch wenn Node-Red, was Diagramme angeht, nicht so viel kann, f\u00fcr den Hausgebrauch reicht es.\u00a0 Ein weiterer Vorteil von Node-Red ist, dass die Darstellung auf mobilen Endger\u00e4ten besser ist.<\/p>\n<p><!--more--><\/p>\n<p>Nachteil ist, dass wir die in Influx seriell abgespeicherten Daten erst mit etwas Aufwand in JSON umwandeln m\u00fcssen. Aber das ist ein lehrreicher Einmalaufwand &#8211; siehe weiter unten.<\/p>\n<p>Wie schon im <a href=\"https:\/\/www.rustimation.eu\/index.php\/viessmann-api-und-node-red-teil-6\/\">vorigen Kapitel<\/a> geschrieben, konzentriere ich mich auf Influx Version 1.8 mit dem Command Line Interface.<\/p>\n<p>Das Influx Command Line Interface (CLI) ist richtig Old School; Look and Feel erinnern an das selige dBase von vor 40 Jahren. Die Befehle sind auch sehr \u00e4hnlich, wer schon einmal SQL Abfragen gebaut hat, wird sich sehr schnell zu Hause f\u00fchlen. Alle anderen m\u00fcssen sich jedoch keine Sorgen machen, ich erk\u00e4re das im Folgenden.<\/p>\n<h2>Erste Schritte<\/h2>\n<h3>Influx CLI<\/h3>\n<p>Der Aufruf erfolgt im RaspbianTerminal ganz einfach mit <span class=\"lang:default decode:true crayon-inline \">influx<\/span> . Falls das nicht klappt, bitte bei meinem Artikel <a href=\"https:\/\/www.rustimation.eu\/index.php\/viessmann-api-und-node-red-teil-6\/\">Viessmann API und Node-Red \u2013 Teil 6a<\/a> nachsehen. Da steht, wie man Influx installiert.<\/p>\n<p>Wir legen als n\u00e4chstes eine D<span style=\"color: #000000;\">atenbank na<\/span>mens apidata an: <span class=\"lang:default decode:true crayon-inline\">create database apidata <\/span>. Der Name ist nat\u00fcrlich wahlfrei.<\/p>\n<p style=\"padding-left: 40px;\"><span style=\"color: #999999;\">In einer einzelnen Datenbank k\u00f6nnen mehrere verschiedene Tabellen mit Messwerten stecken. z.B. Temperatur, Betriebsdauer, Verbrauch. Diese Tabellen hei\u00dfen Buckets (Eimer), die dort hineingeschriebenen Messwerte nennt man Measurements (Messungen).<\/span><\/p>\n<h3>Fake Daten generieren mit Node-Red<\/h3>\n<p>Bauen wir uns erst einmal einen kleinen Datenzoo aus Fake-Werten: Dazu brauchen wir einen <em>Inject<\/em>-, <em>Funktions<\/em>&#8211; und\u00a0 einen <em>Influx Out-Node<\/em>. Eventuell sind die Influx Nodes noch nicht vorhanden. Dann m\u00fcssen wir sie \u00fcber die Node-Red Palettenverwaltung nachinstallieren.<\/p>\n<p>Der <em>Inject Node<\/em> sollte so eingestellt werden, dass er alle paar Minuten ein Timestamp losschickt.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-2141\" src=\"https:\/\/www.rustimation.eu\/wordpress\/wp-content\/uploads\/2023\/02\/2023-02-03-16_40_16-Node-RED-_-192.168.178.24-\u2013-Mozilla-Firefox.png\" alt=\"\" width=\"1030\" height=\"140\" srcset=\"https:\/\/www.rustimation.eu\/wordpress\/wp-content\/uploads\/2023\/02\/2023-02-03-16_40_16-Node-RED-_-192.168.178.24-\u2013-Mozilla-Firefox.png 1030w, https:\/\/www.rustimation.eu\/wordpress\/wp-content\/uploads\/2023\/02\/2023-02-03-16_40_16-Node-RED-_-192.168.178.24-\u2013-Mozilla-Firefox-300x41.png 300w, https:\/\/www.rustimation.eu\/wordpress\/wp-content\/uploads\/2023\/02\/2023-02-03-16_40_16-Node-RED-_-192.168.178.24-\u2013-Mozilla-Firefox-1024x139.png 1024w, https:\/\/www.rustimation.eu\/wordpress\/wp-content\/uploads\/2023\/02\/2023-02-03-16_40_16-Node-RED-_-192.168.178.24-\u2013-Mozilla-Firefox-768x104.png 768w\" sizes=\"auto, (max-width: 1030px) 100vw, 1030px\" \/><\/p>\n<p>Der f<em>unction node<\/em> sieht so aus:<\/p>\n<pre class=\"toolbar-overlay:false toolbar-hide:false lang:js decode:true\">\/\/Insert Query mit Zufallswerten f\u00fcr Temperatur\r\nvar twert = Math.floor(Math.random() * 20);\r\nmsg.payload = twert;\r\nreturn msg;<\/pre>\n<p>Der <em>Influx out<\/em> Node wird wie folgt eingestellt:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-2142\" src=\"https:\/\/www.rustimation.eu\/wordpress\/wp-content\/uploads\/2023\/02\/2023-02-03-16_51_22-Node-RED-_-192.168.178.24-\u2013-Mozilla-Firefox.png\" alt=\"\" width=\"629\" height=\"379\" srcset=\"https:\/\/www.rustimation.eu\/wordpress\/wp-content\/uploads\/2023\/02\/2023-02-03-16_51_22-Node-RED-_-192.168.178.24-\u2013-Mozilla-Firefox.png 629w, https:\/\/www.rustimation.eu\/wordpress\/wp-content\/uploads\/2023\/02\/2023-02-03-16_51_22-Node-RED-_-192.168.178.24-\u2013-Mozilla-Firefox-300x181.png 300w\" sizes=\"auto, (max-width: 629px) 100vw, 629px\" \/><\/p>\n<p>Node-Red nennt hier den (oder die?) Bucket \"Measurement\", was meines Erachtens etwas doppeldeutig ist.<\/p>\n<p>Will man Temperaturen von mehreren Messstationen in demselben Bucket abspeichern, (z.B. f\u00fcr Nordseite oder Terrasse) dann brauchen wir noch ein so genanntes <em>Tag<\/em> (Etikett), das dem Wert in der Payload mitgegeben wird. Wird die Payload wie oben dargestellt so mir nichts dir nichts \u00fcbergeben, bekommt der Wert die \u00dcberschrift \"value\", was bei komlexeren Datenstrukturen nicht gerade aussagekr\u00e4ftig ist. Deshalb sollten wir noch entsprechenden \u00dcberschriften f\u00fcr das Tag und den Wert mitliefern. Der Funktionsnode sieht dann so aus:<\/p>\n<pre class=\"toolbar-overlay:false toolbar-hide:false lang:js mark:3 decode:true\">\/\/Insert Query mit Zufallswerten\r\nvar twert = Math.floor(Math.random() * 20);\r\nmsg.payload = {ort: \"Nordseite\", gradc: twert};\r\nreturn msg;<\/pre>\n<p>Die Payload wird hiermit zu einem Objekt.\u00a0 Das Tag f\u00fcr den Ort ist hier \"Nordseite\" und die Temperaturwerte bekommen die \u00dcberschrift \"gradc\".<\/p>\n<h4>Arbeiten mit echten Daten<\/h4>\n<p>Im \"echten Leben\", also ohne Fake Daten wird der <em>influx in<\/em> Node direkt mit dem Datenoutput aus unserer API verkabelt.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-2205\" src=\"https:\/\/www.rustimation.eu\/wordpress\/wp-content\/uploads\/2023\/02\/2023-02-06-17_06_27-Node-RED-_-192.168.178.24-\u2013-Mozilla-Firefox.png\" alt=\"\" width=\"754\" height=\"86\" srcset=\"https:\/\/www.rustimation.eu\/wordpress\/wp-content\/uploads\/2023\/02\/2023-02-06-17_06_27-Node-RED-_-192.168.178.24-\u2013-Mozilla-Firefox.png 754w, https:\/\/www.rustimation.eu\/wordpress\/wp-content\/uploads\/2023\/02\/2023-02-06-17_06_27-Node-RED-_-192.168.178.24-\u2013-Mozilla-Firefox-300x34.png 300w\" sizes=\"auto, (max-width: 754px) 100vw, 754px\" \/><\/p>\n<p>Fragt man nun die Viessmann Daten alle 90 Sekunden ab, werden \u00fcber den Tag 960 Werte weggespeichert. Viele davon sind gleich, da sich die Lufttemperatur nicht andauernd \u00e4ndert sondern \u00fcber Stunden gleich bleibt. Daher ist es besser, einen Filter einzubauen, damit unsere Datenbank sparsam bef\u00fcllt wird:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-2206\" src=\"https:\/\/www.rustimation.eu\/wordpress\/wp-content\/uploads\/2023\/02\/2023-02-06-17_07_32-Node-RED-_-192.168.178.24-\u2013-Mozilla-Firefox.png\" alt=\"\" width=\"922\" height=\"103\" srcset=\"https:\/\/www.rustimation.eu\/wordpress\/wp-content\/uploads\/2023\/02\/2023-02-06-17_07_32-Node-RED-_-192.168.178.24-\u2013-Mozilla-Firefox.png 922w, https:\/\/www.rustimation.eu\/wordpress\/wp-content\/uploads\/2023\/02\/2023-02-06-17_07_32-Node-RED-_-192.168.178.24-\u2013-Mozilla-Firefox-300x34.png 300w, https:\/\/www.rustimation.eu\/wordpress\/wp-content\/uploads\/2023\/02\/2023-02-06-17_07_32-Node-RED-_-192.168.178.24-\u2013-Mozilla-Firefox-768x86.png 768w\" sizes=\"auto, (max-width: 922px) 100vw, 922px\" \/><\/p>\n<p>Der Filter Node schickt empfangene Daten nur weiter, wenn sie sich gegen\u00fcber dem vorherigen Datum ge\u00e4ndert haben. Sehr praktisch!<\/p>\n<h3>Daten mit Influx ansehen<\/h3>\n<p>Springen wir zur\u00fcck zu unserer Tabelle mit den gefaketen Werten und lassen den Flow ein paar Minuten oder l\u00e4nger laufen &#8211; oder klicken mehrmals auf den Inject Node. Ansehen k\u00f6nnen wir uns die Rohdaten wieder im Influx CLI mit <span class=\"lang:default decode:true crayon-inline\">select * from temperatur<\/span><\/p>\n<p>Das wird\u00a0 allerdings einen Fehler <span class=\"lang:default decode:true crayon-inline\">ERR: database name required<\/span>\u00a0 produzieren. Wir m\u00fcssen zuerst die zu verwendende Datenbank mit <span class=\"lang:default decode:true crayon-inline \">use apidata<\/span>\u00a0 setzen &#8211; dann funktioniert's.<\/p>\n<p>Das Ergebnis mit unseren Phantasiewerten sieht ungef\u00e4hr so aus:<\/p>\n<pre class=\"toolbar-overlay:false toolbar-hide:false lang:default decode:true\">&gt; select * from temperatur\r\nname: temperatur\r\ntime                gradc ort\r\n----                ----- ---\r\n1675529440180191561 11    Nordseite\r\n1675529740177792060 16    Nordseite\r\n1675530040178699621 19    Nordseite\r\n\r\n<\/pre>\n<p>Die Spalte <em>time<\/em> enth\u00e4lt den <a href=\"https:\/\/de.wikipedia.org\/wiki\/Unixzeit\" target=\"_blank\" rel=\"noopener\">Unix Timestamp<\/a> auf Nanosekunden genau. Eigentlich ein Overkill, aber es ist halt so.\u00a0 Die Website <a href=\"https:\/\/www.unixtimestamp.com\/\" target=\"_blank\" rel=\"noopener\">Unix time stamp<\/a> ist sehr hilfreich dabei, diese Daten hin oder her zu konvertieren.<\/p>\n<p style=\"padding-left: 40px;\"><span style=\"color: #999999;\">Der 19-stellige Nanosekunden genaue Timestamp ist f\u00fcr Node Red etwas zuviel. Damit Node Red mit diesen Timestamps rechnen kann,\u00a0 m\u00fcssen wir den Timestamp durch 1000 dividieren, d.h. auf Millisekunden k\u00fcrzen. Das macht man am einfachsten mit der <\/span><span style=\"color: #993300;\"><span class=\"lang:js decode:true crayon-inline\">Number()<\/span><a style=\"color: #993300;\" href=\"https:\/\/www.w3schools.com\/jsref\/jsref_number.asp\" target=\"_blank\" rel=\"noopener\">Methode.<\/a><\/span><\/p>\n<p>Will man die Daten der letzten 3 Stunden abfragen, lautet der Befehl:<\/p>\n<p><span class=\"lang:default decode:true crayon-inline\">select time, gradc from temperatur where time &gt; (now() &#8211; 3h)<\/span><\/p>\n<p>Influxdb 1.8 erlaubt folgende Literale f\u00fcr die Zeitdauer:<\/p>\n<ul>\n<li>microseconds:\u00a0<span class=\"lang:default decode:true crayon-inline \">u<\/span>\u00a0 or<span class=\"lang:default decode:true crayon-inline \">\u00b5<\/span><\/li>\n<li>milliseconds: <span class=\"lang:default decode:true crayon-inline \">ms<\/span><\/li>\n<li>seconds: <span class=\"lang:default decode:true crayon-inline \">s<\/span><\/li>\n<li>minutes: <span class=\"lang:default decode:true crayon-inline \">m<\/span><\/li>\n<li>hours: <span class=\"lang:default decode:true crayon-inline \">h<\/span><\/li>\n<li>days: <span class=\"lang:default decode:true crayon-inline \">d<\/span><\/li>\n<li>weeks: <span class=\"lang:default decode:true crayon-inline \">w<\/span><\/li>\n<\/ul>\n<p>Inflix 2.x kann da etwas mehr. Bei 1.8 fehlt z.B. <span class=\"lang:default decode:true crayon-inline \">1mo<\/span>\u00a0 f\u00fcr einen Monat.<\/p>\n<p>Eine Dokumentation der Influx Abfragesprache findet ihr <a href=\"https:\/\/docs.influxdata.com\/influxdb\/v1.8\/query_language\/explore-data\/\" target=\"_blank\" rel=\"noopener\">hier<\/a>.<\/p>\n<h2>Darstellung mit Node-Red<\/h2>\n<p>Wie schon beschrieben, muss man die in Influx steckenden seriellen Daten erst in ein JSON Objekt konvertieren, bevor man sie in einem Node-Red Diagramm darstellen kann. Ich habe mich hier von folgender Website inspirieren lassen: <a href=\"https:\/\/funprojects.blog\/2020\/02\/01\/influxdb-with-node-red\/\" target=\"_blank\" rel=\"noopener\">https:\/\/funprojects.blog\/2020\/02\/01\/influxdb-with-node-red\/<\/a><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-2164\" src=\"https:\/\/www.rustimation.eu\/wordpress\/wp-content\/uploads\/2023\/02\/2023-02-05-10_35_01-Node-RED-_-192.168.178.24-\u2013-Mozilla-Firefox.png\" alt=\"\" width=\"1090\" height=\"221\" srcset=\"https:\/\/www.rustimation.eu\/wordpress\/wp-content\/uploads\/2023\/02\/2023-02-05-10_35_01-Node-RED-_-192.168.178.24-\u2013-Mozilla-Firefox.png 1090w, https:\/\/www.rustimation.eu\/wordpress\/wp-content\/uploads\/2023\/02\/2023-02-05-10_35_01-Node-RED-_-192.168.178.24-\u2013-Mozilla-Firefox-300x61.png 300w, https:\/\/www.rustimation.eu\/wordpress\/wp-content\/uploads\/2023\/02\/2023-02-05-10_35_01-Node-RED-_-192.168.178.24-\u2013-Mozilla-Firefox-1024x208.png 1024w, https:\/\/www.rustimation.eu\/wordpress\/wp-content\/uploads\/2023\/02\/2023-02-05-10_35_01-Node-RED-_-192.168.178.24-\u2013-Mozilla-Firefox-768x156.png 768w\" sizes=\"auto, (max-width: 1090px) 100vw, 1090px\" \/><\/p>\n<p>Der Node D<em>efine Query<\/em> enth\u00e4lt die oben stehende Abfrage f\u00fcr Werte der letzten 3 Stunden wie folgt:<\/p>\n<pre class=\"toolbar-overlay:false toolbar-hide:false lang:js decode:true\">msg.query ='select time, gradc from temperatur where time &gt; now() - 3h';\r\nreturn msg;<\/pre>\n<p>Der<em> influxdb in<\/em> Node enth\u00e4lt lediglich den Server und die Datenbank und liefert als Output ein Array der Art<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-2165\" src=\"https:\/\/www.rustimation.eu\/wordpress\/wp-content\/uploads\/2023\/02\/2023-02-05-11_40_00-Node-RED-_-192.168.178.24-\u2013-Mozilla-Firefox.png\" alt=\"\" width=\"348\" height=\"190\" srcset=\"https:\/\/www.rustimation.eu\/wordpress\/wp-content\/uploads\/2023\/02\/2023-02-05-11_40_00-Node-RED-_-192.168.178.24-\u2013-Mozilla-Firefox.png 348w, https:\/\/www.rustimation.eu\/wordpress\/wp-content\/uploads\/2023\/02\/2023-02-05-11_40_00-Node-RED-_-192.168.178.24-\u2013-Mozilla-Firefox-300x164.png 300w\" sizes=\"auto, (max-width: 348px) 100vw, 348px\" \/><\/p>\n<p style=\"padding-left: 40px;\"><span style=\"color: #999999;\">Man k\u00f6nnte die Query auch direkt in den Influxdb in Node schreiben, warum auch immer,\u00a0 ich finde es besser, die Query sauber getrennt in einem eigenen Knoten unterzubringen.<\/span><\/p>\n<h3>Liniendiagramm<\/h3>\n<p><span style=\"color: #000000;\">Der Node <em>make JSON <\/em>ist etwas komplexer &#8211; Erkl\u00e4rung in den Kommentaren:<\/span><\/p>\n<pre class=\"toolbar-overlay:false toolbar-hide:false lang:js mark:8 decode:true\">var series = [\"Temp. Nordseite\"]; \/\/Beschriftung der Linie bei Mouse Over\r\nvar labels = [\"Werte\"];           \/\/ wichtig f\u00fcr Payload - Inhalt egal\r\nvar data = \"[[\";                  \/\/ Anfang des generierten JSON Strings\r\nvar thetime;                      \/\/ Variable f\u00fcr gerade gelesenen Zeitstempel\r\n\r\nfor (var i = 0; i &lt; msg.payload.length; i++) {    \/\/Schleife \u00fcber die Anzahl der Payload Elemente\r\n    thetime = Number(msg.payload[i].time);        \/\/ Number() macht aus Nanosekunden Millisekunden, NR kann mit langen Timestamps nicht rechnen\r\n    data += '{ \"x\":' + thetime + ', \"y\":' + msg.payload[i].gradc + '}'; \/\/gelesenen Timestamp und Wert in JSON String schreiben\r\n    if (i &lt; (msg.payload.length - 1)) {           \/\/ Check ob alles gelesen\r\n        data += \",\"                               \/\/ wenn nein, trennendes Komma in JSON String einf\u00fcgen\r\n    } else {                                      \/\/ sonst\r\n        data += \"]]\"                              \/\/ JSON String abschlie\u00dfen\r\n    }\r\n}\r\nvar jsondata = JSON.parse(data);                  \/\/ Umwandeln JSON String in JSON Objekt\r\nmsg.payload = [{ \"series\": series, \"data\": jsondata, \"labels\": labels }]; \/\/ Datensatz f\u00fcr NR Linechart\r\nreturn msg;<\/pre>\n<p>Heraus kommt ein &#8211; in unserem Fall weitgehend sinnloses &#8211; Liniendiagramm:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-2167\" src=\"https:\/\/www.rustimation.eu\/wordpress\/wp-content\/uploads\/2023\/02\/2023-02-05-11_53_50-Centralina-\u2013-Mozilla-Firefox.png\" alt=\"\" width=\"666\" height=\"477\" srcset=\"https:\/\/www.rustimation.eu\/wordpress\/wp-content\/uploads\/2023\/02\/2023-02-05-11_53_50-Centralina-\u2013-Mozilla-Firefox.png 666w, https:\/\/www.rustimation.eu\/wordpress\/wp-content\/uploads\/2023\/02\/2023-02-05-11_53_50-Centralina-\u2013-Mozilla-Firefox-300x215.png 300w\" sizes=\"auto, (max-width: 666px) 100vw, 666px\" \/><\/p>\n<p>Das Sch\u00f6ne ist, wir k\u00f6nnen den oben dargestellten make JSON Node beliebig weiterverwenden. Anzupassen sind lediglich der Wert der \"Series\" Variablen sowie die Variable bei <span class=\"lang:js decode:true crayon-inline\">msg.payload[i].gradc<\/span> .<\/p>\n<h3>Mehr Flexibilit\u00e4t<\/h3>\n<p>Vielleicht willst du die Zeitspanne, f\u00fcr welche die Daten ausgewertet werden etwas flexibler gestalten. Das geht recht einfach \u00fcber den <em>dropdown\u00a0<\/em>Node des Dashboards.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-2170\" src=\"https:\/\/www.rustimation.eu\/wordpress\/wp-content\/uploads\/2023\/02\/2023-02-05-12_34_30-Node-RED-_-192.168.178.24-\u2013-Mozilla-Firefox.png\" alt=\"\" width=\"732\" height=\"108\" srcset=\"https:\/\/www.rustimation.eu\/wordpress\/wp-content\/uploads\/2023\/02\/2023-02-05-12_34_30-Node-RED-_-192.168.178.24-\u2013-Mozilla-Firefox.png 732w, https:\/\/www.rustimation.eu\/wordpress\/wp-content\/uploads\/2023\/02\/2023-02-05-12_34_30-Node-RED-_-192.168.178.24-\u2013-Mozilla-Firefox-300x44.png 300w\" sizes=\"auto, (max-width: 732px) 100vw, 732px\" \/><\/p>\n<p>Das Dropdown kann z.B. wie folgt bef\u00fcllt werden:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-2171\" src=\"https:\/\/www.rustimation.eu\/wordpress\/wp-content\/uploads\/2023\/02\/2023-02-05-12_35_46-Node-RED-_-192.168.178.24-\u2013-Mozilla-Firefox.png\" alt=\"\" width=\"710\" height=\"979\" srcset=\"https:\/\/www.rustimation.eu\/wordpress\/wp-content\/uploads\/2023\/02\/2023-02-05-12_35_46-Node-RED-_-192.168.178.24-\u2013-Mozilla-Firefox.png 710w, https:\/\/www.rustimation.eu\/wordpress\/wp-content\/uploads\/2023\/02\/2023-02-05-12_35_46-Node-RED-_-192.168.178.24-\u2013-Mozilla-Firefox-218x300.png 218w\" sizes=\"auto, (max-width: 710px) 100vw, 710px\" \/><\/p>\n<p>Der Query Node wird geringf\u00fcgig modifiziert:<\/p>\n<pre class=\"toolbar-overlay:false toolbar-hide:false lang:js decode:true \">msg.query ='select time, gradc from temperatur where time &gt; now() - ' + msg.payload;\r\nreturn msg;<\/pre>\n<h3>Breitere Linien und Schraffuren<\/h3>\n<p>F\u00fcgt man vor dem <span class=\"lang:js decode:true crayon-inline \">return msg;<\/span>\u00a0 Kommando des <em>make JSON<\/em> Nodes noch folgende Befehle ein, kann man die Darstellung der Linie etwas aufh\u00fcbschen:<\/p>\n<pre class=\"toolbar-overlay:false toolbar-hide:false lang:js mark:6-7 decode:true\">\/\/+++++++++++++++++++++++++++++\r\nmsg.ui_control = {\r\n    \"options\": {\r\n        \"elements\": {\r\n            \"line\": {\r\n                \"borderWidth\": 5,\r\n                \"fill\": true,\r\n                \"interpolate\": \"step\",\r\n            },\r\n            \"point\": {\r\n                \"radius\": 0\r\n            }\r\n        }\r\n    }\r\n}\r\n\/\/++++++++++++++++++++++++++++++<\/pre>\n<p>borderWidth stellt die Breite in Pixel ein, steht der fill Parameter auf true, wird der Bereich unterhalb der Linie transparent in Linienfarbe schraffiert.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-2157\" src=\"https:\/\/www.rustimation.eu\/wordpress\/wp-content\/uploads\/2023\/02\/2023-02-04-18_02_04-Centralina-\u2013-Mozilla-Firefox.png\" alt=\"\" width=\"798\" height=\"546\" srcset=\"https:\/\/www.rustimation.eu\/wordpress\/wp-content\/uploads\/2023\/02\/2023-02-04-18_02_04-Centralina-\u2013-Mozilla-Firefox.png 798w, https:\/\/www.rustimation.eu\/wordpress\/wp-content\/uploads\/2023\/02\/2023-02-04-18_02_04-Centralina-\u2013-Mozilla-Firefox-300x205.png 300w, https:\/\/www.rustimation.eu\/wordpress\/wp-content\/uploads\/2023\/02\/2023-02-04-18_02_04-Centralina-\u2013-Mozilla-Firefox-768x525.png 768w\" sizes=\"auto, (max-width: 798px) 100vw, 798px\" \/><\/p>\n<p>Problem dabei ist, dass sich diese Methode auf s\u00e4mtliche Linien des Line-Charts auswirkt, bei Multi Value Line-Charts also eher nicht zu empfehlen.<\/p>\n<h2>Balkendiagramme<\/h2>\n<p>Balkendiagramme sind auch m\u00f6glich. Allerdings m\u00fcssen wir hier etwas mehr M\u00fche in die <em>Label<\/em> Variablen stecken &#8211; beim Line Chart konnten wir die ja beliebig gestalten.<\/p>\n<h3>Verbrauch anzeigen<\/h3>\n<p>Den Tages-Verbauchswert in kW kannst du (neben anderen Verbrauchswerten) aus der API abgreifen:<\/p>\n<pre class=\"lang:js decode:true \">var idx = featureArray.findIndex((element) =&gt; element.feature === 'heating.gas.consumption.total');\r\nmsg.payload = msg.payload.data[idx].properties.day.value[0];<\/pre>\n<p>Kurz vor 24 Uhr sollte dieser Wert dann in die InfluxDB geschrieben werden, damit wir eine tageweise Auswertung fahren k\u00f6nnen.<\/p>\n<p>Lange Rede, kurzer Sinn, hier ist das JSON File zum importieren in Node-Red &#8211; der Make JSON Funktionsnode ist ausf\u00fchrlich kommentiert:<\/p>\n<pre class=\"lang:js decode:true\">[{\"id\":\"a238ea5bcd74a042\",\"type\":\"ui_chart\",\"z\":\"dff40f773b14f98a\",\"name\":\"\",\"group\":\"72a2229e7be23694\",\"order\":5,\"width\":\"0\",\"height\":\"0\",\"label\":\"Liter GPL\",\"chartType\":\"bar\",\"legend\":\"false\",\"xformat\":\"HH:mm:ss\",\"interpolate\":\"linear\",\"nodata\":\"waiting\",\"dot\":false,\"ymin\":\"0\",\"ymax\":\"\",\"removeOlder\":1,\"removeOlderPoints\":\"\",\"removeOlderUnit\":\"3600\",\"cutout\":0,\"useOneColor\":true,\"useUTC\":false,\"colors\":[\"#1f77b4\",\"#aec7e8\",\"#ff7f0e\",\"#2ca02c\",\"#98df8a\",\"#d62728\",\"#ff9896\",\"#9467bd\",\"#c5b0d5\"],\"outputs\":1,\"useDifferentColor\":false,\"className\":\"\",\"x\":1240,\"y\":760,\"wires\":[[]]},{\"id\":\"e1327a4006ba5802\",\"type\":\"function\",\"z\":\"dff40f773b14f98a\",\"name\":\"make JSON\",\"func\":\"function round(value, decimals) { \/\/ Funktion zum Runden der Werte\\n    return Number(Math.round(value + 'e' + decimals) + 'e-' + decimals);\\n}\\n\\nvar series = [\\\"ltr\\\"]; \/\/ Einheiten in Liter GPL - anpassen an die verwendete Einheit kWh o.\u00e4.\\nvar labels = \\\"[\\\";     \/\/ Beginn des JSON Strings f\u00fcr die Labels\\nvar data = \\\"[[\\\";      \/\/ Beginn des JSON Strings f\u00fcr die Werte\\nvar thetime;          \/\/ speichert die aktuel gelesene Zeit\\n\\nfor (var i = 0; i &lt; msg.payload.length; i++) {  \/\/ Schleife \u00fcber alle Werte\\n    thetime = Number(msg.payload[i].time);      \/\/ Lesen der Zeit und konvertieren in ms\\n    data += round(msg.payload[i].value, 1);     \/\/ Gerundetetn Wert an Datenarray anf\u00fcgen\\n    var d = new Date(thetime);                  \/\/ Datum aus Timestring ziehen\\n    var day = d.getDate();                      \/\/ Tag aus Datum ziehen   \\n    var month = d.getMonth() + 1;               \/\/ Monat aus Datum ziehen\\n    labels += '\\\"' + day + '\/' + month + '\\\"';    \/\/ Tag\/Monat in Labelarray schreiben    \\n    if (i &lt; (msg.payload.length - 1)) {         \/\/ solange noch Werte da\\n        data += \\\",\\\"                             \/\/ Array mit Trennkomma versehen\\n        labels += ','                           \/\/ dito Labelarray\\n    } else {                                    \/\/ sonst    \\n        data += \\\"]]\\\"                            \/\/ JSON Strings abschlie\u00dfen\\n        labels += \\\"]\\\"\\n        }\\n}\\n\/\/msg.payload = labels;                         \/\/ hilfreich f\u00fcr Testzwecke\\nvar jsondata = JSON.parse(data);                \/\/ JSONObjekt aus String erzeugen\\nvar jsonlabels = JSON.parse(labels);            \/\/ dito f\u00fcr Labels\\nmsg.payload = [{ \\\"series\\\": series, \\\"data\\\": jsondata, \\\"labels\\\": jsonlabels }]; \/\/ \u00dcbergabe an Barchart\\nreturn msg;\\n\",\"outputs\":1,\"noerr\":0,\"initialize\":\"\",\"finalize\":\"\",\"libs\":[],\"x\":990,\"y\":760,\"wires\":[[\"a238ea5bcd74a042\",\"5cfaa4df526f8f4b\"]]},{\"id\":\"99b2d04c0b6ac58a\",\"type\":\"influxdb in\",\"z\":\"dff40f773b14f98a\",\"influxdb\":\"3aa60ef18acf78e3\",\"name\":\"\",\"query\":\"\",\"rawOutput\":false,\"precision\":\"\",\"retentionPolicy\":\"\",\"org\":\"organisation\",\"x\":740,\"y\":760,\"wires\":[[\"e1327a4006ba5802\"]]},{\"id\":\"229e7a9670314daa\",\"type\":\"function\",\"z\":\"dff40f773b14f98a\",\"name\":\"Define Query\",\"func\":\"msg.query ='select time, value from verbrauch where time &gt; now() - ' + msg.payload;\\nreturn msg;\",\"outputs\":1,\"noerr\":0,\"initialize\":\"\",\"finalize\":\"\",\"libs\":[],\"x\":510,\"y\":760,\"wires\":[[\"99b2d04c0b6ac58a\",\"63458a37f6a9c260\"]]},{\"id\":\"5cfaa4df526f8f4b\",\"type\":\"debug\",\"z\":\"dff40f773b14f98a\",\"name\":\"debug 39\",\"active\":true,\"tosidebar\":true,\"console\":false,\"tostatus\":false,\"complete\":\"false\",\"statusVal\":\"\",\"statusType\":\"auto\",\"x\":1240,\"y\":800,\"wires\":[]},{\"id\":\"7e146baebb72cb66\",\"type\":\"inject\",\"z\":\"dff40f773b14f98a\",\"name\":\"\",\"props\":[{\"p\":\"payload\"},{\"p\":\"topic\",\"vt\":\"str\"}],\"repeat\":\"\",\"crontab\":\"00 03 * * *\",\"once\":true,\"onceDelay\":0.1,\"topic\":\"\",\"payload\":\"30d\",\"payloadType\":\"str\",\"x\":150,\"y\":760,\"wires\":[[\"27f5113d48cbe20f\"]]},{\"id\":\"27f5113d48cbe20f\",\"type\":\"ui_dropdown\",\"z\":\"dff40f773b14f98a\",\"name\":\"\",\"label\":\"\",\"tooltip\":\"\",\"place\":\"30 Tage\",\"group\":\"72a2229e7be23694\",\"order\":1,\"width\":6,\"height\":1,\"passthru\":true,\"multiple\":false,\"options\":[{\"label\":\"gestern\",\"value\":\"1d\",\"type\":\"str\"},{\"label\":\"1 Woche\",\"value\":\"1w\",\"type\":\"str\"},{\"label\":\"30 Tage\",\"value\":\"30d\",\"type\":\"str\"},{\"label\":\"90 Tage\",\"value\":\"90d\",\"type\":\"str\"},{\"label\":\"1 Jahr\",\"value\":\"365d\",\"type\":\"str\"}],\"payload\":\"\",\"topic\":\"topic\",\"topicType\":\"msg\",\"className\":\"\",\"x\":320,\"y\":760,\"wires\":[[\"229e7a9670314daa\"]]},{\"id\":\"72a2229e7be23694\",\"type\":\"ui_group\",\"name\":\"Verbrauch\",\"tab\":\"713a8ca9566dabdd\",\"order\":7,\"disp\":true,\"width\":\"6\",\"collapse\":false,\"className\":\"\"},{\"id\":\"3aa60ef18acf78e3\",\"type\":\"influxdb\",\"hostname\":\"127.0.0.1\",\"port\":\"8086\",\"protocol\":\"http\",\"database\":\"apidata\",\"name\":\"\",\"usetls\":false,\"tls\":\"\",\"influxdbVersion\":\"1.x\",\"url\":\"http:\/\/localhost:8086\",\"rejectUnauthorized\":true},{\"id\":\"713a8ca9566dabdd\",\"type\":\"ui_tab\",\"name\":\"Blog Beispiel\",\"icon\":\"dashboard\",\"disabled\":false,\"hidden\":false}]<\/pre>\n<p>Hat alles geklappt bekommen wir ein Balkendiagramm dieser Art:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-2201\" src=\"https:\/\/www.rustimation.eu\/wordpress\/wp-content\/uploads\/2023\/02\/2023-02-06-13_07_51-Centralina-\u2013-Mozilla-Firefox.png\" alt=\"\" width=\"796\" height=\"544\" srcset=\"https:\/\/www.rustimation.eu\/wordpress\/wp-content\/uploads\/2023\/02\/2023-02-06-13_07_51-Centralina-\u2013-Mozilla-Firefox.png 796w, https:\/\/www.rustimation.eu\/wordpress\/wp-content\/uploads\/2023\/02\/2023-02-06-13_07_51-Centralina-\u2013-Mozilla-Firefox-300x205.png 300w, https:\/\/www.rustimation.eu\/wordpress\/wp-content\/uploads\/2023\/02\/2023-02-06-13_07_51-Centralina-\u2013-Mozilla-Firefox-768x525.png 768w\" sizes=\"auto, (max-width: 796px) 100vw, 796px\" \/><\/p>\n<p>Das Datum \"seit Bef\u00fcllung\" wird extra berechnet und ist nicht Bestandteil des obigen JSON Codes.<\/p>\n<p style=\"padding-left: 40px;\"><span style=\"color: #999999;\">In meinem Node-Red Setup generiere ich den momentanen und t\u00e4glichen Gasverbrauch aus <span class=\"lang:default decode:true crayon-inline\">Modulation in %\u00a0 * Zeitdauer * Kesselleistung<\/span> . Die Modulation wird bei mir alle 90 Sekunden abgefragt: habe ich \u00fcber den Tag z.B. eine durchschnittliche Modulation von 30%, dann muss ich das nur mit den 19kWh des Kessels multiplizieren und erhalte dann einen Tagesenergieverbrauch von 0,3 * 19kWh * 24 = 136,8 kWh. Das kann ich dann je nach dem spezifischen Energiegehalt des Brennstoffs umrechnen in <\/span><br \/>\n<span style=\"color: #999999;\">z.B. Erdgas 136[kW] \/ 10,36 [kW\/m\u00b3] = 13m\u00b3 oder<\/span><br \/>\n<span style=\"color: #999999;\">Propangas 136[kW] \/ 7,3 [kW\/Liter] = 18,6 Liter GPL<\/span><br \/>\n<span style=\"color: #999999;\">Klingt zuerst einmal etwas umst\u00e4ndlich. Beim Momentanverbrauch ist diese Methode jedoch genauer &#8211; Viessmann aktualisiert den kWh Wert n\u00e4mlich nur jede Stunde.<\/span><\/p>\n<h3>Aggregierte Werte<\/h3>\n<p>Wie jede gute Datenbank bietet Influx auch die M\u00f6glichkeit, Werte nach Tagen, Wochen etc. zu aggregieren. Leider kommt uns jetzt der geringere Funktionsumfang der Version 1.8 in die Quere. Monatsweises aggregieren geht in der Datenbank nur mit Klimmz\u00fcgen, da das Zeitspannenliteral \"mo\" f\u00fcr Monat erst in 2.x eingef\u00fchrt wurde.<\/p>\n<p style=\"padding-left: 40px;\"><span style=\"color: #808080;\">Wir k\u00f6nnten uns zwar mit 30d behelfen, aber dann wird immer 30 tageweise von heute zur\u00fcck gez\u00e4hlt. Das w\u00e4re bei \"mo\" aber auch so.<br \/>\n<\/span><\/p>\n<p><span style=\"color: #808080;\"><span style=\"color: #000000;\">Wir k\u00f6nnen unsere Datenbank-Abfrage nat\u00fcrlich auch sehr aufw\u00e4ndig vorberechnen, damit sie als Monatswerte brav die Daten vom jeweils 1. bis zum Letzten eines Monats aufsummiert.<\/span> <\/span><\/p>\n<p><span style=\"color: #000000;\">Ich bin deshalb etwas anders vorgegangen und habe die monatlichen Werte erst im Nachgang aufsummiert. Hier das JSON daf\u00fcr:<br \/>\n<\/span><\/p>\n<pre class=\"lang:js decode:true\">[{\"id\":\"43b49e97b503b0b1\",\"type\":\"influxdb in\",\"z\":\"67a324f68c479a55\",\"influxdb\":\"3aa60ef18acf78e3\",\"name\":\"\",\"query\":\"\",\"rawOutput\":false,\"precision\":\"\",\"retentionPolicy\":\"\",\"org\":\"organisation\",\"x\":580,\"y\":560,\"wires\":[[\"79f6fde06c5cd284\",\"bd73e12a90fff014\"]]},{\"id\":\"a6b4270f2075c6dc\",\"type\":\"function\",\"z\":\"67a324f68c479a55\",\"name\":\"Define Query\",\"func\":\"msg.query ='select time, value from verbrauch where time &gt;(now()-365d)';\\nreturn msg;\",\"outputs\":1,\"noerr\":0,\"initialize\":\"\",\"finalize\":\"\",\"libs\":[],\"x\":330,\"y\":560,\"wires\":[[\"43b49e97b503b0b1\"]]},{\"id\":\"743603e0ef5c093c\",\"type\":\"debug\",\"z\":\"67a324f68c479a55\",\"name\":\"debug 24\",\"active\":true,\"tosidebar\":true,\"console\":false,\"tostatus\":false,\"complete\":\"payload\",\"targetType\":\"msg\",\"statusVal\":\"\",\"statusType\":\"auto\",\"x\":1080,\"y\":560,\"wires\":[]},{\"id\":\"8b2bf3e5276b66dc\",\"type\":\"inject\",\"z\":\"67a324f68c479a55\",\"name\":\"\",\"props\":[{\"p\":\"payload\"},{\"p\":\"topic\",\"vt\":\"str\"}],\"repeat\":\"\",\"crontab\":\"00 03 * * *\",\"once\":true,\"onceDelay\":0.1,\"topic\":\"\",\"payload\":\"\",\"payloadType\":\"date\",\"x\":130,\"y\":560,\"wires\":[[\"a6b4270f2075c6dc\"]]},{\"id\":\"79f6fde06c5cd284\",\"type\":\"function\",\"z\":\"67a324f68c479a55\",\"name\":\"make JSON\",\"func\":\"\\nconst mNum = [];            \/\/Array f\u00fcr Monatsnummern (Label)\\nconst mSum =[];             \/\/ Array f\u00fcrdie monatlichen Summen\\nvar series = [\\\"ltr\\\"];       \/\/ Beschriftung f\u00fcr Einheiit - ggf anpassen an eigene Verh\u00e4ltnisse    \\nvar mIdx = -1;              \/\/ Initialwert Monatsindex\\nvar lMonth = -1;            \/\/ Initialwert Nummer vergangenen Monats\\nvar month = -1;             \/\/ Initialwert f\u00fcr aktuellen Monat\\n\\n\\nfor (var i = 0; i &lt; msg.payload.length; i++) {          \/\/ Schleife \u00fcber alle Werte\\n    var thetime = Number(msg.payload[i].time);          \/\/ Auslesen der aktuellen Zeit und konvertieren in ms\\n    var d = new Date(thetime);                          \/\/ d ist das Datum des gelesenen Werts \\n    var month = d.getMonth() + 1;                       \/\/ getMonth() l\u00e4uft von 0 - 11 deshalb um 1 erh\u00f6hen\\n    \\n    if (month != lMonth) {                              \/\/ Neuer Monat? --&gt; initialisiere Monatswerte    \\n        mNum.push(month);                               \/\/ Monat in Array schieben\\n        mSum.push(0);                                   \/\/ Monatssumme nullen     \\n        lMonth = month;                                 \/\/ Monat und Monat des letzen Werts dind jetzt gleich    \\n        mIdx++;                                         \/\/ Monatsindex um 1 erh\u00f6hen    \\n        }\\n\\n    mSum[mIdx] = mSum[mIdx] + msg.payload[i].value;     \/\/ Wert im Monat aufsummieren\\n    mSum[mIdx] = Math.round(mSum[mIdx]*100)\/100;        \/\/ Monatswert runden\\n}\\n\/\/msg.payload = mSum;\\nvar mNumString = '[' + mNum + ']';                      \/\/ Monatsarray (enth\u00e4lt Label) mit Klammern umschlie\u00dfen\\nvar mNumJSON = JSON.parse(mNumString);                  \/\/ JSON Objekt generieren\\n\\nvar mSumString = '[[' + mSum +']]';                     \/\/ Wertearray (enth\u00e4lt Monatssummen) mit doppelten Klamern umschlie\u00dfen\\nvar mSumJSON = JSON.parse(mSumString);                  \/\/JSON Objekt generieren\\n\\nmsg.payload = [{\\\"series\\\":series, \\\"data\\\": mSumJSON, \\\"labels\\\": mNumJSON}]; \/\/ \u00dcbergabe an Barchart\\n\\nreturn msg;\",\"outputs\":1,\"noerr\":0,\"initialize\":\"\",\"finalize\":\"\",\"libs\":[],\"x\":910,\"y\":560,\"wires\":[[\"743603e0ef5c093c\",\"ce2a53df3b272481\"]]},{\"id\":\"ce2a53df3b272481\",\"type\":\"ui_chart\",\"z\":\"67a324f68c479a55\",\"name\":\"\",\"group\":\"3dbf824d0f14f7e7\",\"order\":1,\"width\":0,\"height\":0,\"label\":\"Liter GPL\",\"chartType\":\"bar\",\"legend\":\"false\",\"xformat\":\"HH:mm:ss\",\"interpolate\":\"linear\",\"nodata\":\"waiting\",\"dot\":false,\"ymin\":\"0\",\"ymax\":\"\",\"removeOlder\":1,\"removeOlderPoints\":\"\",\"removeOlderUnit\":\"3600\",\"cutout\":0,\"useOneColor\":true,\"useUTC\":false,\"colors\":[\"#1f77b4\",\"#aec7e8\",\"#ff7f0e\",\"#2ca02c\",\"#98df8a\",\"#d62728\",\"#ff9896\",\"#9467bd\",\"#c5b0d5\"],\"outputs\":1,\"useDifferentColor\":false,\"className\":\"\",\"x\":1100,\"y\":500,\"wires\":[[]]},{\"id\":\"3aa60ef18acf78e3\",\"type\":\"influxdb\",\"hostname\":\"127.0.0.1\",\"port\":\"8086\",\"protocol\":\"http\",\"database\":\"apidata\",\"name\":\"\",\"usetls\":false,\"tls\":\"\",\"influxdbVersion\":\"1.x\",\"url\":\"http:\/\/localhost:8086\",\"rejectUnauthorized\":true},{\"id\":\"3dbf824d0f14f7e7\",\"type\":\"ui_group\",\"name\":\"Monatsverbrauch letzte 365 Tage \",\"tab\":\"6e2e4833967b487c\",\"order\":3,\"disp\":true,\"width\":12,\"collapse\":false,\"className\":\"\"},{\"id\":\"6e2e4833967b487c\",\"type\":\"ui_tab\",\"name\":\"Stats\",\"icon\":\"dashboard\",\"order\":5,\"disabled\":false,\"hidden\":false}]<\/pre>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-2213 size-medium\" src=\"https:\/\/www.rustimation.eu\/wordpress\/wp-content\/uploads\/2023\/02\/2023-02-06-18_19_58-Centralina-\u2013-Mozilla-Firefox-300x205.png\" alt=\"\" width=\"300\" height=\"205\" srcset=\"https:\/\/www.rustimation.eu\/wordpress\/wp-content\/uploads\/2023\/02\/2023-02-06-18_19_58-Centralina-\u2013-Mozilla-Firefox-300x205.png 300w, https:\/\/www.rustimation.eu\/wordpress\/wp-content\/uploads\/2023\/02\/2023-02-06-18_19_58-Centralina-\u2013-Mozilla-Firefox-768x526.png 768w, https:\/\/www.rustimation.eu\/wordpress\/wp-content\/uploads\/2023\/02\/2023-02-06-18_19_58-Centralina-\u2013-Mozilla-Firefox.png 795w\" sizes=\"auto, (max-width: 300px) 100vw, 300px\" \/><\/p>\n<h4>Daten f\u00fcr Aggregation anreichern<\/h4>\n<p>Wenn man vorhat, Daten \u00fcber bestimmte Zeitspannen zu aggregieren, sollte man immer noch einen Datumswert wie Tag, Monat und\/oder Jahr mitgeben.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-2207\" src=\"https:\/\/www.rustimation.eu\/wordpress\/wp-content\/uploads\/2023\/02\/2023-02-06-17_18_22-Node-RED-_-192.168.178.24-\u2013-Mozilla-Firefox.png\" alt=\"\" width=\"776\" height=\"154\" srcset=\"https:\/\/www.rustimation.eu\/wordpress\/wp-content\/uploads\/2023\/02\/2023-02-06-17_18_22-Node-RED-_-192.168.178.24-\u2013-Mozilla-Firefox.png 776w, https:\/\/www.rustimation.eu\/wordpress\/wp-content\/uploads\/2023\/02\/2023-02-06-17_18_22-Node-RED-_-192.168.178.24-\u2013-Mozilla-Firefox-300x60.png 300w, https:\/\/www.rustimation.eu\/wordpress\/wp-content\/uploads\/2023\/02\/2023-02-06-17_18_22-Node-RED-_-192.168.178.24-\u2013-Mozilla-Firefox-768x152.png 768w\" sizes=\"auto, (max-width: 776px) 100vw, 776px\" \/><\/p>\n<p>Der Datensatz wird dazu wie folgt vorbereitet:<\/p>\n<pre class=\"lang:js decode:true\">var aussen = msg.payload\r\nconst d = new Date();\r\nlet month = d.getMonth() +1; \/\/Monate laufen mit der getMonth() Methode von 0-11\r\nlet year = d.getFullYear();\r\n\r\nmsg.payload = {\r\n    location: 'Nordseite',\r\n    _jahr: year,\r\n    _monat: month,\r\n    gradc: aussen\r\n}\r\nreturn msg;<\/pre>\n<p>Lustigerweise stellt Influx die Tabellenspalten in alphabetischer Reihenfolge dar. Damit Monat und Jahr zusammenbleiben, habe ich die Tags mit einem Unterstrich versehen, das ist nat\u00fcrlich Geschmackssache.<\/p>\n<pre class=\"lang:default decode:true\">&gt; select * from temperatur\r\nname: temperatur\r\ntime                _jahr _monat gradc location\r\n----                ----- ------ ----- --------\r\n1675701285518130854 2023  2      15    Nordseite\r\n1675701286380357650 2023  2      19    Nordseite\r\n1675701287514756318 2023  2      6     Nordseite\r\n1675701287702260310 2023  2      19    Nordseite\r\n&gt;\r\n<\/pre>\n<h3>Aggregierte Werte direkt aus Influx<\/h3>\n<p>Funktioniert gerade heraus recht einfach mit den Zeitspannen, welche Influx als Zeitliterale versteht. Hat man zum Beispiel viele Messwerte \u00fcber den Tag hinweg gespeichert, und m\u00f6chte Tagessummen auswerten, dann geht das mit folgendem Befehl (hier f\u00fcr den Solarertrag):<\/p>\n<pre class=\"lang:js decode:true\">msg.query = 'select SUM(\"value\") from \"rawYield\" WHERE time &gt; (now() - ' + msg.payload + ') GROUP BY time(1d)';\r\nreturn msg;<\/pre>\n<p>Heraus kommt das Solarertragsdiagramm weiter oben.<\/p>\n<p>Das Aggregieren, z.B. nach dem <em>Tag<\/em> \"Monat\" den wir oben mit abgespeichert haben, geht nat\u00fcrlich genau so.<\/p>\n<pre class=\"lang:js decode:true\">msg.query = 'select SUM(\"value\") from \"rawYield\" WHERE time &gt; (now() - ' + msg.payload + ') GROUP BY _monat';\r\nreturn msg;<\/pre>\n<h3>Null Uhr?<\/h3>\n<p>Hat man ein Chart erstellt, das auf mit 1d oder \u00e4hnlich aggregierten Werten beruht, wird man feststellen, dass beim Abfahren des Graphen mit dem Cursor keineswegs 0:00 Uhr als Zeitpunkt der Messung angezeigt wird, sondern 1:00 Uhr oder bei Sommerzeit 2:00 Uhr. Das liegt daran, dass Influx intern immer mit Universal Time Coordinated [UTC] arbeitet &#8211; fr\u00fcher auch Greenwich Mean Time [GMT] genannt.<\/p>\n<p>Bei Solarertr\u00e4gen ist das unerheblich, bei anderen Messungen z.B. Stromverbr\u00e4uchen etc. eher ein Problem, man m\u00f6chte seine Verbr\u00e4uche schlie\u00dflich klar abgrenzen.<\/p>\n<p>Dem hilfst du ab, indem du noch die Zeitzone an die Abfrage hinten anh\u00e4ngst.<\/p>\n<p>Im Command Line Interface zum Beispiel so:<\/p>\n<pre class=\"lang:default decode:true\">select SUM(\"measurement\") from \"bucket\" WHERE time &gt; (now() - 1d) GROUP BY time(1d) tz('Europe\/Berlin')\r\n<\/pre>\n<p>und in der Node Red Query fast genauso. Zus\u00e4tzlich musst du die einfachen Anf\u00fchrungszeichen bei der Ortsangabe mit einem Backslash \"escapen\".<\/p>\n<pre class=\"lang:default decode:true\">msg.query = 'select SUM(\"value\") from \"rawYield\" WHERE time &gt; (now() - ' + msg.payload + ') GROUP BY time(1d) tz(\\'Europe\/Berlin\\')';<\/pre>\n<p>Bei Abfrage mit aggregierten Werten, also z.B. _day, musst du derlei Zeitzonen Klimmz\u00fcge nicht machen.<\/p>\n<h3>Das now() Problem &#8211; tageweise Abgrenzung<\/h3>\n<p>Der \"Jetzt\" Wert <span class=\"lang:js decode:true crayon-inline \">now()<\/span>\u00a0 macht genau das, was der Name bedeutet: er zieht das Datum und die Uhrzeit von gerade eben heran, also z.B. den 20.11.2023 10:15:00<\/p>\n<p>Das ist nicht immer gew\u00fcnscht, vor allem dann, wenn ich eine genau tageweise Abgrenzung haben will. Mit <span class=\"lang:js decode:true crayon-inline\">time &gt; (now() &#8211; 1w)<\/span> liefert mir Influx, unter Zugrundelegung obigen Datums,\u00a0 Daten vom 13.11.2023 10:15:00 bis heute, 20.11.2023 10:15:00. Der erste Tag ist also unvollst\u00e4ndig. Der letzte Tag auch, aber das liegt logischerweise daran, dass heute noch nicht vorbei ist \ud83d\ude09<\/p>\n<p>Bei Influx 1.8 habe ich f\u00fcr das CLI noch keine M\u00f6glichkeit gefunden, das Problem mit Influx Hausmitteln zu l\u00f6sen. Da wir unsere Daten ja sowieso mit Javascript vorbereiten, k\u00f6nnen wir das dort aber recht einfach l\u00f6sen:<\/p>\n<p>Im vorgeschalteten Query Setup Node, der die Datenbankabfrage zusammenstellt, m\u00fcssen wir lediglich einen zuverl\u00e4ssigen Ersatz f\u00fcr now() erzeugen, d.h. Null Uhr des heutigen Tages als Anker, von dem aus wir die Anzahl Tage zur\u00fcckgehen, \u00fcber welche die Auswertung laufen soll.<\/p>\n<pre class=\"lang:js decode:true\">const d = new Date();\r\nmsg.today = d.setHours(0, 0, 0, 0); \/\/ last midnight in ms\r\nmsg.today = msg.today + \"000000\"<\/pre>\n<p>In <em>msg.today<\/em> steckt jetzt der Startwert &#8211; d.h heute fr\u00fch Null Uhr, erst einmal\u00a0 auf die MIllisekunde genau. Da Influx (bl\u00f6derweise) mit Nanosekunden arbeitet, m\u00fcssen wir noch eine Million dranh\u00e4ngen, d.h. noch 6 Nullen. Das geht problemlos als String; Javascript bzw NR ist in Bezug auf gemischte Zahl- und Stringwerte sehr tolerant.<\/p>\n<p>Der Query String wird jetzt aufgrund der in den String einzuf\u00fcgenden <em>msg.today<\/em> und <em>msg.payload<\/em> etwas aufwendiger:<\/p>\n<pre class=\"lang:js decode:true\">msg.query = 'select SUM(sum) from hourpvprod WHERE time &gt; (' + msg.today + ' - '  + msg.payload +') GROUP BY time(1d) tz(\\'Europe\/Berlin\\')';<\/pre>\n<p>(hourpvprod ist eine Tabelle mit aggregierten Stundenwerten, der Solaranlage)<\/p>\n<p>Mit dieser Abfrage habt ihr eine tagesgenaue Auswertung zur Hand.<\/p>\n<p><span style=\"color: #999999;\">Dieser Beitrag wurde inspiriert von <\/span><a href=\"https:\/\/funprojects.blog\/2020\/02\/01\/influxdb-with-node-red\/\" target=\"_blank\" rel=\"noopener\"><span style=\"color: #999999;\">https:\/\/funprojects.blog\/2020\/02\/01\/influxdb-with-node-red\/<\/span><\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Um es vorweg zu sagen, ich besch\u00e4ftige mich in diesem Artikel mit Influx zur Erzeugung von Diagrammen ausschlie\u00dflich in Node-Red. Es gibt auch andere, leistungsf\u00e4higere Diagramml\u00f6sungen wie Grafana. Die dabei zu durchlaufende Lernkurve ist allerdings sehr steil und au\u00dferdem haben wir anschlie\u00dfend ein weiteres System an der Hand, so dass wir zwischen Diagrammen und Einstellungen &hellip; <a href=\"https:\/\/www.rustimation.eu\/index.php\/viessmann-api-und-node-red-teil-6b\/\" class=\"more-link\"><span class=\"screen-reader-text\">Viessmann API und Node-Red &#8211; Teil 6b &#8211; Influx Diagramme<\/span> weiterlesen <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1,175,53,151,159],"tags":[180,176,126,174,170],"class_list":["post-2119","post","type-post","status-publish","format-standard","hentry","category-allgemein","category-influx-db","category-iot","category-node-red","category-viessmann-api","tag-diagramme","tag-influx-version-1-8","tag-node-red","tag-viessmann-api","tag-visualisieren"],"_links":{"self":[{"href":"https:\/\/www.rustimation.eu\/index.php\/wp-json\/wp\/v2\/posts\/2119","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.rustimation.eu\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.rustimation.eu\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.rustimation.eu\/index.php\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.rustimation.eu\/index.php\/wp-json\/wp\/v2\/comments?post=2119"}],"version-history":[{"count":1,"href":"https:\/\/www.rustimation.eu\/index.php\/wp-json\/wp\/v2\/posts\/2119\/revisions"}],"predecessor-version":[{"id":3681,"href":"https:\/\/www.rustimation.eu\/index.php\/wp-json\/wp\/v2\/posts\/2119\/revisions\/3681"}],"wp:attachment":[{"href":"https:\/\/www.rustimation.eu\/index.php\/wp-json\/wp\/v2\/media?parent=2119"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.rustimation.eu\/index.php\/wp-json\/wp\/v2\/categories?post=2119"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.rustimation.eu\/index.php\/wp-json\/wp\/v2\/tags?post=2119"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}