Weiter zum Inhalt

Node-Red

969 Themen 13.3k Beiträge

Hilfe für Skripterstellung mit Node-RED

NEWS

  • tcp Node disconnect per dashboard button Node

    11
    1
    0 Stimmen
    11 Beiträge
    128 Aufrufe
    R
    So klappt es endlich!!! Vielen Dank für die freundliche Hilfe, das hätte ich nie hinbekommen!!!
  • Node-RED Nodes für externe ioBroker Integration

    communication node-red
    133
    4
    7 Stimmen
    133 Beiträge
    29k Aufrufe
    A
    Vielen Dank für die Node, bis jetzt funktioniert alles einwandfrei
  • gelöst: ich brauche etwas Nachhilfe mit Node Red und RS232

    78
    3
    0 Stimmen
    78 Beiträge
    2k Aufrufe
    A
    Ich denke ich habe es geschafft und zeige hier den Flow, für alle die vor einem ählichen Thema stehen. [ { "id": "924b846988fa37b8", "type": "tab", "label": "Pytes", "disabled": false, "info": "", "env": [] }, { "id": "7b0f9153dc441129", "type": "inject", "z": "924b846988fa37b8", "name": "Start Impuls", "props": [ { "p": "payload" } ], "repeat": "900", "crontab": "", "once": false, "onceDelay": 0.1, "topic": "", "payload": "bat", "payloadType": "str", "x": 270, "y": 420, "wires": [ [ "93bb9e44683dd328", "00be40db9cc98e10", "7d79635bc32374b4", "a7456323ba755bf3", "7b0d5d1b02bd9085", "f3f5ae187655f871", "92a88797a1b73b4a", "03519a44cb4f244f", "2b0172f542977f16" ] ] }, { "id": "b377e7183086a062", "type": "trigger", "z": "924b846988fa37b8", "name": "bat 1", "op1": "bat 1", "op2": "", "op1type": "str", "op2type": "nul", "duration": "250", "extend": false, "overrideDelay": false, "units": "ms", "reset": "", "bytopic": "all", "topic": "topic", "outputs": 1, "x": 950, "y": 420, "wires": [ [ "26225d8717897af1" ] ] }, { "id": "26225d8717897af1", "type": "serial request", "z": "924b846988fa37b8", "name": "BMSMaster", "serial": "d031dd424e7b1ff2", "x": 1250, "y": 420, "wires": [ [ "b2c11fc7a1bd0a9c" ] ] }, { "id": "30b978183f66ca0b", "type": "debug", "z": "924b846988fa37b8", "name": "debug 4", "active": true, "tosidebar": true, "console": false, "tostatus": false, "complete": "true", "targetType": "full", "statusVal": "", "statusType": "auto", "x": 2100, "y": 500, "wires": [] }, { "id": "be4f995ff9ae4db1", "type": "function", "z": "924b846988fa37b8", "name": "Nachrichten erzeugen", "func": "if (msg.messageType !== \"data\") {\n return null;\n}\n\nvar f_battery = flow.get(\"f_battery\");\n\nconst battery = msg.payload;\n\nif (!battery || battery.battery === undefined) {\n node.error(\"Ungültiges Battery-Objekt\", msg);\n return null;\n}\n\nconst batteryId = battery.battery;\n\nconst states = [\n {\n id: \"voltage\",\n value: battery.volt,\n name: \"Voltage\",\n role: \"value.voltage\",\n type: \"number\",\n readonly: true,\n unit: \"mV\",\n min: 0\n },\n {\n id: \"temperature\",\n value: battery.tempr / 1000,\n name: \"Temperature\",\n role: \"value.temperature\",\n type: \"number\",\n readonly: true,\n unit: \"°C\"\n },\n {\n id: \"baseState\",\n value: battery.baseState,\n name: \"Base State\",\n role: \"text\",\n type: \"string\",\n readonly: true\n },\n {\n id: \"voltageState\",\n value: battery.voltState,\n name: \"Voltage State\",\n role: \"text\",\n type: \"string\",\n readonly: true\n },\n {\n id: \"currentState\",\n value: battery.currState,\n name: \"Current State\",\n role: \"text\",\n type: \"string\",\n readonly: true\n },\n {\n id: \"temperatureState\",\n value: battery.tempState,\n name: \"Temperature State\",\n role: \"text\",\n type: \"string\",\n readonly: true\n },\n {\n id: \"charge\",\n value: battery.coulomb,\n name: \"Charge\",\n role: \"value.battery\",\n type: \"number\",\n readonly: true,\n unit: \"%\",\n min: 0,\n max: 100\n },\n {\n id: \"capacity\",\n value: battery.capacity,\n name: \"Capacity\",\n role: \"value\",\n type: \"number\",\n readonly: true,\n unit: \"mAh\",\n min: 0\n }\n];\n\nconst messages = [];\n\nfor (const state of states) {\n const out = {\n topic: `0_userdata.0.pytes2.${f_battery}.${batteryId}.${state.id}`,\n payload: state.value,\n\n stateName: state.name,\n stateRole: state.role,\n stateType: state.type,\n stateReadonly: state.readonly\n };\n\n if (state.unit !== undefined) {\n out.stateUnit = state.unit;\n }\n\n if (state.min !== undefined) {\n out.stateMin = state.min;\n }\n\n if (state.max !== undefined) {\n out.stateMax = state.max;\n }\n\n messages.push(out);\n}\n\nreturn [messages];", "outputs": 1, "timeout": 0, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 1860, "y": 420, "wires": [ [ "30b978183f66ca0b", "26c696e68b092904" ] ] }, { "id": "93bb9e44683dd328", "type": "function", "z": "924b846988fa37b8", "name": "bat 1", "func": "var f_battery = flow.get('f_battery') || 1;\nf_battery = 1;\nflow.set('f_battery', f_battery);\nmsg.payload=f_battery;\nreturn msg;", "outputs": 1, "timeout": 0, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 810, "y": 420, "wires": [ [ "b377e7183086a062" ] ] }, { "id": "8ccdf3ea686387b8", "type": "trigger", "z": "924b846988fa37b8", "name": "bat 2", "op1": "bat 2", "op2": "", "op1type": "str", "op2type": "nul", "duration": "250", "extend": false, "overrideDelay": false, "units": "ms", "reset": "", "bytopic": "all", "topic": "topic", "outputs": 1, "x": 950, "y": 520, "wires": [ [ "26225d8717897af1" ] ] }, { "id": "489520c23b161924", "type": "function", "z": "924b846988fa37b8", "name": "bat 2", "func": "var f_battery = flow.get('f_battery') || 1;\nf_battery = 2;\nflow.set('f_battery', f_battery);\nmsg.payload=f_battery;\nreturn msg;", "outputs": 1, "timeout": 0, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 810, "y": 520, "wires": [ [ "8ccdf3ea686387b8" ] ] }, { "id": "00be40db9cc98e10", "type": "delay", "z": "924b846988fa37b8", "name": "", "pauseType": "delay", "timeout": "1", "timeoutUnits": "minutes", "rate": "1", "nbRateUnits": "1", "rateUnits": "second", "randomFirst": "1", "randomLast": "5", "randomUnits": "seconds", "drop": false, "allowrate": false, "outputs": 1, "x": 640, "y": 520, "wires": [ [ "489520c23b161924" ] ] }, { "id": "ae16d8955790d92a", "type": "trigger", "z": "924b846988fa37b8", "name": "bat 3", "op1": "bat 3", "op2": "", "op1type": "str", "op2type": "nul", "duration": "250", "extend": false, "overrideDelay": false, "units": "ms", "reset": "", "bytopic": "all", "topic": "topic", "outputs": 1, "x": 950, "y": 620, "wires": [ [ "26225d8717897af1" ] ] }, { "id": "4538197122a9bdc2", "type": "function", "z": "924b846988fa37b8", "name": "bat 3", "func": "var f_battery = flow.get('f_battery') || 1;\nf_battery = 3;\nflow.set('f_battery', f_battery);\nmsg.payload=f_battery;\nreturn msg;", "outputs": 1, "timeout": 0, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 810, "y": 620, "wires": [ [ "ae16d8955790d92a" ] ] }, { "id": "7d79635bc32374b4", "type": "delay", "z": "924b846988fa37b8", "name": "", "pauseType": "delay", "timeout": "2", "timeoutUnits": "minutes", "rate": "1", "nbRateUnits": "1", "rateUnits": "second", "randomFirst": "1", "randomLast": "5", "randomUnits": "seconds", "drop": false, "allowrate": false, "outputs": 1, "x": 640, "y": 620, "wires": [ [ "4538197122a9bdc2" ] ] }, { "id": "faf99396662dd9fe", "type": "trigger", "z": "924b846988fa37b8", "name": "bat 4", "op1": "bat 4", "op2": "", "op1type": "str", "op2type": "nul", "duration": "250", "extend": false, "overrideDelay": false, "units": "ms", "reset": "", "bytopic": "all", "topic": "topic", "outputs": 1, "x": 950, "y": 720, "wires": [ [ "26225d8717897af1" ] ] }, { "id": "2935dde14a7e9b69", "type": "function", "z": "924b846988fa37b8", "name": "bat 4", "func": "var f_battery = flow.get('f_battery') || 1;\nf_battery = 4;\nflow.set('f_battery', f_battery);\nmsg.payload=f_battery;\nreturn msg;", "outputs": 1, "timeout": 0, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 810, "y": 720, "wires": [ [ "faf99396662dd9fe" ] ] }, { "id": "a7456323ba755bf3", "type": "delay", "z": "924b846988fa37b8", "name": "", "pauseType": "delay", "timeout": "3", "timeoutUnits": "minutes", "rate": "1", "nbRateUnits": "1", "rateUnits": "second", "randomFirst": "1", "randomLast": "5", "randomUnits": "seconds", "drop": false, "allowrate": false, "outputs": 1, "x": 640, "y": 720, "wires": [ [ "2935dde14a7e9b69" ] ] }, { "id": "0283b7e720d4eae8", "type": "trigger", "z": "924b846988fa37b8", "name": "bat 5", "op1": "bat 5", "op2": "", "op1type": "str", "op2type": "nul", "duration": "250", "extend": false, "overrideDelay": false, "units": "ms", "reset": "", "bytopic": "all", "topic": "topic", "outputs": 1, "x": 950, "y": 820, "wires": [ [ "26225d8717897af1" ] ] }, { "id": "035e99dbc09bc62e", "type": "function", "z": "924b846988fa37b8", "name": "bat 5", "func": "var f_battery = flow.get('f_battery') || 1;\nf_battery = 5;\nflow.set('f_battery', f_battery);\nmsg.payload=f_battery;\nreturn msg;", "outputs": 1, "timeout": 0, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 810, "y": 820, "wires": [ [ "0283b7e720d4eae8" ] ] }, { "id": "7b0d5d1b02bd9085", "type": "delay", "z": "924b846988fa37b8", "name": "", "pauseType": "delay", "timeout": "4", "timeoutUnits": "minutes", "rate": "1", "nbRateUnits": "1", "rateUnits": "second", "randomFirst": "1", "randomLast": "5", "randomUnits": "seconds", "drop": false, "allowrate": false, "outputs": 1, "x": 640, "y": 820, "wires": [ [ "035e99dbc09bc62e" ] ] }, { "id": "caa332d68e18fa0e", "type": "trigger", "z": "924b846988fa37b8", "name": "bat 6", "op1": "bat 6", "op2": "", "op1type": "str", "op2type": "nul", "duration": "250", "extend": false, "overrideDelay": false, "units": "ms", "reset": "", "bytopic": "all", "topic": "topic", "outputs": 1, "x": 950, "y": 920, "wires": [ [ "26225d8717897af1" ] ] }, { "id": "f0b60bc3dd7fab8b", "type": "function", "z": "924b846988fa37b8", "name": "bat 6", "func": "var f_battery = flow.get('f_battery') || 1;\nf_battery = 6;\nflow.set('f_battery', f_battery);\nmsg.payload=f_battery;\nreturn msg;", "outputs": 1, "timeout": 0, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 810, "y": 920, "wires": [ [ "caa332d68e18fa0e" ] ] }, { "id": "f3f5ae187655f871", "type": "delay", "z": "924b846988fa37b8", "name": "", "pauseType": "delay", "timeout": "5", "timeoutUnits": "minutes", "rate": "1", "nbRateUnits": "1", "rateUnits": "second", "randomFirst": "1", "randomLast": "5", "randomUnits": "seconds", "drop": false, "allowrate": false, "outputs": 1, "x": 640, "y": 920, "wires": [ [ "f0b60bc3dd7fab8b" ] ] }, { "id": "a4f9934bfcf077f9", "type": "trigger", "z": "924b846988fa37b8", "name": "bat 7", "op1": "bat 7", "op2": "", "op1type": "str", "op2type": "nul", "duration": "250", "extend": false, "overrideDelay": false, "units": "ms", "reset": "", "bytopic": "all", "topic": "topic", "outputs": 1, "x": 950, "y": 1020, "wires": [ [ "26225d8717897af1" ] ] }, { "id": "225333085c3b1b95", "type": "function", "z": "924b846988fa37b8", "name": "bat 7", "func": "var f_battery = flow.get('f_battery') || 1;\nf_battery = 7;\nflow.set('f_battery', f_battery);\nmsg.payload=f_battery;\nreturn msg;", "outputs": 1, "timeout": 0, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 810, "y": 1020, "wires": [ [ "a4f9934bfcf077f9" ] ] }, { "id": "92a88797a1b73b4a", "type": "delay", "z": "924b846988fa37b8", "name": "", "pauseType": "delay", "timeout": "6", "timeoutUnits": "minutes", "rate": "1", "nbRateUnits": "1", "rateUnits": "second", "randomFirst": "1", "randomLast": "5", "randomUnits": "seconds", "drop": false, "allowrate": false, "outputs": 1, "x": 640, "y": 1020, "wires": [ [ "225333085c3b1b95" ] ] }, { "id": "9089dacc8e8b8393", "type": "trigger", "z": "924b846988fa37b8", "name": "bat 8", "op1": "bat 8", "op2": "", "op1type": "str", "op2type": "nul", "duration": "250", "extend": false, "overrideDelay": false, "units": "ms", "reset": "", "bytopic": "all", "topic": "topic", "outputs": 1, "x": 950, "y": 1120, "wires": [ [ "26225d8717897af1" ] ] }, { "id": "2a37874391c9c5d0", "type": "function", "z": "924b846988fa37b8", "name": "bat 8", "func": "var f_battery = flow.get('f_battery') || 1;\nf_battery = 8;\nflow.set('f_battery', f_battery);\nmsg.payload=f_battery;\nreturn msg;", "outputs": 1, "timeout": 0, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 810, "y": 1120, "wires": [ [ "9089dacc8e8b8393" ] ] }, { "id": "03519a44cb4f244f", "type": "delay", "z": "924b846988fa37b8", "name": "", "pauseType": "delay", "timeout": "7", "timeoutUnits": "minutes", "rate": "1", "nbRateUnits": "1", "rateUnits": "second", "randomFirst": "1", "randomLast": "5", "randomUnits": "seconds", "drop": false, "allowrate": false, "outputs": 1, "x": 640, "y": 1120, "wires": [ [ "2a37874391c9c5d0" ] ] }, { "id": "f4a30319262f08c0", "type": "catch", "z": "924b846988fa37b8", "name": "", "scope": "group", "uncaught": false, "x": 1370, "y": 300, "wires": [ [ "676dc7e85bab0fb0" ] ] }, { "id": "2948459dbbd003a5", "type": "debug", "z": "924b846988fa37b8", "name": "debug 1", "active": false, "tosidebar": true, "console": false, "tostatus": false, "complete": "false", "statusVal": "", "statusType": "auto", "x": 1700, "y": 560, "wires": [] }, { "id": "76351a67c6019345", "type": "function", "z": "924b846988fa37b8", "name": "parser 232", "func": "/*\n * Robuster Parser für RS232-Batteriedaten\n *\n * Eigenschaften:\n * - verarbeitet fragmentierte Serial-Daten über mehrere msg.payload hinweg\n * - erkennt Beginn eines Datensatzes anhand der Headerzeile\n * - erkennt Ende anhand \"Command completed successfully\"\n * - erwartet Batterien 0..15 genau einmal\n * - verwirft unvollständige Datensätze komplett\n * - prüft Zahlen, Prozentwert, Capacity und grobe Wertebereiche\n * - erzeugt Diagnosemeldungen für Warnungen/Fehler\n * - schreibt Warnungen zusätzlich in die Node-RED Debug-/Log-Ausgabe\n *\n * EIN Ausgang:\n * msg.messageType === \"data\" -> gültige Batteriedaten\n * msg.messageType === \"warning\" -> Diagnose/Warnung\n * msg.messageType === \"error\" -> Diagnose/Fehler\n */\n \n \n// ------------------------------------------------------------\n// Konfiguration\n// ------------------------------------------------------------\n \nconst EXPECTED_BATTERIES = 16;\n \n// Maximale Zeit, die ein begonnener Datensatz offen bleiben darf.\n// Die Prüfung erfolgt beim nächsten eintreffenden msg.\nconst BLOCK_TIMEOUT_MS = 30000;\n \n// Schutz gegen unbegrenzt anwachsende Fragmentpuffer\nconst MAX_FRAGMENT_LENGTH = 4096;\n \n// Schutz gegen völlig entartete Datensätze\nconst MAX_BLOCK_LINES = 100;\n \n// Sehr großzügige Plausibilitätsgrenzen.\n// Diese können später an die tatsächliche Anlage angepasst werden.\nconst LIMITS = {\n voltMin: 0,\n voltMax: 10000,\n \n temprMin: -100000,\n temprMax: 200000,\n \n coulombMin: 0,\n coulombMax: 100,\n \n capacityMin: 0,\n capacityMax: 1000000\n};\n \n \n// ------------------------------------------------------------\n// Eingang prüfen\n// ------------------------------------------------------------\n \nconst text = msg.payload;\n \nif (typeof text !== \"string\") {\n \n const diagnostic = createDiagnostic(\n \"error\",\n \"INVALID_PAYLOAD_TYPE\",\n \"msg.payload ist kein String\",\n {\n receivedType: typeof text\n }\n );\n \n node.error(diagnostic, msg);\n \n return [[diagnostic]];\n}\n \n \n// ------------------------------------------------------------\n// Zustand aus Node-Context holen\n// ------------------------------------------------------------\n \nlet fragment = context.get(\"serialFragment\") || \"\";\nlet block = context.get(\"batteryBlock\") || null;\n \n \n// ------------------------------------------------------------\n// Ausgaben dieser Function-Ausführung\n// ------------------------------------------------------------\n \nconst output = [];\n \n \n// ------------------------------------------------------------\n// Diagnosefunktion\n// ------------------------------------------------------------\n \nfunction createDiagnostic(severity, code, message, details) {\n \n return {\n ...msg,\n \n messageType: severity,\n \n payload: {\n severity: severity,\n code: code,\n message: message,\n timestamp: new Date().toISOString(),\n details: details || {}\n }\n };\n}\n \n \nfunction diagnostic(severity, code, message, details) {\n \n const diag = createDiagnostic(\n severity,\n code,\n message,\n details\n );\n \n output.push(diag);\n \n // Zusätzlich in Node-RED sichtbar machen\n if (severity === \"error\") {\n node.error(\n code + \": \" + message +\n (details ? \" | \" + JSON.stringify(details) : \"\")\n );\n } else {\n node.warn(\n code + \": \" + message +\n (details ? \" | \" + JSON.stringify(details) : \"\")\n );\n }\n}\n \n \n// ------------------------------------------------------------\n// Hilfsfunktionen\n// ------------------------------------------------------------\n \nfunction isHeader(line) {\n \n return /^Battery\\s+Volt\\s+Tempr\\s+Base\\s+State/i.test(line);\n}\n \n \nfunction isEndMarker(line) {\n \n return /^Command completed successfully\\s*$/i.test(line);\n}\n \n \nfunction isPromptOrNoise(line) {\n \n return (\n line === \"\" ||\n line === \"$$\" ||\n line === \"@\" ||\n /^PYTES>/i.test(line)\n );\n}\n \n \n// ------------------------------------------------------------\n// Batteriezeile parsen\n// ------------------------------------------------------------\n \nfunction parseBatteryLine(line) {\n \n /*\n * Beispiel:\n *\n * 0 3462 28000 Charge Normal\n * Normal Normal 97% 95980 mAH\n *\n * Absichtlich nicht nur split(/\\s{2,}/), damit kleinere\n * Änderungen der Abstände nicht sofort zum Fehler führen.\n */\n \n const match = line.match(\n /^(\\d+)\\s+(-?\\d+(?:\\.\\d+)?)\\s+(-?\\d+(?:\\.\\d+)?)\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)\\s+(\\d+(?:\\.\\d+)?)%\\s+(\\d+)\\s*mAH\\s*$/i\n );\n \n if (!match) {\n return {\n valid: false,\n reason: \"FORMAT_ERROR\"\n };\n }\n \n const battery = {\n battery: Number(match[1]),\n volt: Number(match[2]),\n tempr: Number(match[3]),\n baseState: match[4],\n voltState: match[5],\n currState: match[6],\n tempState: match[7],\n coulomb: Number(match[8]),\n capacity: Number(match[9])\n };\n \n \n // --------------------------------------------------------\n // Datentypen prüfen\n // --------------------------------------------------------\n \n const numericValues = [\n battery.battery,\n battery.volt,\n battery.tempr,\n battery.coulomb,\n battery.capacity\n ];\n \n if (numericValues.some(v => !Number.isFinite(v))) {\n \n return {\n valid: false,\n reason: \"INVALID_NUMBER\",\n battery: battery\n };\n }\n \n \n // --------------------------------------------------------\n // Harte Plausibilitätsprüfungen\n // --------------------------------------------------------\n \n if (\n battery.battery < 0 ||\n battery.battery >= EXPECTED_BATTERIES\n ) {\n return {\n valid: false,\n reason: \"INVALID_BATTERY_NUMBER\",\n battery: battery\n };\n }\n \n \n if (\n battery.coulomb < LIMITS.coulombMin ||\n battery.coulomb > LIMITS.coulombMax\n ) {\n return {\n valid: false,\n reason: \"INVALID_COULOMB_VALUE\",\n battery: battery\n };\n }\n \n \n if (\n battery.capacity < LIMITS.capacityMin ||\n battery.capacity > LIMITS.capacityMax\n ) {\n return {\n valid: false,\n reason: \"INVALID_CAPACITY_VALUE\",\n battery: battery\n };\n }\n \n \n if (\n battery.volt < LIMITS.voltMin ||\n battery.volt > LIMITS.voltMax\n ) {\n return {\n valid: false,\n reason: \"INVALID_VOLTAGE_VALUE\",\n battery: battery\n };\n }\n \n \n if (\n battery.tempr < LIMITS.temprMin ||\n battery.tempr > LIMITS.temprMax\n ) {\n return {\n valid: false,\n reason: \"INVALID_TEMPERATURE_VALUE\",\n battery: battery\n };\n }\n \n \n return {\n valid: true,\n battery: battery\n };\n}\n \n \n// ------------------------------------------------------------\n// Angefangenen Block verwerfen\n// ------------------------------------------------------------\n \nfunction discardBlock(reason, additionalDetails) {\n \n if (!block) {\n return;\n }\n \n const receivedBatteries = Object.keys(block.batteries)\n .map(Number)\n .sort((a, b) => a - b);\n \n diagnostic(\n \"warning\",\n \"BLOCK_DISCARDED\",\n \"Unvollständiger oder beschädigter Batteriedatensatz wurde verworfen.\",\n {\n reason: reason,\n receivedBatteryCount: receivedBatteries.length,\n receivedBatteries: receivedBatteries,\n invalidLines: block.invalidLines,\n startedAt: new Date(block.startedAt).toISOString(),\n ...additionalDetails\n }\n );\n \n block = null;\n}\n \n \n// ------------------------------------------------------------\n// Fertigen Block validieren\n// ------------------------------------------------------------\n \nfunction finishBlock() {\n \n if (!block) {\n \n diagnostic(\n \"warning\",\n \"END_WITHOUT_HEADER\",\n \"Blockende erkannt, obwohl kein aktiver Datensatz vorhanden war.\"\n );\n \n return;\n }\n \n \n const batteryNumbers = Object.keys(block.batteries)\n .map(Number)\n .sort((a, b) => a - b);\n \n \n // Erwartete Batterien erzeugen\n const expected = [];\n \n for (let i = 0; i < EXPECTED_BATTERIES; i++) {\n expected.push(i);\n }\n \n \n const missing = expected.filter(\n batteryNumber => !batteryNumbers.includes(batteryNumber)\n );\n \n \n // --------------------------------------------------------\n // Datensatz unvollständig\n // --------------------------------------------------------\n \n if (\n batteryNumbers.length !== EXPECTED_BATTERIES ||\n missing.length > 0 ||\n block.invalidLines.length > 0\n ) {\n \n diagnostic(\n \"warning\",\n \"INCOMPLETE_DATASET\",\n \"Datensatz wurde vollständig empfangen beendet, enthält aber nicht alle erwarteten gültigen Batteriedaten.\",\n {\n expectedBatteryCount: EXPECTED_BATTERIES,\n receivedBatteryCount: batteryNumbers.length,\n receivedBatteries: batteryNumbers,\n missingBatteries: missing,\n invalidLines: block.invalidLines,\n startedAt: new Date(block.startedAt).toISOString(),\n durationMs: Date.now() - block.startedAt\n }\n );\n \n block = null;\n \n return;\n }\n \n \n // --------------------------------------------------------\n // Vollständigen Datensatz ausgeben\n // --------------------------------------------------------\n \n const batchId =\n Date.now().toString(36) +\n \"-\" +\n Math.random().toString(36).substring(2, 8);\n \n \n for (let i = 0; i < EXPECTED_BATTERIES; i++) {\n \n output.push({\n ...msg,\n \n messageType: \"data\",\n \n batchId: batchId,\n \n dataQuality: {\n valid: true,\n completeDataset: true,\n batteryCount: EXPECTED_BATTERIES\n },\n \n payload: block.batteries[i]\n });\n }\n \n \n // optional im Node-Status sichtbar\n node.status({\n fill: \"green\",\n shape: \"dot\",\n text: \"16 Batterien OK\"\n });\n \n \n block = null;\n}\n \n \n// ------------------------------------------------------------\n// Einzelne komplette Textzeile verarbeiten\n// ------------------------------------------------------------\n \nfunction processLine(rawLine) {\n \n const line = rawLine.trim();\n \n \n // --------------------------------------------------------\n // Header\n // --------------------------------------------------------\n \n if (isHeader(line)) {\n \n // Es läuft bereits ein Block:\n // der vorherige wurde offensichtlich nicht sauber beendet.\n if (block) {\n \n discardBlock(\n \"NEW_HEADER_BEFORE_END\",\n {\n newHeader: line\n }\n );\n }\n \n \n block = {\n startedAt: Date.now(),\n batteries: {},\n invalidLines: [],\n lineCount: 0\n };\n \n \n node.status({\n fill: \"yellow\",\n shape: \"ring\",\n text: \"Datensatz empfangen...\"\n });\n \n return;\n }\n \n \n // --------------------------------------------------------\n // Blockende\n // --------------------------------------------------------\n \n if (isEndMarker(line)) {\n \n finishBlock();\n \n return;\n }\n \n \n // --------------------------------------------------------\n // bekannte Prompt-/Steuerzeilen ignorieren\n // --------------------------------------------------------\n \n if (isPromptOrNoise(line)) {\n return;\n }\n \n \n // --------------------------------------------------------\n // Außerhalb eines Blocks\n // --------------------------------------------------------\n \n if (!block) {\n \n // Batteriezeile außerhalb eines Headers ist verdächtig\n if (/^\\d+\\s/.test(line)) {\n \n diagnostic(\n \"warning\",\n \"DATA_OUTSIDE_BLOCK\",\n \"Batteriezeile außerhalb eines gültigen Datensatzes erkannt und verworfen.\",\n {\n rawLine: line\n }\n );\n }\n \n return;\n }\n \n \n block.lineCount++;\n \n \n // --------------------------------------------------------\n // Sicherheitslimit\n // --------------------------------------------------------\n \n if (block.lineCount > MAX_BLOCK_LINES) {\n \n discardBlock(\n \"TOO_MANY_LINES\",\n {\n lineCount: block.lineCount\n }\n );\n \n return;\n }\n \n \n // --------------------------------------------------------\n // Nur Zeilen, die mit Batterie-ID anfangen, interessieren\n // --------------------------------------------------------\n \n if (!/^\\d+\\s/.test(line)) {\n \n diagnostic(\n \"warning\",\n \"UNEXPECTED_LINE\",\n \"Unerwartete Zeile innerhalb eines Batteriedatensatzes.\",\n {\n rawLine: line\n }\n );\n \n return;\n }\n \n \n // --------------------------------------------------------\n // Batteriezeile parsen\n // --------------------------------------------------------\n \n const parsed = parseBatteryLine(line);\n \n \n if (!parsed.valid) {\n \n block.invalidLines.push({\n line: line,\n reason: parsed.reason\n });\n \n \n diagnostic(\n \"warning\",\n \"INVALID_BATTERY_LINE\",\n \"Batteriezeile konnte nicht sicher verarbeitet werden.\",\n {\n reason: parsed.reason,\n rawLine: line,\n parsedData: parsed.battery || null\n }\n );\n \n return;\n }\n \n \n const batteryNumber = parsed.battery.battery;\n \n \n // --------------------------------------------------------\n // Doppelte Batterie-ID\n // --------------------------------------------------------\n \n if (block.batteries[batteryNumber] !== undefined) {\n \n block.invalidLines.push({\n line: line,\n reason: \"DUPLICATE_BATTERY\"\n });\n \n \n diagnostic(\n \"warning\",\n \"DUPLICATE_BATTERY\",\n \"Batterienummer wurde innerhalb desselben Datensatzes mehrfach empfangen.\",\n {\n battery: batteryNumber,\n rawLine: line\n }\n );\n \n return;\n }\n \n \n block.batteries[batteryNumber] = parsed.battery;\n}\n \n \n// ------------------------------------------------------------\n// Timeout des vorherigen Blocks prüfen\n// ------------------------------------------------------------\n \nif (\n block &&\n Date.now() - block.startedAt > BLOCK_TIMEOUT_MS\n) {\n \n discardBlock(\n \"BLOCK_TIMEOUT\",\n {\n timeoutMs: BLOCK_TIMEOUT_MS\n }\n );\n}\n \n \n// ------------------------------------------------------------\n// Fragmentierung der seriellen Schnittstelle behandeln\n// ------------------------------------------------------------\n \nfragment += text;\n \n \n// Schutz gegen kaputte / niemals abgeschlossene Eingabe\nif (fragment.length > MAX_FRAGMENT_LENGTH) {\n \n diagnostic(\n \"error\",\n \"SERIAL_FRAGMENT_OVERFLOW\",\n \"Der serielle Fragmentpuffer wurde zu groß und wurde zurückgesetzt.\",\n {\n fragmentLength: fragment.length,\n maxFragmentLength: MAX_FRAGMENT_LENGTH,\n fragmentPreview: fragment.substring(0, 500)\n }\n );\n \n fragment = \"\";\n \n discardBlock(\n \"SERIAL_FRAGMENT_OVERFLOW\"\n );\n}\n \n \n// ------------------------------------------------------------\n// Zeilenenden erkennen\n//\n// Unterstützt:\n// \\r\\n\n// \\n\n// \\r\n// ------------------------------------------------------------\n \nconst parts = fragment.split(/\\r\\n|\\n|\\r/);\n \n \n// Das letzte Element könnte eine noch unvollständige Zeile sein\nfragment = parts.pop() || \"\";\n \n \n// Alle sicher abgeschlossenen Zeilen verarbeiten\nfor (const line of parts) {\n processLine(line);\n}\n \n \n// ------------------------------------------------------------\n// Sonderfall:\n// Serial-Node liefert möglicherweise bereits jeweils EINE komplette\n// Zeile, entfernt aber selbst das Zeilenende.\n//\n// Wenn das aktuelle Fragment eindeutig wie eine vollständige\n// relevante Zeile aussieht, kann es sofort verarbeitet werden.\n// ------------------------------------------------------------\n \nconst trimmedFragment = fragment.trim();\n \nif (trimmedFragment !== \"\") {\n \n const completeBatteryLine =\n parseBatteryLine(trimmedFragment).valid;\n \n const obviousCompleteLine =\n isHeader(trimmedFragment) ||\n isEndMarker(trimmedFragment) ||\n isPromptOrNoise(trimmedFragment) ||\n completeBatteryLine;\n \n if (obviousCompleteLine) {\n \n processLine(trimmedFragment);\n \n fragment = \"\";\n }\n}\n \n \n// ------------------------------------------------------------\n// Zustand speichern\n// ------------------------------------------------------------\n \ncontext.set(\"serialFragment\", fragment);\ncontext.set(\"batteryBlock\", block);\n \n \n// ------------------------------------------------------------\n// Node-Status bei Fehlern/Warnungen\n// ------------------------------------------------------------\n \nif (output.some(m => m.messageType === \"error\")) {\n \n node.status({\n fill: \"red\",\n shape: \"dot\",\n text: \"RS232 Fehler\"\n });\n \n} else if (output.some(m => m.messageType === \"warning\")) {\n \n node.status({\n fill: \"yellow\",\n shape: \"dot\",\n text: \"RS232 Warnung\"\n });\n}\n \n \n// ------------------------------------------------------------\n// Alle Nachrichten nacheinander über denselben Ausgang senden\n// ------------------------------------------------------------\n \nreturn [output];", "outputs": 1, "timeout": 0, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 1610, "y": 420, "wires": [ [ "be4f995ff9ae4db1", "2948459dbbd003a5" ] ] }, { "id": "ddc4662ffce50bd4", "type": "serial control", "z": "924b846988fa37b8", "name": "", "serial": "d031dd424e7b1ff2", "x": 2070, "y": 240, "wires": [ [] ] }, { "id": "676dc7e85bab0fb0", "type": "function", "z": "924b846988fa37b8", "name": "RS232 schliessen", "func": "// @ts-nocheck\nmsg.payload = {\n \"serialport\": \"/dev/serial/by-id/usb-FTDI_USB_Serial_Converter_FTFS8I5L-if00-port0\",\n \"serialbaud\": \"115200\",\n \"databits\": 8,\n \"parity\": \"none\",\n \"stopbits\": 1,\n \"enabled\": false\n};\n\nreturn msg;\n", "outputs": 1, "timeout": 0, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 1630, "y": 300, "wires": [ [ "ddc4662ffce50bd4" ] ] }, { "id": "a5b021a6e4d25d63", "type": "inject", "z": "924b846988fa37b8", "name": "", "props": [ { "p": "payload" }, { "p": "topic", "vt": "str" } ], "repeat": "", "crontab": "", "once": false, "onceDelay": 0.1, "topic": "", "payload": "", "payloadType": "date", "x": 1380, "y": 340, "wires": [ [ "676dc7e85bab0fb0" ] ] }, { "id": "2b0172f542977f16", "type": "function", "z": "924b846988fa37b8", "name": "RS232 öffnen", "func": "// @ts-nocheck\nmsg.payload = {\n \"serialport\": \"/dev/serial/by-id/usb-FTDI_USB_Serial_Converter_FTFS8I5L-if00-port0\",\n \"serialbaud\": \"115200\",\n \"databits\": 8,\n \"parity\": \"none\",\n \"stopbits\": 1,\n \"enabled\": true\n};\n\nreturn msg;\n", "outputs": 1, "timeout": 0, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 480, "y": 240, "wires": [ [ "ddc4662ffce50bd4" ] ] }, { "id": "58e6f27bc04e9d56", "type": "inject", "z": "924b846988fa37b8", "name": "", "props": [ { "p": "payload" }, { "p": "topic", "vt": "str" } ], "repeat": "", "crontab": "", "once": false, "onceDelay": 0.1, "topic": "", "payload": "", "payloadType": "date", "x": 280, "y": 240, "wires": [ [ "2b0172f542977f16" ] ] }, { "id": "b2c11fc7a1bd0a9c", "type": "switch", "z": "924b846988fa37b8", "name": "", "property": "payload", "propertyType": "msg", "rules": [ { "t": "cont", "v": "completed", "vt": "str" }, { "t": "else" } ], "checkall": "true", "repair": false, "outputs": 2, "x": 1430, "y": 420, "wires": [ [ "76351a67c6019345" ], [ "2948459dbbd003a5", "676dc7e85bab0fb0" ] ] }, { "id": "26c696e68b092904", "type": "iobout", "z": "924b846988fa37b8", "name": "iobroker", "state": "", "inputProperty": "payload", "setMode": "value", "autoCreate": true, "stateName": "", "stateRole": "", "payloadType": "", "stateReadonly": "", "stateUnit": "", "stateMin": "", "stateMax": "", "enableHistory": false, "historyAdapter": "", "historyTemplate": "", "historySettings": "", "server": "0e0cc42ad0c78fbc", "x": 2100, "y": 420, "wires": [] }, { "id": "d031dd424e7b1ff2", "type": "serial-port", "name": "Pytes", "serialport": "/dev/serial/by-id/usb-FTDI_USB_Serial_Converter_FTFS8I5L-if00-port0", "serialbaud": "115200", "databits": 8, "parity": "none", "stopbits": 1, "waitfor": "@", "dtr": "none", "rts": "none", "cts": "none", "dsr": "none", "newline": "250", "bin": "false", "out": "time", "addchar": "\\r\\n", "responsetimeout": "250" }, { "id": "0e0cc42ad0c78fbc", "type": "iob-config", "name": "IoB Datenpunkte", "iobhost": "192.168.130.10", "iobport": "8091", "usessl": false, "isDefault": false }, { "id": "1b8fbb2c72c666d5", "type": "global-config", "env": [], "modules": { "node-red-node-serialport": "2.0.3", "node-red-contrib-iobroker": "1.4.1" } } ] [image: Bildschirmfoto vom 2026-09-19 17-46-53.png] Dieser Flow läuft unterdessen in einem eigenen LXC-Container, weil sich mein Iobroker sehr seltsam verhalten hat, wenn die RS232 Schnittstelle der Pytes eine Störung hat. Da es unterdessen Nods gibt mit denen man Daten direkt in die Datenbunkte aus Node-Red einfügen kann, ist das Problem einfach umgangen. IoB Node-Red Nods Es ist absolut klar, die Profis hier werden es besser und schlanker programieren können, aber jeder beginnt am Anfang. Ein Tip an die Profis: schaut euch doch den vorhanden Adapter an, dann wären alle diese Bemühungen hier gar nicht notwenidig gewesen, oder korrigiert die Beschreibung im Adapter zu welchen genauen Typen der Pytes BMS er passen soll. Danke
  • welche Palette zur Einbindung von Alexa Sprachsteuerung?

    6
    0 Stimmen
    6 Beiträge
    244 Aufrufe
    Dr. BakteriusD
    Doppelpost - kann gelöscht werden
  • (gelöst) Node-Red V5?? wann kommt das Update?

    6
    0 Stimmen
    6 Beiträge
    224 Aufrufe
    mcm1957M
    habe es wollen über GitHub installieren, selbst in der Beta-Repository leider keinen Update eintrag. Tja fällt unter ss (selber schuld) :-) Es wird immer wieder davon ABGERATEN direkt von GitHub zu installieren. Und immer mehr Adapter werden dies auch nicht mehr fehlerfrei unterstützen. Also schön langsam dran geöhnen nur dann von GitHub was versuchen zu installieren wenn der Dev es explizit im Zuge einer Fehlersuche angeboten bzw drum ersucht hat.
  • How to set host name?

    11
    1
    0 Stimmen
    11 Beiträge
    398 Aufrufe
    Marc BergM
    @HomeUser Das Issue ist aber aus meiner Sicht beim Node RED Adapter an der falschen Stelle. Das ist ein Admin Adapter Thema.
  • Proble bei Neuinstalation von Node Red

    4
    1
    0 Stimmen
    4 Beiträge
    150 Aufrufe
    T
    Vielen Dank für eure Hilfe! Die "npm i node-red-contrib-sun-position@2.1.1" hat funktioniert. Was ich nicht so ganz verstehe: Das Backup wurde mit NodeRed gesichert. Beim Einspielen des NodeRed-Backups wurde nicht der richtige Flow eingespielt. Liegt es vielleicht daran, dass automatisch die neueste Version des Flows eingespielt wird, die mit der vorigen Installation nicht funktioniert? Vielen Dank nochmals!
  • node-red-contrib-sun-position .... Fehler nach update

    3
    3
    0 Stimmen
    3 Beiträge
    109 Aufrufe
    A
    @Marc-Berg sagte: @ak68 sagte: Wie kann ich den Adapter manuell updaten ? Gar nicht, du müsstest warten, bis der Adapter hochgezogen wird. Alternativ den sun-position Node downgraden, oder benötigst du das Update fachlich? Danke, habe jetzt down-gegraded :(
  • Zeit in NodeRed Adapter

    11
    0 Stimmen
    11 Beiträge
    288 Aufrufe
    Marc BergM
    @Thomas-Braun sagte: In der Konfigurationsdatei von Node Red, also in der settings.js, kann durch hinzufügen der Zeitzone das Problem behoben werden. Wenn man den Adapter nutzt, werden Änderungen in der settings.js immer wieder überschrieben. Das bringt nichts. @taddy81 Was konkret ist denn das Problem? Lösen die inject Nodes falsch aus? Oder geht es nur um diese Meldung, bei der niemand weiß, woher sie kommt? @Taddy81 sagte: Auszug: 7/26/2026, 11:15:23 AMnode: debug 3msg.payload : string[24]
  • Node-Red: Flow über ein Javascript neu starten

    6
    1
    0 Stimmen
    6 Beiträge
    2k Aufrufe
    Marc BergM
    Man kann einen Flow von außen neu starten, indem man ihn per API deaktiviert und wieder aktiviert. Nicht sehr elegant, aber geht. Hier ein Flow dazu, das könnte man auch im Skript abbilden (hostname und Flow-ID im Inject Node anpassen: [ { "id": "0101b52dd8b0e69c", "type": "inject", "z": "tab_restart", "name": "Flow neu starten", "props": [ { "p": "baseUrl", "v": "http://nuc.fritz.box:1880", "vt": "str" }, { "p": "flowId", "v": "56a35d28dbb8d4c1", "vt": "str" } ], "repeat": "", "crontab": "", "once": false, "onceDelay": "", "topic": "", "x": 160, "y": 140, "wires": [ [ "ffb73632947a1b74" ] ] }, { "id": "ffb73632947a1b74", "type": "http request", "z": "tab_restart", "name": "GET /flow/:id", "method": "GET", "ret": "obj", "paytoqs": "ignore", "url": "{{{baseUrl}}}/flow/{{flowId}}", "tls": "", "persist": false, "proxy": "", "insecureHTTPParser": false, "authType": "", "senderr": false, "headers": [], "x": 370, "y": 140, "wires": [ [ "78e47724692d9c26" ] ] }, { "id": "78e47724692d9c26", "type": "change", "z": "tab_restart", "name": "deaktivieren", "rules": [ { "t": "set", "p": "flow", "pt": "msg", "to": "payload", "tot": "msg" }, { "t": "set", "p": "flow.disabled", "pt": "msg", "to": "true", "tot": "bool" }, { "t": "set", "p": "payload", "pt": "msg", "to": "flow", "tot": "msg" }, { "t": "set", "p": "headers", "pt": "msg", "to": "{}", "tot": "json" }, { "t": "set", "p": "headers.content-type", "pt": "msg", "to": "application/json", "tot": "str" } ], "action": "", "property": "", "from": "", "to": "", "reg": false, "x": 570, "y": 140, "wires": [ [ "b570b346c80301a6" ] ] }, { "id": "b570b346c80301a6", "type": "http request", "z": "tab_restart", "name": "PUT (aus)", "method": "PUT", "ret": "obj", "paytoqs": "ignore", "url": "{{{baseUrl}}}/flow/{{flowId}}", "tls": "", "persist": false, "proxy": "", "insecureHTTPParser": false, "authType": "", "senderr": false, "headers": [], "x": 770, "y": 140, "wires": [ [ "a062c2baa0932d3e" ] ] }, { "id": "a062c2baa0932d3e", "type": "delay", "z": "tab_restart", "name": "1s warten", "pauseType": "delay", "timeout": "1", "timeoutUnits": "seconds", "rate": "1", "nbRateUnits": "1", "rateUnits": "second", "randomFirst": "1", "randomLast": "5", "randomUnits": "seconds", "drop": false, "outputs": 1, "x": 960, "y": 140, "wires": [ [ "664a32887d83b305" ] ] }, { "id": "664a32887d83b305", "type": "change", "z": "tab_restart", "name": "aktivieren", "rules": [ { "t": "set", "p": "flow.disabled", "pt": "msg", "to": "false", "tot": "bool" }, { "t": "set", "p": "payload", "pt": "msg", "to": "flow", "tot": "msg" }, { "t": "set", "p": "headers", "pt": "msg", "to": "{}", "tot": "json" }, { "t": "set", "p": "headers.content-type", "pt": "msg", "to": "application/json", "tot": "str" } ], "action": "", "property": "", "from": "", "to": "", "reg": false, "x": 140, "y": 220, "wires": [ [ "83d399c90d46240f" ] ] }, { "id": "83d399c90d46240f", "type": "http request", "z": "tab_restart", "name": "PUT (ein)", "method": "PUT", "ret": "obj", "paytoqs": "ignore", "url": "{{{baseUrl}}}/flow/{{flowId}}", "tls": "", "persist": false, "proxy": "", "insecureHTTPParser": false, "authType": "", "senderr": false, "headers": [], "x": 320, "y": 220, "wires": [ [ "529c0e1896d1f278" ] ] }, { "id": "529c0e1896d1f278", "type": "debug", "z": "tab_restart", "name": "Ergebnis", "active": true, "tosidebar": true, "console": false, "tostatus": false, "complete": "true", "targetType": "full", "statusVal": "", "statusType": "auto", "x": 480, "y": 220, "wires": [] } ]
  • Ansteuerung Sonoff SWV-ZFE Ventil

    8
    1
    0 Stimmen
    8 Beiträge
    916 Aufrufe
    webwalker2001W
    Hab den Adapter aktualisiert, jetzt wird das Ventil erkannt. Danke
  • Shelly Addon Temperatur Fühler in Victron CerboGX einfügen

    9
    1
    0 Stimmen
    9 Beiträge
    900 Aufrufe
    G
    Hallo, die Temperatur wird im dashboard ausgegeben, aber nicht in der CerboGX, Danke erstmal. Mir wurde dieses Forum empfohlen !
  • alexa findet nichts bzw verzögerte Ausführung

    9
    1
    0 Stimmen
    9 Beiträge
    928 Aufrufe
    Samson71S
    @peterfido Stimmt, die hatte ich übersehen. Danke für den Hinweis.
  • [durch backitup gelöst] Fehler beim Start von Nodered

    7
    0 Stimmen
    7 Beiträge
    826 Aufrufe
    M
    @geschild Du kannst in der Instanz den RAM erhöhen. Standard ist 128MB. Könnte etwas knapp sein. [image: 4987c65c-722e-46f5-8638-4db4554a203d-image.jpeg]
  • Node-Red Datenpunkt Anwahl

    12
    1
    0 Stimmen
    12 Beiträge
    2k Aufrufe
    K
    Danke, hatte ich aktiviert. Nachdem ich es aus hatte, geht das im Node-Red wieder. Ich war schon verzweifelt, das Problem war seit ein paar Wochen da und ich wusste nicht woran es liegt. Ein Haken weg und schon geht alles. Die Datenpunkte habe ich mit einem Adapter-Update lösen können. Nun geht die Kommunikation zwischen den Objekten und Node-Red wieder.
  • Laufzeit eines Ausgangs zählen, dann Funktion ausführen

    node-red
    1
    0 Stimmen
    1 Beiträge
    382 Aufrufe
    Niemand hat geantwortet
  • Aktivierung in bestimmtem Zeitraum (erledigt!)

    8
    0 Stimmen
    8 Beiträge
    986 Aufrufe
    mickymM
    @Tontechniker Man kann solche Filter aber auch OHNE neue Nodes zu installieren mit einfachen Standard-Switch Nodes realisieren: [image: af3cab3c-2716-43f4-b7df-7f74e8bf051b-image.jpeg] ggf. einfach nochmal meinen alten Thread anschauen: https://forum.iobroker.net/topic/50086/datum-und-zeitverarbeitung-mit-nodered
  • Nodered Wemo Emulation und Alexa Echo Erledigt

    2
    0 Stimmen
    2 Beiträge
    733 Aufrufe
    F
    Wie so oft erledigen sich die meisten Dinge mit liegen lassen. Jetzt läuft es stabil. Gruss achim
  • Node Red UND Verknüpfung

    7
    1
    0 Stimmen
    7 Beiträge
    3k Aufrufe
    S
    welche Palette hast du installiert, damit du den "AND" Baustein hast?
  • Hilfe bei Node red benötigt.

    Verschoben
    37
    1
    0 Stimmen
    37 Beiträge
    3k Aufrufe
    Thomas BraunT
    Für Portscans von extern verwende ich immer https://www.grc.com/x/ne.dll?bh0bkyd2

222

Online

33.1k

Benutzende

83.9k

Themen

1.4m

Beiträge