Da die komplette Umstellung auf Node-RED bei mir abgeschlossen ist, ist die eine oder andere Erfahrung dabei raus gekommen, die ich gerne teilen möchte.
Generell gibt es die Aufgabe die aktualisierten Werte zu verarbeiten aber auch unter bestimmten Bedingungen vorhandene Werte abzufragen. Dabei verzettelt man sich leicht und es kommt zu den wildesten Konstrukten. Zumindest war es bei mir so.
Das Ziel ist eigentlich jeden Sensor nur einmalig in Node-RED anzulegen und die Daten entsprechend zu sammeln. Das mach ein Umbenennen und Austauschen einfacher und mit dem richtigen Konzept ist es nachher egal ob die Daten über Homematic, MQTT oder was sonst was kommen.
Als Beispiel nehme ich dafür die Differenz zwischen der Soll- und der Ist-Temperatur sowie die Berechnung der absoluten Feuchte eines Raumthermostaten. Diese Werte brauche ich um eine Steuerung/Anzeige der Belüftung aufgrund der durchschnittlichen Außentemperatur und der berechneten Wandfeuchte durchzuführen.
Also, 1. Versuch:
So habe ich mal angefangen. Der Knopf "Test" dient zum Abfragen der Werte, die anderen 3 blauen Events aktualisieren die Werte und in den hellblauen "get value" Nodes wird der Sensor abgefragt und der Wert entsprechend ins payload geschrieben.
Raus kommt:
Es funktioniert, aber ist fehleranfällig und schlecht wartbar. Geschweige davon, das man bei mehreren Räumen bald das komplette Chaos hat.
Fazit: So was veröffentliche ich in keinen flow.
Node-RED/flow: Differenz Soll und Ist Temperatur, Taupunkt und Feuchte
Moderator: Co-Administratoren
- Henke
- Beiträge: 1500
- Registriert: 27.06.2022, 20:51
- System: CCU
- Hat sich bedankt: 133 Mal
- Danksagung erhalten: 304 Mal
Re: Node-RED/flow: Differenz Soll und Ist Temperatur, Taupunkt und Feuchte
Nächster Versuch...
Reduziert die Sache schon erheblich und bringt mich direkt auf Version 3, die schon 2 Räume verarbeitet. Raus kommt: Dazu kann ich gerne den flow hier veröffentlichen, aber das gelbe vom Ei ist das noch lange nicht...
dabei werden die "get value" Nodes über den Eingang mit dem entsprechenden Sensor gefüllt und es müssen nur noch die Datenpunkte gesetzt werden.Reduziert die Sache schon erheblich und bringt mich direkt auf Version 3, die schon 2 Räume verarbeitet. Raus kommt: Dazu kann ich gerne den flow hier veröffentlichen, aber das gelbe vom Ei ist das noch lange nicht...
- Henke
- Beiträge: 1500
- Registriert: 27.06.2022, 20:51
- System: CCU
- Hat sich bedankt: 133 Mal
- Danksagung erhalten: 304 Mal
Re: Node-RED/flow: Differenz Soll und Ist Temperatur, Taupunkt und Feuchte
Das Sammeln der Daten erfolgt über eine RCP-Methode bei der vorher die Message gesichert werden muss und nachher zusammengeführt wird. Sind beim Eingang z.B. schon Daten wie payload.FussbodenVentielLevel = 48 vorhanden, so bleiben die!
Die RPC Methode braucht unterschiedlich viel Zeit, aber durch die 1msg/Sekunde ist es ziemlich sicher.
Den RPC Block habe ich mit einer besseren/nicht zeitlichen Sicherung in einen Subflow verpackt, aber das benötigt eine zusätzliche Node. Da weiß ich nicht, ob hier überhaupt das Interesse dazu vorhanden ist. Laufen tut es auch so.
Raus kommt: Damit hat man mit weniger Konfiguration, mehr Werte - ein Mehrwert also?
Dazu der flow:
Code: Alles auswählen
[
{
"id": "52d825e9ff1a5737",
"type": "group",
"z": "084c5252778f131e",
"style": {
"stroke": "#999999",
"stroke-opacity": "1",
"fill": "none",
"fill-opacity": "1",
"label": true,
"label-position": "nw",
"color": "#a4a4a4"
},
"nodes": [
"e3a3a2c76e64e60a",
"df79872785e6bcf2",
"6a4d2639e4490d6e",
"ec9319bf7fcf3c2f",
"d6e99407459c569e",
"c7342894c9ef626c",
"2c5831df07a997fb",
"4498ba772c59070a",
"5fc39e70c9be22d9",
"676982b83b086dc7",
"c9684f05b4fc6d03",
"355cc3e57d1272c2",
"00337bbf1677110f",
"bd6f95f9ab533df9",
"722f9d17a1b009a7",
"bc80a798e5d0aa82",
"1fe03eab465d33bf",
"5b9249905cc5c6d3"
],
"x": 34,
"y": 1579,
"w": 1292,
"h": 362
},
{
"id": "e3a3a2c76e64e60a",
"type": "template",
"z": "084c5252778f131e",
"g": "52d825e9ff1a5737",
"name": "",
"field": "rpc",
"fieldType": "msg",
"format": "handlebars",
"syntax": "mustache",
"template": "{\n \"iface\": \"{{iface}}\",\n \"method\": \"getParamset\",\n \"params\":[\"{{channel}}\",\"VALUES\"]\n}",
"output": "json",
"x": 740,
"y": 1720,
"wires": [
[
"ec9319bf7fcf3c2f"
]
]
},
{
"id": "df79872785e6bcf2",
"type": "function",
"z": "084c5252778f131e",
"g": "52d825e9ff1a5737",
"name": "restore msg",
"func": "var ori = flow.get(\"_msg\");\nvar oriP = ori.payload;\n\nvar pay;\nif (typeof msg.payload !== 'object') {\n if (msg.payload != \"\") {\n // node.send([null, ori]);\n node.send([null, msg]);\n node.error(\"payload falsch\", msg);\n }\n pay = {};\n}\nelse\n pay = msg.payload;\n\nvar oMerge = { ...oriP, ...pay };\nori.payload = oMerge;\n\nnode.send([ori, null]);\nnode.done();\n// flow.set(\"_msg\"); // löschen",
"outputs": 2,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 750,
"y": 1880,
"wires": [
[
"5b9249905cc5c6d3"
],
[]
]
},
{
"id": "6a4d2639e4490d6e",
"type": "ccu-rpc",
"z": "084c5252778f131e",
"g": "52d825e9ff1a5737",
"name": "",
"ccuConfig": "38263145.35ea0e",
"iface": "",
"method": "",
"params": "",
"topic": "",
"x": 730,
"y": 1840,
"wires": [
[
"df79872785e6bcf2"
]
]
},
{
"id": "ec9319bf7fcf3c2f",
"type": "function",
"z": "084c5252778f131e",
"g": "52d825e9ff1a5737",
"name": "save msg",
"func": "// var oriMsg = RED.util.cloneMessage(msg);\nconst oriMsg = msg;\n// oriMsg.start = new Date();\n\nif (typeof oriMsg.payload !== \"object\")\n oriMsg.payload = {};\n\nflow.set(\"_msg\", oriMsg);\nnode.send(oriMsg);\nnode.done();",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 740,
"y": 1760,
"wires": [
[
"d6e99407459c569e"
]
]
},
{
"id": "d6e99407459c569e",
"type": "change",
"z": "084c5252778f131e",
"g": "52d825e9ff1a5737",
"name": "",
"rules": [
{
"t": "set",
"p": "iface",
"pt": "msg",
"to": "rpc.iface",
"tot": "msg"
},
{
"t": "set",
"p": "method",
"pt": "msg",
"to": "rpc.method",
"tot": "msg"
},
{
"t": "set",
"p": "payload",
"pt": "msg",
"to": "rpc.params",
"tot": "msg"
}
],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
"x": 770,
"y": 1800,
"wires": [
[
"6a4d2639e4490d6e"
]
]
},
{
"id": "c7342894c9ef626c",
"type": "delay",
"z": "084c5252778f131e",
"g": "52d825e9ff1a5737",
"name": "",
"pauseType": "rate",
"timeout": "5",
"timeoutUnits": "seconds",
"rate": "1",
"nbRateUnits": "1",
"rateUnits": "second",
"randomFirst": "1",
"randomLast": "5",
"randomUnits": "seconds",
"drop": false,
"allowrate": false,
"outputs": 1,
"x": 780,
"y": 1660,
"wires": [
[
"e3a3a2c76e64e60a"
]
]
},
{
"id": "2c5831df07a997fb",
"type": "switch",
"z": "084c5252778f131e",
"g": "52d825e9ff1a5737",
"name": "Raum?",
"property": "room",
"propertyType": "msg",
"rules": [
{
"t": "eq",
"v": "U-Büro",
"vt": "str"
},
{
"t": "eq",
"v": "U-Bad",
"vt": "str"
},
{
"t": "else"
}
],
"checkall": "true",
"repair": false,
"outputs": 3,
"x": 1040,
"y": 1780,
"wires": [
[
"4498ba772c59070a"
],
[
"676982b83b086dc7"
],
[
"5fc39e70c9be22d9"
]
]
},
{
"id": "4498ba772c59070a",
"type": "debug",
"z": "084c5252778f131e",
"g": "52d825e9ff1a5737",
"name": "",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": true,
"complete": "true",
"targetType": "full",
"statusVal": "room",
"statusType": "msg",
"x": 1230,
"y": 1780,
"wires": []
},
{
"id": "5fc39e70c9be22d9",
"type": "debug",
"z": "084c5252778f131e",
"g": "52d825e9ff1a5737",
"name": "",
"active": false,
"tosidebar": true,
"console": false,
"tostatus": true,
"complete": "true",
"targetType": "full",
"statusVal": "",
"statusType": "counter",
"x": 1230,
"y": 1900,
"wires": []
},
{
"id": "676982b83b086dc7",
"type": "debug",
"z": "084c5252778f131e",
"g": "52d825e9ff1a5737",
"name": "",
"active": false,
"tosidebar": true,
"console": false,
"tostatus": true,
"complete": "true",
"targetType": "full",
"statusVal": "room",
"statusType": "msg",
"x": 1230,
"y": 1840,
"wires": []
},
{
"id": "c9684f05b4fc6d03",
"type": "inject",
"z": "084c5252778f131e",
"g": "52d825e9ff1a5737",
"name": "Test",
"props": [
{
"p": "payload"
}
],
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "",
"payload": "true",
"payloadType": "bool",
"x": 130,
"y": 1660,
"wires": [
[
"00337bbf1677110f"
]
]
},
{
"id": "355cc3e57d1272c2",
"type": "ccu-rpc-event",
"z": "084c5252778f131e",
"g": "52d825e9ff1a5737",
"name": "Alle HmIP-WTH-2",
"iface": "HmIP-RF",
"ccuConfig": "38263145.35ea0e",
"rooms": "",
"roomsRx": "str",
"functions": "",
"functionsRx": "str",
"device": "",
"deviceRx": "str",
"deviceName": "",
"deviceNameRx": "str",
"deviceType": "HmIP-WTH-2",
"deviceTypeRx": "str",
"channel": "",
"channelRx": "str",
"channelName": "",
"channelNameRx": "str",
"channelType": "",
"channelTypeRx": "str",
"channelIndex": "1",
"channelIndexRx": "str",
"datapoint": "",
"datapointRx": "str",
"change": true,
"working": false,
"cache": true,
"topic": "${CCU}/${Interface}/${channelName}/${datapoint}",
"x": 170,
"y": 1780,
"wires": [
[
"bd6f95f9ab533df9"
]
]
},
{
"id": "00337bbf1677110f",
"type": "ccu-get-value",
"z": "084c5252778f131e",
"g": "52d825e9ff1a5737",
"name": "HmIP-WTH-2 U-Büro",
"ccuConfig": "38263145.35ea0e",
"iface": "HmIP-RF",
"channel": "000A9D89B6606E:1 HmIP-WTH-2 U-Büro:1",
"sysvar": "",
"sysvarProperty": "value",
"datapoint": "ACTUAL_TEMPERATURE",
"datapointProperty": "all",
"setProp": "payload",
"setPropType": "cmsg",
"x": 320,
"y": 1660,
"wires": [
[
"c7342894c9ef626c"
]
]
},
{
"id": "bd6f95f9ab533df9",
"type": "trigger",
"z": "084c5252778f131e",
"g": "52d825e9ff1a5737",
"name": "channel filtern, es reicht das letzte Object",
"op1": "",
"op2": "",
"op1type": "nul",
"op2type": "payl",
"duration": "250",
"extend": true,
"overrideDelay": false,
"units": "ms",
"reset": "",
"bytopic": "topic",
"topic": "channel",
"outputs": 1,
"x": 240,
"y": 1840,
"wires": [
[
"c7342894c9ef626c"
]
]
},
{
"id": "722f9d17a1b009a7",
"type": "inject",
"z": "084c5252778f131e",
"g": "52d825e9ff1a5737",
"name": "Test",
"props": [
{
"p": "payload"
}
],
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "",
"payload": "true",
"payloadType": "bool",
"x": 130,
"y": 1720,
"wires": [
[
"bc80a798e5d0aa82"
]
]
},
{
"id": "bc80a798e5d0aa82",
"type": "ccu-get-value",
"z": "084c5252778f131e",
"g": "52d825e9ff1a5737",
"name": "HmIP-WTH-2 U-Büro",
"ccuConfig": "38263145.35ea0e",
"iface": "HmIP-RF",
"channel": "000A9D89B65F61:1 HmIP-WTH-2 U-Bad:1",
"sysvar": "",
"sysvarProperty": "value",
"datapoint": "ACTUAL_TEMPERATURE",
"datapointProperty": "all",
"setProp": "payload",
"setPropType": "cmsg",
"x": 320,
"y": 1720,
"wires": [
[
"c7342894c9ef626c"
]
]
},
{
"id": "1fe03eab465d33bf",
"type": "comment",
"z": "084c5252778f131e",
"g": "52d825e9ff1a5737",
"name": "Version 4: 1 mal den Sensor pro Raum und 0! mal den Datenpunkt eintragen",
"info": "",
"x": 330,
"y": 1620,
"wires": []
},
{
"id": "5b9249905cc5c6d3",
"type": "link call",
"z": "084c5252778f131e",
"g": "52d825e9ff1a5737",
"name": "",
"links": [
"834b3376478dfa4b"
],
"linkType": "static",
"timeout": "30",
"x": 1110,
"y": 1720,
"wires": [
[
"2c5831df07a997fb"
]
]
},
{
"id": "38263145.35ea0e",
"type": "ccu-connection",
"name": "localhost",
"host": "localhost",
"regaEnabled": true,
"bcrfEnabled": false,
"iprfEnabled": true,
"virtEnabled": true,
"bcwiEnabled": false,
"jackEnabled": true,
"cuxdEnabled": false,
"regaPoll": true,
"regaInterval": "30",
"rpcPingTimeout": "30",
"rpcInitAddress": "127.0.0.1",
"rpcServerHost": "127.0.0.1",
"rpcBinPort": "2047",
"rpcXmlPort": "2048",
"tls": false,
"inSecure": false,
"authentication": false,
"queueTimeout": "5000",
"queuePause": "250",
"contextStore": "default"
},
{
"id": "b42b9e6bf4f73dfe",
"type": "group",
"z": "084c5252778f131e",
"style": {
"stroke": "#999999",
"stroke-opacity": "1",
"fill": "none",
"fill-opacity": "1",
"label": true,
"label-position": "nw",
"color": "#a4a4a4"
},
"nodes": [
"9b47c8ba75a5484e",
"edd7124f4fd3e4aa",
"834b3376478dfa4b",
"5699e37e15fcb701",
"2c6449978a5967a2"
],
"x": 1394,
"y": 1639,
"w": 352,
"h": 262
},
{
"id": "9b47c8ba75a5484e",
"type": "function",
"z": "084c5252778f131e",
"g": "b42b9e6bf4f73dfe",
"name": "Diff Set/Akt",
"func": "if (typeof msg.payload.SET_POINT_TEMPERATURE == 'number')\n if (typeof msg.payload.ACTUAL_TEMPERATURE == 'number') {\n var diff = msg.payload.ACTUAL_TEMPERATURE - msg.payload.SET_POINT_TEMPERATURE;\n msg.payload.DiffAktSet = diff;\n return msg;\n }\nreturn msg;\n",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 1490,
"y": 1780,
"wires": [
[
"edd7124f4fd3e4aa"
]
]
},
{
"id": "edd7124f4fd3e4aa",
"type": "function",
"z": "084c5252778f131e",
"g": "b42b9e6bf4f73dfe",
"name": "Taupunkt und AbsFeuchte berechnen",
"func": "var InTemp = msg.payload.ACTUAL_TEMPERATURE;\nif ( typeof InTemp != 'number')\n return msg;\nvar InFeuchte = msg.payload.HUMIDITY;\nif (typeof InFeuchte != 'number')\n return msg;\n\nvar rA = 7.5;\nvar rB = 237.3; // für T >= 0\n// real rR = 8314.3; !J / (kmol * K)(universelle Gaskonstante)\n// !real rMw = 18.016; !kg / kmol(Molekulargewicht des Wasserdampfes)\n// real rKelvin = 273.15;\nvar rDD = 6.1078 * Math.pow(10.0, ((rA * InTemp) / (rB + InTemp))) * InFeuchte / 100.0;\nvar rV = Math.log10 (rDD / 6.1078);\n// obj.temp.Taupunkt = Number( Math.round ( (rB * rV / (rA - rV))*100)/100);\n// obj.huminity.AbsFeuchte = Number(Math.round(((Math.pow(10.0, 5.0) * (18.016 / 8314.3) * rDD / (tInTemp + 273.15)) * 100)) / 100);\nmsg.payload.Taupunkt = Number( rB * rV / (rA - rV) );\nmsg.payload.AbsFeuchte = Number(Math.pow(10.0, 5.0) * (18.016 / 8314.3) * rDD / (InTemp + 273.15));\nreturn msg;\n",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 1570,
"y": 1820,
"wires": [
[
"5699e37e15fcb701"
]
]
},
{
"id": "834b3376478dfa4b",
"type": "link in",
"z": "084c5252778f131e",
"g": "b42b9e6bf4f73dfe",
"name": "DiffAktSet/Taupunkt/AbsFeuchte",
"links": [],
"x": 1435,
"y": 1740,
"wires": [
[
"9b47c8ba75a5484e"
]
]
},
{
"id": "5699e37e15fcb701",
"type": "link out",
"z": "084c5252778f131e",
"g": "b42b9e6bf4f73dfe",
"name": "link out 64",
"mode": "return",
"links": [],
"x": 1435,
"y": 1860,
"wires": []
},
{
"id": "2c6449978a5967a2",
"type": "link call",
"z": "084c5252778f131e",
"g": "b42b9e6bf4f73dfe",
"name": "",
"links": [
"834b3376478dfa4b"
],
"linkType": "static",
"timeout": "30",
"x": 1550,
"y": 1680,
"wires": [
[]
]
}
]
Zuletzt geändert von Henke am 23.11.2022, 02:08, insgesamt 1-mal geändert.
- Henke
- Beiträge: 1500
- Registriert: 27.06.2022, 20:51
- System: CCU
- Hat sich bedankt: 133 Mal
- Danksagung erhalten: 304 Mal
Re: Node-RED/flow: Differenz Soll und Ist Temperatur, Taupunkt und Feuchte
Bzw. im realen Einsatz um das Raumklima, Heizung etc. zu Steuern: Gerne veröffentliche die erstellten Subflows, jedoch weiß ich nicht, ob überhaupt Interesse daran besteht.
Da wären z.B.:
Berechnung Tagesdurchschnitt mit zeitlicher Auswertung, Betriebsstunden
Berechnung Energiezählung - im Aufbau, noch zu wenig Daten
Schnittstelle Datenspeicherung global Werte im zip-Format
Schnittstelle CCU-Variablen über http direkt nach Node-RED
rpc-sets zum Auslesen des Masterset (Komfortemperatur Heizen/Kühlen), Value (s.o.), setzen und löschen von Direktverknüpfungen
Sensor Trigger, Setzen und Abfrage mit Kontrolle ob die Sensor stimmt, der Wert gesendet werden muss, etc
Nutzen der Gewerke, Kontrolle der Gewerke und Räume alternativ auch über ID um so Problem mit Umbenennen zu verhindern.
LG
Michael