Erster Commit
This commit is contained in:
815
flows.json
815
flows.json
@@ -1 +1,814 @@
|
||||
[]
|
||||
[
|
||||
{
|
||||
"id": "0e938b2c7d519455",
|
||||
"type": "tab",
|
||||
"label": "Flow 1",
|
||||
"disabled": false,
|
||||
"info": "",
|
||||
"env": [
|
||||
{
|
||||
"name": "in_port",
|
||||
"value": "20000",
|
||||
"type": "str"
|
||||
},
|
||||
{
|
||||
"name": "out_port",
|
||||
"value": "20000",
|
||||
"type": "str"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "b9502e7d5adbd1fc",
|
||||
"type": "subflow",
|
||||
"name": "Pump Command",
|
||||
"info": "",
|
||||
"category": "",
|
||||
"in": [
|
||||
{
|
||||
"x": 140,
|
||||
"y": 100,
|
||||
"wires": [
|
||||
{
|
||||
"id": "26952e484ee2bdd1"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"out": [
|
||||
{
|
||||
"x": 380,
|
||||
"y": 100,
|
||||
"wires": [
|
||||
{
|
||||
"id": "26952e484ee2bdd1",
|
||||
"port": 0
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"env": [
|
||||
{
|
||||
"name": "TARGET_NODE",
|
||||
"type": "num",
|
||||
"value": ""
|
||||
},
|
||||
{
|
||||
"name": "PUMP_ID",
|
||||
"type": "num",
|
||||
"value": ""
|
||||
}
|
||||
],
|
||||
"meta": {},
|
||||
"color": "#DDAA99"
|
||||
},
|
||||
{
|
||||
"id": "1a6e91b7110fc591",
|
||||
"type": "subflow",
|
||||
"name": "Druck",
|
||||
"info": "",
|
||||
"category": "",
|
||||
"in": [
|
||||
{
|
||||
"x": 60,
|
||||
"y": 40,
|
||||
"wires": [
|
||||
{
|
||||
"id": "6e8a2a39a56ae270"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"out": [
|
||||
{
|
||||
"x": 340,
|
||||
"y": 40,
|
||||
"wires": [
|
||||
{
|
||||
"id": "6e8a2a39a56ae270",
|
||||
"port": 0
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"env": [],
|
||||
"meta": {},
|
||||
"color": "#DDAA99"
|
||||
},
|
||||
{
|
||||
"id": "d9f42b9039b25aff",
|
||||
"type": "server",
|
||||
"name": "Home Assistant",
|
||||
"version": 6,
|
||||
"addon": false,
|
||||
"rejectUnauthorizedCerts": false,
|
||||
"ha_boolean": [
|
||||
"y",
|
||||
"yes",
|
||||
"true",
|
||||
"on",
|
||||
"home",
|
||||
"open"
|
||||
],
|
||||
"connectionDelay": true,
|
||||
"cacheJson": true,
|
||||
"heartbeat": true,
|
||||
"heartbeatInterval": 30,
|
||||
"areaSelector": "friendlyName",
|
||||
"deviceSelector": "friendlyName",
|
||||
"entitySelector": "friendlyName",
|
||||
"statusSeparator": ": ",
|
||||
"statusYear": "hidden",
|
||||
"statusMonth": "short",
|
||||
"statusDay": "numeric",
|
||||
"statusHourCycle": "default",
|
||||
"statusTimeFormat": "h:m",
|
||||
"enableGlobalContextStore": false
|
||||
},
|
||||
{
|
||||
"id": "0a17864de5505a20",
|
||||
"type": "ha-device-config",
|
||||
"name": "Regenwasser",
|
||||
"hwVersion": "",
|
||||
"manufacturer": "Node-RED",
|
||||
"model": "",
|
||||
"swVersion": ""
|
||||
},
|
||||
{
|
||||
"id": "79cd4bcaeefb790a",
|
||||
"type": "ha-entity-config",
|
||||
"server": "d9f42b9039b25aff",
|
||||
"deviceConfig": "0a17864de5505a20",
|
||||
"name": "Systemdruck",
|
||||
"version": 6,
|
||||
"entityType": "sensor",
|
||||
"haConfig": [
|
||||
{
|
||||
"property": "name",
|
||||
"value": "Systemdruck"
|
||||
},
|
||||
{
|
||||
"property": "icon",
|
||||
"value": ""
|
||||
},
|
||||
{
|
||||
"property": "entity_picture",
|
||||
"value": ""
|
||||
},
|
||||
{
|
||||
"property": "entity_category",
|
||||
"value": ""
|
||||
},
|
||||
{
|
||||
"property": "device_class",
|
||||
"value": "pressure"
|
||||
},
|
||||
{
|
||||
"property": "unit_of_measurement",
|
||||
"value": "Pa"
|
||||
},
|
||||
{
|
||||
"property": "state_class",
|
||||
"value": ""
|
||||
}
|
||||
],
|
||||
"resend": false,
|
||||
"debugEnabled": false
|
||||
},
|
||||
{
|
||||
"id": "11087cbf4f9379d1",
|
||||
"type": "ha-entity-config",
|
||||
"server": "d9f42b9039b25aff",
|
||||
"deviceConfig": "0a17864de5505a20",
|
||||
"name": "Wasserstand",
|
||||
"version": 6,
|
||||
"entityType": "sensor",
|
||||
"haConfig": [
|
||||
{
|
||||
"property": "name",
|
||||
"value": "Wasserstand"
|
||||
},
|
||||
{
|
||||
"property": "icon",
|
||||
"value": ""
|
||||
},
|
||||
{
|
||||
"property": "entity_picture",
|
||||
"value": ""
|
||||
},
|
||||
{
|
||||
"property": "entity_category",
|
||||
"value": ""
|
||||
},
|
||||
{
|
||||
"property": "device_class",
|
||||
"value": "pressure"
|
||||
},
|
||||
{
|
||||
"property": "unit_of_measurement",
|
||||
"value": "Pa"
|
||||
},
|
||||
{
|
||||
"property": "state_class",
|
||||
"value": ""
|
||||
}
|
||||
],
|
||||
"resend": false,
|
||||
"debugEnabled": false
|
||||
},
|
||||
{
|
||||
"id": "9cba2b96af7846f4",
|
||||
"type": "ha-entity-config",
|
||||
"server": "d9f42b9039b25aff",
|
||||
"deviceConfig": "0a17864de5505a20",
|
||||
"name": "Pumpe",
|
||||
"version": 6,
|
||||
"entityType": "binary_sensor",
|
||||
"haConfig": [
|
||||
{
|
||||
"property": "name",
|
||||
"value": "Pumpe"
|
||||
},
|
||||
{
|
||||
"property": "icon",
|
||||
"value": ""
|
||||
},
|
||||
{
|
||||
"property": "entity_picture",
|
||||
"value": ""
|
||||
},
|
||||
{
|
||||
"property": "entity_category",
|
||||
"value": ""
|
||||
},
|
||||
{
|
||||
"property": "device_class",
|
||||
"value": ""
|
||||
}
|
||||
],
|
||||
"resend": false,
|
||||
"debugEnabled": false
|
||||
},
|
||||
{
|
||||
"id": "92579cd9fdb0eda1",
|
||||
"type": "ha-entity-config",
|
||||
"server": "d9f42b9039b25aff",
|
||||
"deviceConfig": "0a17864de5505a20",
|
||||
"name": "Quittierung Pumpenstörung",
|
||||
"version": 6,
|
||||
"entityType": "button",
|
||||
"haConfig": [
|
||||
{
|
||||
"property": "name",
|
||||
"value": "Quittierunf Pumpenstörung"
|
||||
},
|
||||
{
|
||||
"property": "icon",
|
||||
"value": ""
|
||||
},
|
||||
{
|
||||
"property": "entity_picture",
|
||||
"value": ""
|
||||
},
|
||||
{
|
||||
"property": "entity_category",
|
||||
"value": "config"
|
||||
},
|
||||
{
|
||||
"property": "device_class",
|
||||
"value": "restart"
|
||||
}
|
||||
],
|
||||
"resend": false,
|
||||
"debugEnabled": true
|
||||
},
|
||||
{
|
||||
"id": "bcc6944e1ff6c6e7",
|
||||
"type": "ha-entity-config",
|
||||
"server": "d9f42b9039b25aff",
|
||||
"deviceConfig": "0a17864de5505a20",
|
||||
"name": "Pumpenfehler",
|
||||
"version": 6,
|
||||
"entityType": "binary_sensor",
|
||||
"haConfig": [
|
||||
{
|
||||
"property": "name",
|
||||
"value": "Pumpenfehler"
|
||||
},
|
||||
{
|
||||
"property": "icon",
|
||||
"value": ""
|
||||
},
|
||||
{
|
||||
"property": "entity_picture",
|
||||
"value": ""
|
||||
},
|
||||
{
|
||||
"property": "entity_category",
|
||||
"value": ""
|
||||
},
|
||||
{
|
||||
"property": "device_class",
|
||||
"value": "problem"
|
||||
}
|
||||
],
|
||||
"resend": false,
|
||||
"debugEnabled": false
|
||||
},
|
||||
{
|
||||
"id": "fd41de37db3bcf9f",
|
||||
"type": "ha-entity-config",
|
||||
"server": "d9f42b9039b25aff",
|
||||
"deviceConfig": "0a17864de5505a20",
|
||||
"name": "Verlorene Frames",
|
||||
"version": 6,
|
||||
"entityType": "sensor",
|
||||
"haConfig": [
|
||||
{
|
||||
"property": "name",
|
||||
"value": "Verlorene Frames"
|
||||
},
|
||||
{
|
||||
"property": "icon",
|
||||
"value": "mdi:alert"
|
||||
},
|
||||
{
|
||||
"property": "entity_picture",
|
||||
"value": ""
|
||||
},
|
||||
{
|
||||
"property": "entity_category",
|
||||
"value": "diagnostic"
|
||||
},
|
||||
{
|
||||
"property": "device_class",
|
||||
"value": ""
|
||||
},
|
||||
{
|
||||
"property": "unit_of_measurement",
|
||||
"value": ""
|
||||
},
|
||||
{
|
||||
"property": "state_class",
|
||||
"value": "measurement"
|
||||
}
|
||||
],
|
||||
"resend": false,
|
||||
"debugEnabled": false
|
||||
},
|
||||
{
|
||||
"id": "26952e484ee2bdd1",
|
||||
"type": "function",
|
||||
"z": "b9502e7d5adbd1fc",
|
||||
"name": "function 1",
|
||||
"func": "let targetNode = env.get(\"TARGET_NODE\");\nlet pumpId = env.get(\"PUMP_ID\");\nlet pumpCmd = msg.payload; \n\nif (typeof pumpCmd !== \"number\") {\n node.warn(\"Befehl muss eine Zahl sein!\");\n return null;\n}\n\n// Wir bauen NUR den Payload (die 3 Bytes Nutzdaten)\nmsg.payload = Buffer.from([ targetNode, pumpId, pumpCmd ]);\n\n// 1. Das komplette Objekt mit allen IDs aus dem globalen Speicher holen\nlet CAN = global.get(\"CAN\");\n\n// 2. Wir hängen dem Paket als \"Post-It\" an, auf welcher CAN-ID es später reisen soll.\n// CAN.CMD_PUMP holt genau die 0x020 aus deiner globalen Liste.\nmsg.canId = CAN.CMD_PUMP; \n\nreturn msg;",
|
||||
"outputs": 1,
|
||||
"timeout": 0,
|
||||
"noerr": 0,
|
||||
"initialize": "",
|
||||
"finalize": "",
|
||||
"libs": [],
|
||||
"x": 260,
|
||||
"y": 100,
|
||||
"wires": [
|
||||
[]
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "6e8a2a39a56ae270",
|
||||
"type": "function",
|
||||
"z": "1a6e91b7110fc591",
|
||||
"name": "Druck vom CAN",
|
||||
"func": "let buf = msg.payload;\n\n// Sicherheitscheck: 6 Bytes erwartet\nif (buf.length !== 6) return null;\n\n// Bytes auslesen\nlet sourceNode = buf.readUInt8(0);\nlet sensorId = buf.readUInt8(1);\nlet pressurePa = buf.readUInt32BE(2); // Die 300.000 auslesen\n\n// Für Home Assistant rechnen wir Pascal wieder in Bar um\nlet pressureBar = pressurePa / 100000.0;\n\n// Payload überschreiben (HA will nur den reinen Wert)\nmsg.payload = pressurePa;\nmsg.sensorId = sensorId;\nmsg.nodeId = sourceNode;\n\n// Grüne LED am Knoten für direktes Feedback\nnode.status({fill:\"green\", shape:\"dot\", text: pressureBar.toFixed(2) + \" Bar\"});\n\nreturn msg;",
|
||||
"outputs": 1,
|
||||
"timeout": 0,
|
||||
"noerr": 0,
|
||||
"initialize": "",
|
||||
"finalize": "",
|
||||
"libs": [],
|
||||
"x": 200,
|
||||
"y": 40,
|
||||
"wires": [
|
||||
[]
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "d5928c3e9f080926",
|
||||
"type": "udp in",
|
||||
"z": "0e938b2c7d519455",
|
||||
"name": "Garten CAN UDP In",
|
||||
"iface": "",
|
||||
"port": "${in_port}",
|
||||
"ipv": "udp4",
|
||||
"multicast": "false",
|
||||
"group": "",
|
||||
"datatype": "buffer",
|
||||
"x": 110,
|
||||
"y": 160,
|
||||
"wires": [
|
||||
[
|
||||
"59119f4834036e0f"
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "59119f4834036e0f",
|
||||
"type": "function",
|
||||
"z": "0e938b2c7d519455",
|
||||
"name": "Input Decoder",
|
||||
"func": "// sender IP merken für spätere Antworten\nflow.set(\"last_gateway_ip\", msg.ip);\n\n// msg.payload enthält den rohen Byte-Buffer vom ESP32/STM32\nlet buf = msg.payload;\n\n// Sicherheitscheck 1: Ist das Paket überhaupt groß genug für den Header?\nif (buf.length < 8) return null;\n\n// Header auslesen (Offsets korrigiert)\nlet canId = buf.readUInt32BE(0);\nlet flags = buf.readUInt8(4);\nlet dlc = buf.readUInt8(5);\nlet seqNo = buf.readUInt16BE(6);\n\n// Sicherheitscheck 2: Stimmen DLC und Puffergrösse überein?\nif (buf.length !== dlc + 8) {\n node.status({ fill: \"red\", shape: \"dot\", text: \"Size mismatch\" });\n return null;\n}\n\n// 1. CAN-ID als sauberen Hex-String formatieren\nlet canIdHex = canId.toString(16).toUpperCase();\n\n// 2. Die echten CAN-Nutzdaten aus dem Buffer herausschneiden (ab Byte 8)\nlet canData = buf.slice(8, 8 + dlc);\n\n// 3. Nachricht für die Weiche und die Decoder vorbereiten\nmsg.canId = canId; // Für den switch-Knoten\nmsg.payload = canData; // Ab hier liegen nur noch die echten Daten im Payload\nmsg.seqNo = seqNo; // Falls ein späterer Node die Sequenznummer braucht\n\n// Grüne LED mit Hex-ID anzeigen\nnode.status({ fill: \"green\", shape: \"dot\", text: \"ID: 0x\" + canIdHex + \" | Seq: \" + seqNo + \" | DLC: \" + dlc });\n\n// --- SEQUENZPRÜFUNG ---\n// Holt die Werte aus dem flüchtigen RAM-Kontext der Node\nlet oldSeq = context.get('oldSeq');\nlet errorCounter = context.get('errorCounter') || 0;\n\nif (oldSeq === undefined) {\n context.set('oldSeq', seqNo);\n context.set('errorCounter', errorCounter);\n msg.errorCounter = errorCounter;\n return msg;\n}\n\n// Delta berechnen (16-Bit Maske wegen readUInt16BE)\nlet delta = (seqNo - oldSeq) & 0xFFFF;\n\nif (delta > 1) {\n // Da du oben 10000 statt 100 gewählt hast: absolut okay für 16-Bit-Zähler bei schnellen Telegrammen\n if (delta < 10000) {\n let lostFrames = delta - 1;\n errorCounter += lostFrames;\n // FIX: Variable auf errorCounter korrigiert\n node.warn(`${lostFrames} Frame(s) verloren! Zähler steht auf: ${errorCounter}`);\n } else {\n // FIX: Variable auf seqNo korrigiert\n node.log(`Sensor-Reset erkannt (Sprung von ${oldSeq} auf ${seqNo}).`);\n }\n}\n\n// Werte für das nächste Paket im RAM merken\ncontext.set('oldSeq', seqNo);\ncontext.set('errorCounter', errorCounter);\n\n// Fehlerzähler an die Nachricht hängen\nmsg.errorCounter = errorCounter;\n\nreturn msg;",
|
||||
"outputs": 1,
|
||||
"timeout": 0,
|
||||
"noerr": 0,
|
||||
"initialize": "",
|
||||
"finalize": "",
|
||||
"libs": [],
|
||||
"x": 360,
|
||||
"y": 160,
|
||||
"wires": [
|
||||
[
|
||||
"1be1fe0ccdfb3d67",
|
||||
"d2f4efb6f0a3199d"
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "f8eb45ba33d70b65",
|
||||
"type": "ha-button",
|
||||
"z": "0e938b2c7d519455",
|
||||
"name": "Quittierung Pumpenstörung",
|
||||
"version": 0,
|
||||
"debugenabled": false,
|
||||
"outputs": 1,
|
||||
"entityConfig": "92579cd9fdb0eda1",
|
||||
"outputProperties": [
|
||||
{
|
||||
"property": "payload",
|
||||
"propertyType": "msg",
|
||||
"value": "0x05",
|
||||
"valueType": "num"
|
||||
}
|
||||
],
|
||||
"x": 140,
|
||||
"y": 480,
|
||||
"wires": [
|
||||
[
|
||||
"a947a45468017733"
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "a0a226dd400461d8",
|
||||
"type": "comment",
|
||||
"z": "0e938b2c7d519455",
|
||||
"name": "",
|
||||
"info": "### Funktionale CAN-Map (11-Bit)\n\n| Hex-ID | Dezimal | Kategorie | Beschreibung |\n| :--- | :--- | :--- | :--- |\n| **`0x000`** | 0 | **NMT** | Network Management (Alle Halt/Start) |\n| **`0x010`** | 16 | **Alarm** | Emergency (Saugrohr trocken, Druck kritisch) |\n| **`0x020`** | 32 | **Befehl** | Pumpensteuerung (Auto/Aus/Reset) |\n| **`0x030`** | 48 | **Befehl** | Ventil setzen (Pos in %) |\n| **`0x100`** | 256 | **Wert** | Systemdruck (Pascal) |\n| **`0x110`** | 272 | **Wert** | Füllstand (Tank / Einperlverfahren) |\n| **`0x200`** | 512 | **Zähler** | Durchflusszähler (Ticks) |\n| **`0x220`** | 544 | **Status** | Pumpenstatus |\n| **`0x230`** | 560 | **Status** | Ventilstatus |\n| **`0x7E0`** | 2016| **System**| Firmware Update (bis 0x7EF) |\n| **`0x7F0`** | 2032| **System**| Debug & Logging (bis 0x7FF) |",
|
||||
"x": 80,
|
||||
"y": 60,
|
||||
"wires": []
|
||||
},
|
||||
{
|
||||
"id": "1be1fe0ccdfb3d67",
|
||||
"type": "switch",
|
||||
"z": "0e938b2c7d519455",
|
||||
"name": "",
|
||||
"property": "canId",
|
||||
"propertyType": "msg",
|
||||
"rules": [
|
||||
{
|
||||
"t": "eq",
|
||||
"v": "CAN.STAT_PUMP",
|
||||
"vt": "global"
|
||||
},
|
||||
{
|
||||
"t": "eq",
|
||||
"v": "CAN.VAL_PRESSURE",
|
||||
"vt": "global"
|
||||
}
|
||||
],
|
||||
"checkall": "true",
|
||||
"repair": false,
|
||||
"outputs": 2,
|
||||
"x": 550,
|
||||
"y": 160,
|
||||
"wires": [
|
||||
[
|
||||
"0c60399fc4327d06"
|
||||
],
|
||||
[
|
||||
"5538f815301a6ace"
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "0c60399fc4327d06",
|
||||
"type": "function",
|
||||
"z": "0e938b2c7d519455",
|
||||
"name": "Pumpenstatus",
|
||||
"func": "let buf = msg.payload;\n\n// Sicherheitscheck: Wir wollen 3 Bytes lesen (Index 0, 1, 2)\nif (buf.length !== 3) return null;\n\nlet nodeId = buf.readUInt8(0);\nlet pumpId = buf.readUInt8(1);\nlet pumpState = buf.readUInt8(2);\n\nlet runState = false;\nlet errorState = false;\n\nswitch (pumpState) {\n case 0:\n runState = false;\n errorState = false;\n break;\n case 1:\n runState = true;\n errorState = false;\n break;\n case 2:\n default:\n runState = false;\n errorState = true;\n break;\n}\n\n// Nachricht 1 (Ausgang Oben) vorbereiten\nmsg.payload = runState;\nmsg.pumpId = pumpId;\nmsg.disabled = errorState;\nmsg.nodeId = nodeId;\n\n// Nachricht 2 (Ausgang Unten) vorbereiten\n// Wichtig: Wir müssen das Objekt erst erschaffen!\nlet msg_error = {\n payload: errorState,\n pumpId: pumpId,\n nodeId: nodeId\n};\n\n// Grüne LED anzeigen (wir nutzen msg.canId, falls du das vorher übergeben hast)\nnode.status({ fill: \"green\", shape: \"dot\", text: \"Pumpe \" + pumpId + \" | Status: \" + pumpState });\n\n// Ausgang 1 bekommt msg, Ausgang 2 bekommt msg_error\nreturn [msg, msg_error];",
|
||||
"outputs": 2,
|
||||
"timeout": 0,
|
||||
"noerr": 0,
|
||||
"initialize": "",
|
||||
"finalize": "",
|
||||
"libs": [],
|
||||
"x": 800,
|
||||
"y": 120,
|
||||
"wires": [
|
||||
[
|
||||
"2f788ae7d216ce09"
|
||||
],
|
||||
[
|
||||
"63466986c3d2f412"
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "2f788ae7d216ce09",
|
||||
"type": "ha-binary-sensor",
|
||||
"z": "0e938b2c7d519455",
|
||||
"name": "Pumpe",
|
||||
"entityConfig": "9cba2b96af7846f4",
|
||||
"version": 0,
|
||||
"state": "payload",
|
||||
"stateType": "msg",
|
||||
"attributes": [
|
||||
{
|
||||
"property": "disable",
|
||||
"value": "disabled",
|
||||
"valueType": "msg"
|
||||
}
|
||||
],
|
||||
"inputOverride": "allow",
|
||||
"outputProperties": [],
|
||||
"x": 1020,
|
||||
"y": 100,
|
||||
"wires": [
|
||||
[]
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "63466986c3d2f412",
|
||||
"type": "ha-binary-sensor",
|
||||
"z": "0e938b2c7d519455",
|
||||
"name": "Pumpenfehler",
|
||||
"entityConfig": "bcc6944e1ff6c6e7",
|
||||
"version": 0,
|
||||
"state": "payload",
|
||||
"stateType": "msg",
|
||||
"attributes": [],
|
||||
"inputOverride": "block",
|
||||
"outputProperties": [],
|
||||
"x": 1040,
|
||||
"y": 160,
|
||||
"wires": [
|
||||
[]
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "a947a45468017733",
|
||||
"type": "subflow:b9502e7d5adbd1fc",
|
||||
"z": "0e938b2c7d519455",
|
||||
"name": "",
|
||||
"env": [
|
||||
{
|
||||
"name": "TARGET_NODE",
|
||||
"value": "1",
|
||||
"type": "num"
|
||||
},
|
||||
{
|
||||
"name": "PUMP_ID",
|
||||
"value": "1",
|
||||
"type": "num"
|
||||
}
|
||||
],
|
||||
"x": 400,
|
||||
"y": 480,
|
||||
"wires": [
|
||||
[
|
||||
"259a6eb05825188b"
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "36e2dae7fa76fa4f",
|
||||
"type": "debug",
|
||||
"z": "0e938b2c7d519455",
|
||||
"name": "Systemdruck",
|
||||
"active": true,
|
||||
"tosidebar": true,
|
||||
"console": false,
|
||||
"tostatus": false,
|
||||
"complete": "true",
|
||||
"targetType": "full",
|
||||
"statusVal": "",
|
||||
"statusType": "auto",
|
||||
"x": 1330,
|
||||
"y": 240,
|
||||
"wires": []
|
||||
},
|
||||
{
|
||||
"id": "efce4aea184f149f",
|
||||
"type": "inject",
|
||||
"z": "0e938b2c7d519455",
|
||||
"name": "",
|
||||
"props": [
|
||||
{
|
||||
"p": "payload"
|
||||
},
|
||||
{
|
||||
"p": "topic",
|
||||
"vt": "str"
|
||||
}
|
||||
],
|
||||
"repeat": "",
|
||||
"crontab": "",
|
||||
"once": true,
|
||||
"onceDelay": 0.1,
|
||||
"topic": "",
|
||||
"payload": "",
|
||||
"payloadType": "date",
|
||||
"x": 330,
|
||||
"y": 60,
|
||||
"wires": [
|
||||
[
|
||||
"14566063729419a9"
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "14566063729419a9",
|
||||
"type": "function",
|
||||
"z": "0e938b2c7d519455",
|
||||
"name": "Globals",
|
||||
"func": "// Wir nutzen jetzt echte Hex-Zahlen (ohne Anführungszeichen!)\nconst CAN_IDS = {\n // Alarme\n ALARM: 0x010, \n \n // Befehle\n CMD_PUMP: 0x020, \n CMD_VALVE: 0x030, \n \n // Werte\n VAL_PRESSURE: 0x100,\n\n // Status\n STAT_PUMP: 0x220,\n};\n\nglobal.set(\"CAN\", CAN_IDS);\n\nnode.status({fill:\"green\", shape:\"dot\", text:\"Globale CAN-IDs geladen\"});\nreturn null;",
|
||||
"outputs": 1,
|
||||
"timeout": 0,
|
||||
"noerr": 0,
|
||||
"initialize": "",
|
||||
"finalize": "",
|
||||
"libs": [],
|
||||
"x": 540,
|
||||
"y": 60,
|
||||
"wires": [
|
||||
[]
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "259a6eb05825188b",
|
||||
"type": "function",
|
||||
"z": "0e938b2c7d519455",
|
||||
"name": "Output Encoder",
|
||||
"func": "let targetIp = flow.get(\"last_gateway_ip\");\n\nif (!targetIp) {\n node.warn(\"Verworfen: Noch keine Gateway-IP gelernt. Warte auf erstes Paket vom ESP...\");\n return null;\n}\n\n// 2. Daten vom vorgeschalteten Knoten holen\nlet canData = msg.payload; // Die 3 Bytes aus dem Subflow\nlet canIdHex = msg.canId; // Die gewünschte CAN-ID (\"20\")\n\n// Sequenznummer global hochzählen\nlet seqNo = global.get(\"txSeqNo\") || 0;\nglobal.set(\"txSeqNo\", (seqNo + 1) % 65536);\n\n// 3. Den rohen UDP-Buffer bauen: 8 Bytes Header + X Bytes Daten\n// Das geniale: canData.length macht die DLC absolut dynamisch!\nlet buf = Buffer.alloc(8 + canData.length);\n\n// --- HEADER ---\nbuf.writeUInt32BE(parseInt(canIdHex, 16), 0); // CAN-ID umwandeln\nbuf.writeUInt8(0, 4); // Flags\nbuf.writeUInt8(canData.length, 5); // DLC (berechnet sich von selbst)\nbuf.writeUInt16BE(seqNo, 6); // Sequenznummer\n\n// --- PAYLOAD ---\n// Kopiert unsere 3 Nutzdaten-Bytes exakt an Position 8 in den großen Buffer\ncanData.copy(buf, 8);\n\n// 4. Feuer frei für den udp-out Knoten\nmsg.payload = buf;\nmsg.ip = targetIp;\n\nreturn msg;",
|
||||
"outputs": 1,
|
||||
"timeout": 0,
|
||||
"noerr": 0,
|
||||
"initialize": "",
|
||||
"finalize": "",
|
||||
"libs": [],
|
||||
"x": 600,
|
||||
"y": 480,
|
||||
"wires": [
|
||||
[
|
||||
"97252b8dc7a55b3e"
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "97252b8dc7a55b3e",
|
||||
"type": "udp out",
|
||||
"z": "0e938b2c7d519455",
|
||||
"name": "",
|
||||
"addr": "",
|
||||
"iface": "",
|
||||
"port": "${out_port}",
|
||||
"ipv": "udp4",
|
||||
"outport": "",
|
||||
"base64": false,
|
||||
"multicast": "false",
|
||||
"x": 840,
|
||||
"y": 480,
|
||||
"wires": []
|
||||
},
|
||||
{
|
||||
"id": "5538f815301a6ace",
|
||||
"type": "subflow:1a6e91b7110fc591",
|
||||
"z": "0e938b2c7d519455",
|
||||
"name": "",
|
||||
"x": 770,
|
||||
"y": 280,
|
||||
"wires": [
|
||||
[
|
||||
"d7b39d46bdc6d1c8"
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "c43b3701443a6dfe",
|
||||
"type": "ha-sensor",
|
||||
"z": "0e938b2c7d519455",
|
||||
"name": "Systemdruck",
|
||||
"entityConfig": "79cd4bcaeefb790a",
|
||||
"version": 0,
|
||||
"state": "payload",
|
||||
"stateType": "msg",
|
||||
"attributes": [],
|
||||
"inputOverride": "allow",
|
||||
"outputProperties": [],
|
||||
"x": 1130,
|
||||
"y": 240,
|
||||
"wires": [
|
||||
[
|
||||
"36e2dae7fa76fa4f"
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "d7b39d46bdc6d1c8",
|
||||
"type": "switch",
|
||||
"z": "0e938b2c7d519455",
|
||||
"name": "",
|
||||
"property": "sensorId",
|
||||
"propertyType": "msg",
|
||||
"rules": [
|
||||
{
|
||||
"t": "eq",
|
||||
"v": "1",
|
||||
"vt": "num"
|
||||
},
|
||||
{
|
||||
"t": "eq",
|
||||
"v": "2",
|
||||
"vt": "num"
|
||||
}
|
||||
],
|
||||
"checkall": "true",
|
||||
"repair": false,
|
||||
"outputs": 2,
|
||||
"x": 910,
|
||||
"y": 280,
|
||||
"wires": [
|
||||
[
|
||||
"c43b3701443a6dfe"
|
||||
],
|
||||
[
|
||||
"aaedd1ee4c078969"
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "aaedd1ee4c078969",
|
||||
"type": "ha-sensor",
|
||||
"z": "0e938b2c7d519455",
|
||||
"name": "Wasserstand",
|
||||
"entityConfig": "11087cbf4f9379d1",
|
||||
"version": 0,
|
||||
"state": "payload",
|
||||
"stateType": "msg",
|
||||
"attributes": [],
|
||||
"inputOverride": "allow",
|
||||
"outputProperties": [],
|
||||
"x": 1130,
|
||||
"y": 300,
|
||||
"wires": [
|
||||
[
|
||||
"d547519394f23d1a"
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "d547519394f23d1a",
|
||||
"type": "debug",
|
||||
"z": "0e938b2c7d519455",
|
||||
"name": "Wasserstand",
|
||||
"active": true,
|
||||
"tosidebar": true,
|
||||
"console": false,
|
||||
"tostatus": false,
|
||||
"complete": "payload",
|
||||
"targetType": "msg",
|
||||
"statusVal": "",
|
||||
"statusType": "auto",
|
||||
"x": 1330,
|
||||
"y": 300,
|
||||
"wires": []
|
||||
},
|
||||
{
|
||||
"id": "d2f4efb6f0a3199d",
|
||||
"type": "ha-sensor",
|
||||
"z": "0e938b2c7d519455",
|
||||
"name": "Verlorene Frames",
|
||||
"entityConfig": "fd41de37db3bcf9f",
|
||||
"version": 0,
|
||||
"state": "errorCounter",
|
||||
"stateType": "msg",
|
||||
"attributes": [],
|
||||
"inputOverride": "allow",
|
||||
"outputProperties": [],
|
||||
"x": 550,
|
||||
"y": 260,
|
||||
"wires": [
|
||||
[]
|
||||
]
|
||||
}
|
||||
]
|
||||
Reference in New Issue
Block a user