Node-RED/flow: Differenz Soll und Ist Temperatur, Taupunkt und Feuchte

Node-RED als CCU3/RaspberryMatic Addon, WebApp, HomeKit, ...

Moderator: Co-Administratoren

Antworten
Benutzeravatar
Henke
Beiträge: 1500
Registriert: 27.06.2022, 20:51
System: CCU
Hat sich bedankt: 133 Mal
Danksagung erhalten: 304 Mal

Node-RED/flow: Differenz Soll und Ist Temperatur, Taupunkt und Feuchte

Beitrag von Henke » 23.11.2022, 01:17

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:
Screenshot 2022-11-23 010544.jpg
Version 1 - Übersicht flow
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:
Screenshot 2022-11-23 010912.jpg
Version 1 - Ausgabe
Screenshot 2022-11-23 010912.jpg (29.22 KiB) 1295 mal betrachtet
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. :D

Benutzeravatar
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

Beitrag von Henke » 23.11.2022, 01:25

Nächster Versuch...
Screenshot 2022-11-23 011804.jpg
Version 2 - Übersicht flow
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.
Screenshot 2022-11-23 012148.jpg
Version 3 - Übersicht flow
Raus kommt:
Screenshot 2022-11-23 012316.jpg
Version 1 - Ausgabe
Dazu kann ich gerne den flow hier veröffentlichen, aber das gelbe vom Ei ist das noch lange nicht...

Benutzeravatar
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

Beitrag von Henke » 23.11.2022, 01:44

Screenshot 2022-11-23 012813.jpg
Version 4 - Übersicht flow
Die Berechnungen sind ausgelagert in einen "Link call". Hintergrund ist, ich benötige sie mehrmals, warte aber nur einmal den Code. Für einen Sublink ist es zu klein und hat weder eine Anzeige nötig, noch gibt es zeitliche Probleme.
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:
Screenshot 2022-11-23 013945.jpg
Version 4 - Ausgabe, nur den payload
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": [
            []
        ]
    }
]
Aussicht...
Zuletzt geändert von Henke am 23.11.2022, 02:08, insgesamt 1-mal geändert.

Benutzeravatar
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

Beitrag von Henke » 23.11.2022, 02:07

Screenshot 2022-11-23 014503.jpg
Version 5 - Übersicht flow
So sieht es eher aus, wenn ich es benutze.
Bzw. im realen Einsatz um das Raumklima, Heizung etc. zu Steuern:
Screenshot 2022-11-23 014950.jpg
Übersicht realer Einsatz
Screenshot 2022-11-23 015601.jpg
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

Antworten

Zurück zu „RedMatic“