Ich habe mich in letzter Zeit intensiv mit der Verbindung von Node-RED und FHEM beschäftigt. Ursprünglich hatte ich die dedizierten FHEM-Nodes im Einsatz, aber ehrlich gesagt: Wir sind keine Freunde geworden. Die Dokumentation war für mich lückenhaft, und was schwerer wiegt: Das Schreiben von Readings hat mein FHEM regelmäßig in die Knie gezwungen.
Wenn man bei der Fehlersuche mehr damit beschäftigt ist, die Logik der Nodes zu verstehen, als den eigentlichen Flow zu fixen, läuft etwas falsch.
Die Lösung: Zurück zu den Basics.
Ich habe mir drei Subflows ,,geklöppelt", die rein auf der HTTP-Schnittstelle basieren. Das ist stabil, transparent und vor allem: Es stürzt nichts mehr ab.
Die drei Werkzeuge im Überblick
Ich nutze jetzt drei spezialisierte Subflows für die tägliche Arbeit:
1. Get Reading
Hiermit hole ich mir aktiv Werte aus FHEM ab.
Konfiguration: FHEM-URL (ist im Subflow als Default hinterlegt), Device-Name, das spezifische Reading und der Datentyp (Zahl, String oder Boolean).
Funktionsweise: Der Knoten benötigt einen Trigger (z. B. eine Injection oder einen Impuls aus einem anderen Flow). Am Ausgang wird der Wert sauber in msg.payload ausgegeben.
2. Set Reading
Das Gegenstück, um Werte in FHEM zu schreiben.
Konfiguration: Identisch zum ,,Get"-Knoten, allerdings ohne Datentyp-Wahl.
Funktionsweise: Der Wert, der in FHEM geschrieben werden soll, wird einfach per msg.payload an den Eingang des Knotens gereicht.
3. Execute Command
Für die klassischen Schaltbefehle.
Konfiguration: URL, Device und der Befehl (z. B. on, off, toggle).
Einsatz: Perfekt für die schnelle Steuerung, ohne direkt Readings manipulieren zu müssen.
Warum dieser Weg?
Die Subflows sind im Grunde nur schlanke Kapseln um HTTP-Requests. Das macht sie extrem wartungsfreundlich. Wer schon mal Stunden mit der Fehlersuche in komplexen Drittanbieter-Nodes verbracht hat, wird die Einfachheit der HTTP-Web-Schnittstelle zu schätzen wissen.
JSON-Import
Für alle, die ähnliche Probleme mit den Standard-Nodes haben oder einfach eine schlanke Lösung suchen, ist hier das JSON zum Importieren in Node-RED:
[
{
"id": "sf_logic_prep",
"type": "function",
"z": "fhem_subflow_id",
"name": "URL bauen",
"func": "const baseUrl = env.get(\"FHEM_URL\");\nconst device = env.get(\"FHEM_DEVICE\");\n\nmsg.url = ${baseUrl}/fhem?cmd=jsonList%20${device}&XHR=1;\nreturn msg;",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 210,
"y": 80,
"wires": [
[
"sf_logic_http"
]
]
},
{
"id": "sf_logic_http",
"type": "http request",
"z": "fhem_subflow_id",
"name": "",
"method": "GET",
"ret": "obj",
"paytoqs": "ignore",
"url": "",
"tls": "",
"persist": false,
"proxy": "",
"authType": "",
"senderr": false,
"headers": [],
"x": 370,
"y": 80,
"wires": [
[
"sf_logic_extract"
]
]
},
{
"id": "sf_logic_extract",
"type": "function",
"z": "fhem_subflow_id",
"name": "Wert wandeln",
"func": "const readingName = env.get(\"FHEM_READING\");\nconst targetType = env.get(\"RET_TYPE\");\n\ntry {\n let rawValue = msg.payload.Results[0].Readings[readingName].Value;\n \n if (targetType === \"Zahl\") {\n msg.payload = parseFloat(rawValue.replace(',', '.'));\n } else if (targetType === \"Boolean\") {\n let low = rawValue.toLowerCase();\n msg.payload = (low === \"true\" || low === \"on\" || low === \"1\" || low === \"yes\");\n } else {\n msg.payload = String(rawValue);\n }\n \n return msg;\n} catch (e) {\n node.error(\"Reading '\" + readingName + \"' nicht gefunden!\");\n return null;\n}",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 540,
"y": 80,
"wires": [
[]
]
},
{
"id": "sf_set_logic_prep",
"type": "function",
"z": "fhem_set_subflow_id",
"name": "URL bauen",
"func": "const baseUrl = env.get(\"FHEM_URL\");\nconst device = env.get(\"FHEM_DEVICE\");\nconst reading = env.get(\"FHEM_READING\");\nconst val = msg.payload;\n\n// Wert sicherheitshalber URL-kodieren (falls Leerzeichen o.ä. enthalten sind)\nconst encodedVal = encodeURIComponent(val);\n\nmsg.url = ${baseUrl}/fhem?cmd=setreading%20${device}%20${reading}%20${encodedVal}&XHR=1;\nreturn msg;",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 220,
"y": 80,
"wires": [
[
"sf_set_logic_http"
]
]
},
{
"id": "sf_set_logic_http",
"type": "http request",
"z": "fhem_set_subflow_id",
"name": "",
"method": "GET",
"ret": "txt",
"paytoqs": "ignore",
"url": "",
"tls": "",
"persist": false,
"proxy": "",
"authType": "",
"senderr": false,
"headers": [],
"x": 410,
"y": 80,
"wires": [
[]
]
},
{
"id": "sf_cmd_logic_prep",
"type": "function",
"z": "fhem_cmd_subflow_id",
"name": "Befehl bauen",
"func": "const baseUrl = env.get(\"FHEM_URL\");\nconst device = env.get(\"FHEM_DEVICE\");\nconst command = env.get(\"FHEM_COMMAND\");\nconst val = msg.payload;\n\n// Basis-URL mit set Befehl\nlet fhemCmd = set%20${device}%20${command};\n\n// Wenn ein Wert in msg.payload mitgeliefert wird, hänge ihn an\nif (val !== undefined && val !== null && val !== \"\") {\n fhemCmd += %20${encodeURIComponent(val)};\n}\n\nmsg.url = ${baseUrl}/fhem?cmd=${fhemCmd}&XHR=1;\nreturn msg;",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 220,
"y": 80,
"wires": [
[
"sf_cmd_logic_http"
]
]
},
{
"id": "sf_cmd_logic_http",
"type": "http request",
"z": "fhem_cmd_subflow_id",
"name": "",
"method": "GET",
"ret": "txt",
"paytoqs": "ignore",
"url": "",
"tls": "",
"persist": false,
"proxy": "",
"authType": "",
"senderr": false,
"headers": [],
"x": 410,
"y": 80,
"wires": [
[]
]
}
]
Vielleicht hilft es ja dem einen oder anderen, sein Smart Home Setup etwas stabiler zu gestalten.