Jetzt beginnt der schönere Teil. Hat man einmal die wesentlichen Datenpunkte gefunden kann man diese ganz einfach über einen MQTT In Node in Node-Red verarbeiten.
Inhalt
Node-Red
Bevor ihr allerdings eure komplette API Kopplung löscht, empfehle ich, erst einmal ein Backup zu machen. Des Weiteren würde ich in einem ersten Schritt, die Optolink Geschichte in einem eigenen Tab ausprobieren und schauen, ob die angezeigten Optolink Werte tatsächlich mit den API Werten übereinstimmen. Bei Außentemperatur, Speichertemperatur und ein paar anderen liefert Viessmann die Werte zeitverzögert aus. Es muss also nicht immer zu 100% übereinstimmen.
Einfache Werte auslesen
Zum Glück kommen die meisten Werte verzögerungsfrei aus dem System – egal ob mit Optolink oder der API ausgelesen. Hier ein Beispiel für dieWarmwassertemperatur:
Der MQTT In Node erhält das Topic, das wir in der Polling Liste von settings_ini.py für Warmwassertemperatur definiert haben. Die Quality of Service definiere ich persönlich mit "1" was bei wackeligen Verbindungen etwas mehr Betriebssicherheit bietet – wie ich meine.
Bei "Ausgang" empfehle ich "String" zu verwenden. Natürlich geht auch "Auto-Erkennung", meistens jedenfalls. Bei Hexadezimal Werten allerdings nicht.
Weiterverarbeitung
Normalerweise können wir wie oben direkt ein Dashboard Element an den MQTT In Node dranhängen.
Manchmal jedoch können wir Dashboard Switches nicht 1 zu 1 übernehmen, da die API mit "on" oder "off" Werten arbeitet und Optolink mit 1 oder 0. Hier entsprechend den Dashboard Node anpassen.
Zu viele Werte
Die Außentemperatur wird von Viessmann in der API nur ca. jede Stunde oder bei größeren Änderungen aktualisiert. Wir haben jetzt die Möglichkeit, die Temperatur alle 30 Sekunden abzugreifen. Will man diesen Wert aber in Influx speichern, ist das ein Overkill. Anstatt einer schönen Temperaturlinie gibt es ein zittriges Band von Werten.
Ich habe mir so beholfen:
Nach dem MQTT In Node kommt ein Delay Node, der nur alle 10 Minuten einen Wert durchlässt sowie ein Filter Node, der nur bei Wertänderungen die payload.msg weiterleitet. Das spart etwas Platz in der Datenbank. Ferner benutze ich das Topic Vitodens/AussenTemp_fltrd 0x5525 und nicht den in Phil's Original Polling Liste ebenfalls verfügbaren Wert bei 0x800.
Alternativ könnten wir die Temperatur nicht über die Pollingliste sondern alle x Minuten direkt auslesen. Weiter unten steht mehr dazu.
Hexadezimale Werte – lauter kleine Endians
Endian hat nichts mit den amerikanischen Ureinwohnern zu tun, sondern mit Gullivers Reisen und der Art, wie man gekochte Eier aufschlägt 🙂
Informatiker können dieses Kapitel gerne überspringen…
Einige Werte sind weder Integer noch Float sondern hexadezimal. Zum Beispiel die Verbrauchswerte [kWh]. Bei Gasgeräten stehten diese in der Gegend von 0x9000 bei Wärmepumpen und anderer moderner Technologie wahrscheinlich an einer anderen Stelle.
In der Polling Liste werden diese als "raw" ausgelesen:
("HeizgVerbrTag", 0x9000, 32, 'raw'),
MQTT spuckt dann für 8 Tage Verbrauchsdaten z.B. folgendes aus: 2A00000004000000000000000d000000180000000e0000000000000000000000
So etwas erschließt sich nicht von selbst. Der Hex String ist 64 Zeichen lang, je Byte sind das zwei Zeichen, also insgesamt 32 Byte, somit 4 Byte pro Tag.
Die 4 Byte (8 Zeichen) ganz links stehen für "heute".
Was man jetzt beachten muss, ist, dass die 4 Byte nach der "Little Endian" Notation da stehen. Das heißt, das niederwertigste Byte kommt zuerst, dann das nächst höherwertige und so fort. Auf das Dezimalsystem übertragen hieße das, die Zahl '3456' wird als '6543 dargestellt. Also Einer zuerst, dann Zehner, dann Hunderter, dann Tausender.
Nach dieser Einleitung wissen wir hoffentlich, wie die ersten 4 Byte zu interpretieren sind:, nämlich als
0000002A
oder dezimal 42 – die Antwort auf 'Life, the Universe And Everything'. Das heißt, wir haben heute einen Bespielverbrauch von 42 kWh gehabt.
Da diese Hex Werte für Nicht-Informatiker wie mich schwer zu verdauen sind, habe ich spaßeshalber einmal Chat GPT gefragt, ob es mir nicht eine Aufdrösel-Routine zur Verfügung stellen könnte. Frage war: "Ich brauche ein Javascript Skript, das eine lange Kette von 4 Byte hexadezimal Werten zerlegt und unter Berücksichtigung von Little Endian in Dezimalwerte umwandelt." Und siehe da, es hat geklappt: Auf einen Node Red Funktionsknoten angepasst sieht das so aus:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
// original JS code generated by ChatGPT - adapted to NR Function Node let hexString = msg.payload; // Entfernt Leerzeichen und konvertiert in Kleinbuchstaben hexString = hexString.replace(/\s+/g, '').toLowerCase(); // Überprüft, ob die Länge der Hexadezimalzeichenkette ein Vielfaches von 8 ist (4 Bytes = 8 Hex-Zeichen) if (hexString.length % 8 !== 0) { throw new Error("Hexadezimalzeichenkette muss ein Vielfaches von 8 Zeichen sein."); } const decimalValues = []; // Zerlegt die Hexadezimalzeichenkette in 8-stellige Stücke (4 Byte) for (let i = 0; i < hexString.length; i += 8) { let hexChunk = hexString.slice(i, i + 8); // Zerlegt das 4-Byte-Hexadezimalstück in Paare von 2 Hex-Zeichen (1 Byte) und dreht es um let littleEndianChunk = hexChunk.match(/../g).reverse().join(''); // Konvertiert in einen Dezimalwert let decimalValue = parseInt(littleEndianChunk, 16); decimalValues.push(decimalValue); } msg.payload = decimalValues; return msg; |
Schön kommentieren kann die KI auch noch.
Ich möchte allerdings nicht wissen, wieviel Watt Strom durch diese Anfrage verbraucht wurden. Kein Wunder, dass Google und Amazon inzwischen wieder Atomkraftwerke reaktivieren, um die unnatürliche Intelligenz zu betreiben.
Füttert man den "Hex String 2 Decimal" Function Node mit dem ganzen Output von MQTT, kommt folgendes Array heraus:
Analog können wir bei den anderen Verbrauchwerten vorgehen. Aber Achtung: Optolink liefert aus irgend welchen Gründen gwöhnlich nur 55 valide Byte. Der Rest ist dann einfach "00". Der Wochenverbrauch (53 Wochen) ist 212 Byte lang und müsste in 4 Teile gestückelt werden. Siehe Byte-Bitweiser Zugriff direkt unterhalb.
Byte-Bitweiser Zugriff
Wem das in diesem Kapitel Beschriebene zu kompliziert (oder zu langatmig) ist, kann auch einfach Byte-Bitweise auf die Werte zugreifen. Der heutige Verbrauch wäre dann so zu konfigurieren:
("HeizgVerbr_heute", 0x9000, 32, 'b:0:3', 1), (lese ab 0x9000 die Bytes 0 bis 3 und konvertiere in Dezimal ohne zu skalieren)
Der gestrige Verbrauch wäre demnach so einzustellen
("HeizgVerbr_gestern", 0x9000, 32, 'b:4:7, 1), und so weiter.
Die Frage dabei ist, macht es Sinn, MQTT alle 30 Sekunden mit einer Abfrage zu beaufschlagen, deren Ergebnis sich nur sehr selten ändert? Eher nein, weshalb ich für derlei Abfragen anders vorgehe. Siehe unten.
Schreiben von Werten
Während das Auslesen von gepollten Werten sehr komfortabel ist, muss man zum Schreiben mit MQTT die betreffenden Speicheradressen des Viessmann Geräts ansprechen. Hier ein Beispiel zum Setzen der Solltemperatur in Heizkreis 2:
Die Solltemperatur von Heizkreis 2 liegt bei meiner Therme auf 0x3306 und nennt sich BedienRTSolltemperaturM2~0x3306. RT steht für "Raumthermostat".
Nun basteln wir uns die Payload zusammen, die wir mittels MQTT Out Node an das Topic Vitodens/cmnd schicken:
1 2 3 4 5 6 |
let addr = "0x3306"; // Adresse let bytes=1; // Länge byte var setTo = "23"; // Soll Wert let message= "write;" + addr + ";" + bytes + ";" + setTo; msg.payload = message; return msg; |
Wie schon in einem Vissmann API Beitrag geschrieben, habe ich das ganze etwas betriebsicherer gemacht. Der Vollständigkeit halber hier noch der komplette Flow dazu
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 |
[ { "id": "4c1996e23928f78f", "type": "tab", "label": "Flow 2", "disabled": false, "info": "", "env": [] }, { "id": "d7fd93f2be840b73", "type": "mqtt in", "z": "4c1996e23928f78f", "name": "", "topic": "Vitodens/SollTempTagFBH", "qos": "1", "datatype": "auto-detect", "broker": "c3ec39dc.4614f", "nl": false, "rap": true, "rh": 0, "inputs": 0, "x": 310, "y": 220, "wires": [ [ "4ca4c30250a613a0" ] ] }, { "id": "4ca4c30250a613a0", "type": "ui_slider", "z": "4c1996e23928f78f", "name": "Slider sets temperature", "label": "FBH Tag °C", "tooltip": "", "group": "f0f24855fdba9035", "order": 2, "width": 5, "height": 1, "passthru": false, "outs": "end", "topic": "topic", "topicType": "msg", "min": "5", "max": "30", "step": 1, "className": "", "x": 570, "y": 220, "wires": [ [ "575d48fd94562405" ] ], "icon": "node-red-contrib-sensor-ds18b20/thermometer.png" }, { "id": "575d48fd94562405", "type": "change", "z": "4c1996e23928f78f", "name": "sliFN", "rules": [ { "t": "set", "p": "sliFN", "pt": "flow", "to": "payload", "tot": "msg" } ], "action": "", "property": "", "from": "", "to": "", "reg": false, "x": 770, "y": 220, "wires": [ [] ] }, { "id": "502a6f02d5e9f75d", "type": "ui_button", "z": "4c1996e23928f78f", "name": "OK", "group": "f0f24855fdba9035", "order": 3, "width": 1, "height": 1, "passthru": false, "label": "", "tooltip": "", "color": "yellow", "bgcolor": "#333333", "className": "", "icon": "fa-2x fa-check-square-o ", "payload": "sliFN", "payloadType": "flow", "topic": "topic", "topicType": "msg", "x": 250, "y": 280, "wires": [ [ "3b440720c331f722" ] ] }, { "id": "3b440720c331f722", "type": "function", "z": "4c1996e23928f78f", "name": "Solltemp FBH Tag", "func": "let addr = \"0x3306\";\nlet bytes=1;\nvar setTo = flow.get('sliFN');\nlet message= \"write;\" + addr + \";\" + bytes + \";\" + setTo;\nmsg.payload = message;\nreturn msg;\n", "outputs": 1, "timeout": "", "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 430, "y": 280, "wires": [ [ "48ee9832dc0a347b" ] ] }, { "id": "48ee9832dc0a347b", "type": "mqtt out", "z": "4c1996e23928f78f", "name": "", "topic": "Vitodens/cmnd", "qos": "1", "retain": "", "respTopic": "", "contentType": "", "userProps": "", "correl": "", "expiry": "", "broker": "c3ec39dc.4614f", "x": 640, "y": 280, "wires": [] }, { "id": "c3ec39dc.4614f", "type": "mqtt-broker", "name": "CentralinaPi", "broker": "192.168.1.25", "port": "1883", "clientid": "", "autoConnect": true, "usetls": false, "compatmode": false, "protocolVersion": "4", "keepalive": "60", "cleansession": true, "autoUnsubscribe": true, "birthTopic": "", "birthQos": "0", "birthPayload": "", "birthMsg": {}, "closeTopic": "", "closeQos": "0", "closePayload": "", "closeMsg": {}, "willTopic": "", "willQos": "0", "willPayload": "", "willMsg": {}, "userProps": "", "sessionExpiry": "" }, { "id": "f0f24855fdba9035", "type": "ui_group", "name": "Einstellung FBH Anbau", "tab": "7e5ee24d.2d5ae4", "order": 5, "disp": true, "width": "6", "collapse": false, "className": "" }, { "id": "7e5ee24d.2d5ae4", "type": "ui_tab", "name": "Heizung/WW", "icon": "dashboard", "order": 3, "disabled": false, "hidden": false } ] |
Direkte Abfragen
Wir oben schon erwähnt, belastet das unnötige Abfragen von Werten, die sich nur einmal am Tag oder gar seltener ändern, unseren MQTT Server – zumindest dann, wenn er noch diverse andere MQTT Publisher Clients bedienen muss.
Ich habe ca. 10 Shelly Aktoren, 2 Wechselrichter, ein Smartmeter, Ladegerät, Batterieüberwachung, div. WiFi Thermometer die alle fleißig mit dem MQTT Broker kommunizieren. Da kommt der ganz schön ins Zappeln. Deshalb auch die Beschränkung auf 30 Sekunden Abfrageintervalle beim Optolink-Splitter.
Es bietet sich daher an, zeitgesteuert direkte Abfragen zu starten ohne, dass diese in der Pollingliste drin stehen. Das geschieht analog der manuellen Testeingabe im MQTT Explorer.
Wollen wir z.B. die Verbrauchwerte der letzten 12+1 Monate wissen, können wir das bei 0x90F4 abfragen.
Der Funktionsnode wird wie folgt befüllt:
1 2 3 4 5 6 7 |
let addr = "0x90F4"; let bytes=52; let type = "raw"; let message= "read;" + addr + ";" + bytes + ";" + type; msg.payload = message; return msg; |
Die erzeugte Payload wird an einen MQTT Out Node geschickt, welcher die Abfrage durchführt und den Wert am MQTT Server ablegt. Hier werden wie beim Zugriff über den MQTT Client Semikola als Trenner verwendet.
Gelesen wird die Abfrage wieder über einen MQTT In Node, diesmal beim Topic Vitodens/resp Dieses Topic haben wir beim initalen Anpassen der settings_ini.py Datei so definiert.
1;37108;38000000000000000000000000000000000000003d0000007501000077020000fd000000ae040000900400009400000012000000
Die Antwort müssen wir noch zerlegen, da der Optolink Splitter immer noch das Ergebnis der Abfrage und den Dezimalwert der Adresse mitliefert. Uns interessiert der Wert nach dem zweiten Semikolon:
1 2 3 |
const myArray = msg.payload.split(";") msg.payload = myArray[2]; return msg; |
Das gesamte Konstrukt für direkte Abfragen sieht dann so aus:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 |
[ { "id": "4c1996e23928f78f", "type": "tab", "label": "Flow 2", "disabled": false, "info": "", "env": [] }, { "id": "d7fd93f2be840b73", "type": "mqtt in", "z": "4c1996e23928f78f", "name": "", "topic": "Vitodens/resp", "qos": "1", "datatype": "utf8", "broker": "c3ec39dc.4614f", "nl": false, "rap": true, "rh": 0, "inputs": 0, "x": 190, "y": 340, "wires": [ [ "a7dfadeeb2f3754c" ] ] }, { "id": "3b440720c331f722", "type": "function", "z": "4c1996e23928f78f", "name": "Einzelwert erzeugen", "func": "let addr = \"0x90F4\";\nlet bytes=52;\nlet type = \"raw\";\n\nlet message= \"read;\" + addr + \";\" + bytes + \";\" + type;\nmsg.payload = message;\nreturn msg;\n", "outputs": 1, "timeout": "", "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 440, "y": 280, "wires": [ [ "48ee9832dc0a347b" ] ] }, { "id": "48ee9832dc0a347b", "type": "mqtt out", "z": "4c1996e23928f78f", "name": "", "topic": "Vitodens/cmnd", "qos": "1", "retain": "", "respTopic": "", "contentType": "", "userProps": "", "correl": "", "expiry": "", "broker": "c3ec39dc.4614f", "x": 640, "y": 280, "wires": [] }, { "id": "0059ec9495bf299f", "type": "inject", "z": "4c1996e23928f78f", "name": "", "props": [ { "p": "payload" }, { "p": "topic", "vt": "str" } ], "repeat": "", "crontab": "", "once": false, "onceDelay": 0.1, "topic": "", "payload": "", "payloadType": "date", "x": 200, "y": 280, "wires": [ [ "3b440720c331f722" ] ] }, { "id": "a7dfadeeb2f3754c", "type": "function", "z": "4c1996e23928f78f", "name": "Output filtern", "func": "const myArray = msg.payload.split(\";\")\nmsg.payload = myArray[2];\nreturn msg;", "outputs": 1, "timeout": 0, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 410, "y": 340, "wires": [ [ "0892534f190c5d89" ] ] }, { "id": "0892534f190c5d89", "type": "debug", "z": "4c1996e23928f78f", "name": "debug 206", "active": true, "tosidebar": true, "console": false, "tostatus": false, "complete": "false", "statusVal": "", "statusType": "auto", "x": 630, "y": 340, "wires": [] }, { "id": "c3ec39dc.4614f", "type": "mqtt-broker", "name": "CentralinaPi", "broker": "192.168.1.25", "port": "1883", "clientid": "", "autoConnect": true, "usetls": false, "compatmode": false, "protocolVersion": "4", "keepalive": "60", "cleansession": true, "autoUnsubscribe": true, "birthTopic": "", "birthQos": "0", "birthPayload": "", "birthMsg": {}, "closeTopic": "", "closeQos": "0", "closePayload": "", "closeMsg": {}, "willTopic": "", "willQos": "0", "willPayload": "", "willMsg": {}, "userProps": "", "sessionExpiry": "" } ] |
Mehrere direkte Abfragen
Hat man mehrere direkte Abfragen gebaut, braucht man nur einen MQTT-In Node, der alle direkten Abfragen empfängt. Um die Antwort dann richtig zuordnen zu können, muss anschließend noch über den zweiten Teil der Antwort, die zurückgegebene Dezimaladresse (hier 37108) gefiltert werden.
Wer mag, kann in der Response auch eine Hex Adresse anzeigen lassen – wie in nachfolgendem Beispiel: Dazu den Kommentar in der Zeile resp_addr_format = in settings_ini.py beachten.
Dieses Beispiel erzeugt zwei direkte Abfragen – Außentemperatur und tageweiser Verbrauch – welche anschließend über einen Response Filter zugeordnet werden.
1 |
[ { "id": "bc6d8a5de8a30b47", "type": "function", "z": "e97d31eeea2066ef", "name": "AussenTemp direkt", "func": "let addr = \"0x5525\";\nlet bytes=2;\nlet type = \"0.1\";\nlet sInt = \"true\" // must be string!\n\nlet message= \"read;\" + addr + \";\" + bytes + \";\" + type +\";\" + sInt;\nmsg.payload = message;\nreturn msg;", "outputs": 1, "timeout": 0, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 490, "y": 1040, "wires": [ [ "8a952b7955f4b3a1" ] ] }, { "id": "8a952b7955f4b3a1", "type": "mqtt out", "z": "e97d31eeea2066ef", "name": "", "topic": "Vitodens/cmnd", "qos": "1", "retain": "", "respTopic": "", "contentType": "", "userProps": "", "correl": "", "expiry": "", "broker": "c3ec39dc.4614f", "x": 740, "y": 1040, "wires": [] }, { "id": "6f518672b8bed8af", "type": "inject", "z": "e97d31eeea2066ef", "name": "", "props": [ { "p": "payload" }, { "p": "topic", "vt": "str" } ], "repeat": "", "crontab": "", "once": false, "onceDelay": 0.1, "topic": "", "payload": "", "payloadType": "date", "x": 260, "y": 1040, "wires": [ [ "bc6d8a5de8a30b47" ] ] }, { "id": "8ed7e26a45d70242", "type": "mqtt in", "z": "e97d31eeea2066ef", "name": "", "topic": "Vitodens/resp", "qos": "1", "datatype": "auto-detect", "broker": "c3ec39dc.4614f", "nl": false, "rap": true, "rh": 0, "inputs": 0, "x": 250, "y": 1180, "wires": [ [ "c14e88e582990e27", "630376fa15821840" ] ] }, { "id": "c14e88e582990e27", "type": "debug", "z": "e97d31eeea2066ef", "name": "debug 211", "active": true, "tosidebar": true, "console": false, "tostatus": false, "complete": "false", "statusVal": "", "statusType": "auto", "x": 430, "y": 1240, "wires": [] }, { "id": "1a4deac5bc09126b", "type": "function", "z": "e97d31eeea2066ef", "name": "Verbrauch direkt raw", "func": "let addr = \"0x9000\";\nlet bytes=32;\nlet type = \"raw\";\nlet sInt = 1 \n\nlet message= \"read;\" + addr + \";\" + bytes + \";\" + type +\";\" + sInt;\nmsg.payload = message;\nreturn msg;", "outputs": 1, "timeout": 0, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 500, "y": 1100, "wires": [ [ "a9ee097b0378dff8" ] ] }, { "id": "a9ee097b0378dff8", "type": "mqtt out", "z": "e97d31eeea2066ef", "name": "", "topic": "Vitodens/cmnd", "qos": "1", "retain": "", "respTopic": "", "contentType": "", "userProps": "", "correl": "", "expiry": "", "broker": "c3ec39dc.4614f", "x": 740, "y": 1100, "wires": [] }, { "id": "265e0718be4cf0b0", "type": "inject", "z": "e97d31eeea2066ef", "name": "", "props": [ { "p": "payload" }, { "p": "topic", "vt": "str" } ], "repeat": "", "crontab": "", "once": false, "onceDelay": 0.1, "topic": "", "payload": "", "payloadType": "date", "x": 260, "y": 1100, "wires": [ [ "1a4deac5bc09126b" ] ] }, { "id": "3abe9cdefd8d2b8c", "type": "function", "z": "e97d31eeea2066ef", "name": "hexStringToDecimal", "func": "//let hexString = \"00000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\"\nlet hexString = msg.payload[2];\n//let len = 0\n\n // Entfernt Leerzeichen und konvertiert in Kleinbuchstaben\n hexString = hexString.replace(/\\s+/g, '').toLowerCase();\n // Überprüft, ob die Länge der Hexadezimalzeichenkette ein Vielfaches von 8 ist (4 Bytes = 8 Hex-Zeichen)\n //len = hexString.length % 8;\n if (hexString.length % 8 !== 0) {\n throw new Error(\"Hexadezimalzeichenkette muss ein Vielfaches von 8 Zeichen sein.\");\n }\n\n const decimalValues = [];\n\n // Zerlegt die Hexadezimalzeichenkette in 8-stellige Stücke (4 Byte)\n for (let i = 0; i < hexString.length; i += 8) {\n let hexChunk = hexString.slice(i, i + 8);\n \n // Zerlegt das 4-Byte-Hexadezimalstück in Paare von 2 Hex-Zeichen (1 Byte) und dreht es um\n let littleEndianChunk = hexChunk.match(/../g).reverse().join('');\n \n // Konvertiert in einen Dezimalwert\n let decimalValue = parseInt(littleEndianChunk, 16);\n decimalValues.push(decimalValue);\n }\nmsg.payload = decimalValues;\n//msg.payload = len\n return msg;\n", "outputs": 1, "timeout": 0, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 800, "y": 1200, "wires": [ [ "0ab30195e3db7c98" ] ] }, { "id": "0ab30195e3db7c98", "type": "debug", "z": "e97d31eeea2066ef", "name": "debug 212", "active": true, "tosidebar": true, "console": false, "tostatus": false, "complete": "false", "statusVal": "", "statusType": "auto", "x": 1010, "y": 1200, "wires": [] }, { "id": "f3ca7cea508856fc", "type": "switch", "z": "e97d31eeea2066ef", "name": "Filter", "property": "payload[1]", "propertyType": "msg", "rules": [ { "t": "eq", "v": "0x5525", "vt": "str" }, { "t": "eq", "v": "0x9000", "vt": "str" } ], "checkall": "true", "repair": false, "outputs": 2, "x": 590, "y": 1180, "wires": [ [ "1c02585db34ca808" ], [ "3abe9cdefd8d2b8c" ] ] }, { "id": "630376fa15821840", "type": "function", "z": "e97d31eeea2066ef", "name": "make array", "func": "let myarray = msg.payload.split(\";\");\nmsg.payload = myarray;\nreturn msg;", "outputs": 1, "timeout": 0, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 430, "y": 1180, "wires": [ [ "f3ca7cea508856fc" ] ] }, { "id": "1c02585db34ca808", "type": "debug", "z": "e97d31eeea2066ef", "name": "Aussentemp", "active": true, "tosidebar": true, "console": false, "tostatus": false, "complete": "payload[2]", "targetType": "msg", "statusVal": "", "statusType": "auto", "x": 770, "y": 1160, "wires": [] }, { "id": "c3ec39dc.4614f", "type": "mqtt-broker", "name": "CentralinaPi", "broker": "192.168.1.25", "port": "1883", "clientid": "", "autoConnect": true, "usetls": false, "compatmode": false, "protocolVersion": "4", "keepalive": "60", "cleansession": true, "autoUnsubscribe": true, "birthTopic": "", "birthQos": "0", "birthPayload": "", "birthMsg": {}, "closeTopic": "", "closeQos": "0", "closePayload": "", "closeMsg": {}, "willTopic": "", "willQos": "0", "willPayload": "", "willMsg": {}, "userProps": "", "sessionExpiry": "" } ] |
Zu guter Letzt
Fragen zum Umsetzung in Node Red könnt ihr gerne an mich richten. In Bezug auf Gasgeräte kann ich etwas weiterhelfen, bei Wärmepumpen und anderen neueren Technologien bitte ich euch, die Diskussionsfunktion bei Github zu nutzen.