Temperatur und Luftfeuchtigkeit mit einem Sensor zu messen, ist zunächst nichts Besonderes. Auch die Darstellung der Werte in Home Assistant oder Node-RED ist schnell umgesetzt.
Doch was passiert, wenn wir diese Messwerte anschließend von einer KI bewerten lassen?
Genau das habe ich in diesem Projekt ausprobiert. Ein ZigBee-Sensor in meiner Wohnstube liefert Temperatur, Luftfeuchtigkeit und Batteriestand an Home Assistant. Node-RED liest diese Werte aus, bereitet sie auf und übergibt sie anschließend an Claude.
Für die Anbindung an Claude verwende ich weder einen speziellen Node-RED-Node noch direkt die Anthropic API.
Der Aufbau sieht damit so aus:
In diesem Beitrag zeige ich dir, wie ich diesen Flow aufgebaut habe und welche kleinen Stolpersteine dabei aufgetreten sind.
Mein Aufbau
Für das Projekt verwende ich einen einfachen ZigBee-Temperatur- und Luftfeuchtigkeitssensor.



Der Sensor ist über die Tuya-Integration mit Home Assistant verbunden.
Home Assistant stellt mir anschließend drei Sensorwerte als eigene Entities zur Verfügung:
- sensor.temperature_and_humidity_sensor_temperatur
- sensor.temperature_and_humidity_sensor_luftfeuchtigkeit
- sensor.temperature_and_humidity_sensor_batterie
Damit stehen mir folgende Werte zur Verfügung:
- Temperatur
- Luftfeuchtigkeit
- Batteriestand
Node-RED und Home Assistant laufen bei mir gemeinsam auf einem Fedora-System.
Zusätzlich ist auf diesem Rechner die Claude CLI installiert.
Home Assistant mit Node-RED verbinden
Damit Node-RED auf die Sensorwerte von Home Assistant zugreifen kann, verwende ich die Erweiterung: node-red-contrib-home-assistant-websocket
Long-Lived Access Token für Node-RED erstellen
Damit Node-RED auf Home Assistant zugreifen darf, benötigt die Verbindung eine Authentifizierung.
Dafür verwende ich einen sogenannten Long-Lived Access Token. Diesen kann ich direkt in Home Assistant erzeugen.
Dazu öffne ich zunächst mein Benutzerprofil und anschließend:




Als Namen verwende ich beispielsweise: Node-RED
Nach dem Erstellen zeigt Home Assistant den Token einmalig an.
Wichtig: Der Token sollte direkt kopiert und nicht öffentlich weitergegeben werden. Er ermöglicht den Zugriff auf Home Assistant mit den Rechten des Benutzers, für den er erstellt wurde. Nachträglich kann der Token nicht noch einmal angezeigt werden.
Anschließend wechsle ich zu Node-RED.
In einem Home-Assistant-Node öffne ich über das Stift-Symbol die Serverkonfiguration und trage dort meine Home-Assistant-Instanz ein.
Benötigt werden im Wesentlichen zwei Angaben:
Base URL: http://<IP-DES-HOME-ASSISTANT-SERVERS>:8123 Access Token: <MEIN-LONG-LIVED-ACCESS-TOKEN>
Bei der Base URL muss natürlich die Adresse der eigenen Home-Assistant-Installation verwendet werden.
Nach dem Speichern und einem Deploy stellt Node-RED die Verbindung zu Home Assistant her.
Die Erweiterung node-red-contrib-home-assistant-websocket verwendet dabei die REST- und WebSocket-API von Home Assistant. Eine zusätzliche API muss ich in Home Assistant dafür nicht erst aktivieren.
Sensorwerte in Node-RED auslesen
Nachdem die Verbindung steht, kann ich in den Home-Assistant-Nodes direkt auf die vorhandenen Entities zugreifen.
Für meine drei Sensorwerte verwende ich jeweils einen eigenen Events: state-Node:
Temperatur: sensor.temperature_and_humidity_sensor_temperatur Luftfeuchtigkeit: sensor.temperature_and_humidity_sensor_luftfeuchtigkeit Batteriestand: sensor.temperature_and_humidity_sensor_batterie
Alle drei Events: state-Nodes sind so konfiguriert, dass sie den aktuellen Zustand ihrer jeweiligen Entity als msg.payload ausgeben.
Zusätzlich schreibe ich die Entity-ID in: msg.topic. Damit liefert jeder Node zunächst eine eigene Nachricht. Für die spätere Auswertung durch Claude benötige ich Temperatur, Luftfeuchtigkeit und Batteriestand jedoch gemeinsam in einem Objekt.
Genau darum kümmert sich im nächsten Schritt der Join-Node.
Drei Sensorwerte mit dem Join-Node zusammenführen
Jede Änderung eines Sensors erzeugt zunächst eine eigene Nachricht.
Für Claude möchte ich aber nicht drei getrennte Nachrichten senden, sondern ein gemeinsames Objekt erzeugen.
Dafür verwende ich einen Join-Node.

Der Join-Node erstellt aus den eingehenden Nachrichten ein Objekt und verwendet dabei msg.topic als Schlüssel. Nach drei eingegangenen Werten wird das Objekt weitergegeben. In meinem Flow ist außerdem accumulate aktiviert, sodass die zuletzt bekannten Werte erhalten bleiben.
Das Ergebnis sieht beispielsweise so aus:
{
"sensor.temperature_and_humidity_sensor_temperatur": "23.0",
"sensor.temperature_and_humidity_sensor_luftfeuchtigkeit": "54.0",
"sensor.temperature_and_humidity_sensor_batterie": "100.0"
}
Damit haben wir nun alle drei Werte in einer einzigen Nachricht.

Sensordaten aufbereiten
Die Property-Namen im Objekt sind ziemlich lang.
Deshalb bereite ich sie in einem Function-Node zunächst etwas komfortabler auf.

Mein Code dafür lautet:
msg.payload = {
temperatur: msg.payload["sensor.temperature_and_humidity_sensor_temperatur"] + " °C",
luftfeuchtigkeit: msg.payload["sensor.temperature_and_humidity_sensor_luftfeuchtigkeit"] + " %",
batteriestand: msg.payload["sensor.temperature_and_humidity_sensor_batterie"] + " %"
};
return msg;
Genau diese Transformation verwendet mein aktueller Flow.
Das Ergebnis ist deutlich übersichtlicher:
{
"temperatur": "21.0 °C",
"luftfeuchtigkeit": "57.0 %",
"batteriestand": "100.0 %"
}

Warum die eckigen Klammern?
Vielleicht fällt dir an dieser Stelle folgende Schreibweise auf: msg.payload[„sensor.temperature_and_humidity_sensor_temperatur“]
Eine Schreibweise wie: msg.payload.sensor.temperature_and_humidity_sensor_temperatur würde hier nicht funktionieren.
Der Grund sind die Punkte innerhalb des Property-Namens. JavaScript würde jeden Punkt als Zugriff auf ein weiteres Unterobjekt interpretieren. Mit der Bracket-Notation können wir dagegen den kompletten Namen als Schlüssel verwenden.
Einen Prompt für Claude erstellen
Nun besitzen wir ein sauberes Objekt mit allen aktuellen Messwerten. Als Nächstes wird daraus ein Prompt für Claude erzeugt.
Dafür verwende ich einen weiteren Function-Node.
const d = msg.payload;
const prompt = `Bewerte kurz die aktuellen Raumwerte meiner Wohnstube.
Temperatur: ${d.temperatur}
Luftfeuchtigkeit: ${d.luftfeuchtigkeit}
Batteriestand des Sensors: ${d.batteriestand}
Bewerte Temperatur und Luftfeuchtigkeit in 2 bis 3 Sätzen.
Falls die Werte unauffällig sind, sage das ausdrücklich.
Gib keine allgemeinen langen Erklärungen.`;
msg.payload = Buffer.from(prompt, "utf8").toString("base64");
return msg;
Auch dieser Code entspricht meinem aktuellen Node-RED-Flow.
Aus Messwerten wie:
- 20 °C
- 55 %
- 100 %
entsteht damit beispielsweise dieser Prompt:
Bewerte kurz die aktuellen Raumwerte meiner Wohnstube. Temperatur: 20 °C Luftfeuchtigkeit: 55 % Batteriestand des Sensors: 100 % Bewerte Temperatur und Luftfeuchtigkeit in 2 bis 3 Sätzen. Falls die Werte unauffällig sind, sage das ausdrücklich. Gib keine allgemeinen langen Erklärungen.
Warum wird der Prompt mit Base64 kodiert?
Eigentlich könnten wir diesen Text direkt an ein Shell-Skript übergeben. In der Praxis haben Prompts allerdings Leerzeichen, Zeilenumbrüche, Umlaute und möglicherweise auch verschiedene Sonderzeichen.
Das kann bei der Übergabe als Kommandozeilenargument schnell unangenehm werden.
Deshalb kodiere ich den kompletten Prompt zunächst mit Base64:
msg.payload = Buffer.from(prompt, "utf8").toString("base64");
Dadurch entsteht beispielsweise: QmV3ZXJ0ZSBrdXJ6IGRpZSBha3R1ZWxsZW4gUmF1bXdlcnRl...
Dieser String lässt sich problemlos als Kommandozeilenargument übergeben.
Im Shell-Skript wird er anschließend wieder dekodiert.
Claude aus Node-RED starten
Node-RED besitzt bereits einen Exec-Node, mit dem Programme oder Shell-Skripte gestartet werden können.

Ich rufe darüber folgendes Skript auf:
/home/stefandraeger/claude-temp/call-claude.sh
Dabei ist der Exec-Node so eingestellt, dass msg.payload als Argument angehängt wird.

Intern entsteht dadurch ungefähr folgender Aufruf: /home/stefandraeger/claude-temp/call-claude.sh QmV3ZXJ0ZSBrdXJ6...
Das Shell-Skript
Mein funktionierendes Shell-Skript sieht so aus:
#!/usr/bin/env bash set -euo pipefail PROMPT="$(printf '%s' "$1" | base64 --decode)" exec sudo -iu stefandraeger \ /home/stefandraeger/.local/bin/claude \ -p "$PROMPT" \ --output-format text \ < /dev/null
Zunächst wird der Base64-String wieder dekodiert:
PROMPT="$(printf '%s' "$1" | base64 --decode)"
Danach wird die Claude CLI aufgerufen:
/home/stefandraeger/.local/bin/claude
Mit:
-p "$PROMPT"
übergebe ich unseren zuvor erzeugten Prompt.
Und:
--output-format text
sorgt dafür, dass Claude seine Antwort als normalen Text zurückliefert.
Ein wichtiger Stolperstein: Node-RED läuft als anderer Benutzer
Beim ersten Test trat allerdings ein Problem mit dem Benutzerkontext auf. Mein Node-RED-Prozess läuft als root. Die Claude CLI wurde dagegen für meinen normalen Linux-Benutzer eingerichtet.
Der erste Ansatz war: "$HOME/.local/bin/claude", das funktionierte aus Node-RED jedoch nicht.
Node-RED interpretierte $HOME als: /root und versuchte Claude deshalb hier zu finden: /root/.local/bin/claude
Claude ist bei mir aber hier installiert: /home/stefandraeger/.local/bin/claude
Nachdem ich den absoluten Pfad verwendet hatte, trat das nächste Problem auf.
Claude suchte seine Einstellungen teilweise unter: /root/.claude/ anstatt unter meinem Benutzerverzeichnis.
Die entscheidende Lösung war schließlich: sudo -iu stefandraeger
Damit wird Claude nicht einfach nur unter einem anderen Benutzer gestartet, sondern innerhalb der Login-Umgebung dieses Benutzers.
Das vollständige Kommando lautet deshalb:
exec sudo -iu stefandraeger \ /home/stefandraeger/.local/bin/claude \ -p "$PROMPT" \ --output-format text \ < /dev/null
Damit verwendet Claude auch die bereits vorhandene Konfiguration und Authentifizierung meines Benutzers.
Die drei Ausgänge des Exec-Nodes
Der Exec-Node besitzt drei Ausgänge.
Diese nutze ich während der Entwicklung vollständig aus.
Mein aktueller Flow besitzt dafür drei Debug-Nodes mit den Namen:
- stdout
- stderr
- Rückgabe-Code
Das ist insbesondere beim Debuggen sehr hilfreich.
Die eigentliche Antwort von Claude erscheint über: stdout
Fehlermeldungen der Claude CLI oder des Shell-Skripts erscheinen dagegen unter: stderr
Der dritte Ausgang zeigt schließlich den Exit-Code des Prozesses.
Ein erfolgreicher Aufruf liefert normalerweise: 0
Der komplette Flow
Der aktuelle Ablauf sieht damit vereinfacht so aus:
Node-RED Flow zum absenden und auswerten lassen von Sensorwerte über KI
[
{
"id": "14d1fc0e74f50efc",
"type": "tab",
"label": "Flow 1",
"disabled": false,
"info": "",
"env": []
},
{
"id": "8b9eccf6804f27aa",
"type": "server-state-changed",
"z": "14d1fc0e74f50efc",
"name": "",
"server": "7e0937d34f528bf7",
"version": 6,
"outputs": 1,
"exposeAsEntityConfig": "",
"entities": {
"entity": [
"sensor.temperature_and_humidity_sensor_temperatur"
],
"substring": [],
"regex": []
},
"outputInitially": true,
"stateType": "str",
"ifState": "",
"ifStateType": "str",
"ifStateOperator": "is",
"outputOnlyOnStateChange": true,
"for": "0",
"forType": "num",
"forUnits": "minutes",
"ignorePrevStateNull": false,
"ignorePrevStateUnknown": false,
"ignorePrevStateUnavailable": false,
"ignoreCurrentStateUnknown": false,
"ignoreCurrentStateUnavailable": false,
"outputProperties": [
{
"property": "payload",
"propertyType": "msg",
"value": "string",
"valueType": "entityState"
},
{
"property": "data",
"propertyType": "msg",
"value": "",
"valueType": "eventData"
},
{
"property": "topic",
"propertyType": "msg",
"value": "",
"valueType": "triggerId"
}
],
"x": 290,
"y": 60,
"wires": [
[
"b4504d264bd97134"
]
]
},
{
"id": "5787a86f4e5df738",
"type": "server-state-changed",
"z": "14d1fc0e74f50efc",
"name": "",
"server": "7e0937d34f528bf7",
"version": 6,
"outputs": 1,
"exposeAsEntityConfig": "",
"entities": {
"entity": [
"sensor.temperature_and_humidity_sensor_luftfeuchtigkeit"
],
"substring": [],
"regex": []
},
"outputInitially": true,
"stateType": "str",
"ifState": "",
"ifStateType": "str",
"ifStateOperator": "is",
"outputOnlyOnStateChange": true,
"for": "0",
"forType": "num",
"forUnits": "minutes",
"ignorePrevStateNull": false,
"ignorePrevStateUnknown": false,
"ignorePrevStateUnavailable": false,
"ignoreCurrentStateUnknown": false,
"ignoreCurrentStateUnavailable": false,
"outputProperties": [
{
"property": "payload",
"propertyType": "msg",
"value": "string",
"valueType": "entityState"
},
{
"property": "data",
"propertyType": "msg",
"value": "",
"valueType": "eventData"
},
{
"property": "topic",
"propertyType": "msg",
"value": "",
"valueType": "triggerId"
}
],
"x": 300,
"y": 140,
"wires": [
[
"b4504d264bd97134"
]
]
},
{
"id": "3687d7038af6bb09",
"type": "server-state-changed",
"z": "14d1fc0e74f50efc",
"name": "",
"server": "7e0937d34f528bf7",
"version": 6,
"outputs": 1,
"exposeAsEntityConfig": "",
"entities": {
"entity": [
"sensor.temperature_and_humidity_sensor_batterie"
],
"substring": [],
"regex": []
},
"outputInitially": true,
"stateType": "str",
"ifState": "",
"ifStateType": "str",
"ifStateOperator": "is",
"outputOnlyOnStateChange": true,
"for": "0",
"forType": "num",
"forUnits": "minutes",
"ignorePrevStateNull": false,
"ignorePrevStateUnknown": false,
"ignorePrevStateUnavailable": false,
"ignoreCurrentStateUnknown": false,
"ignoreCurrentStateUnavailable": false,
"outputProperties": [
{
"property": "payload",
"propertyType": "msg",
"value": "string",
"valueType": "entityState"
},
{
"property": "data",
"propertyType": "msg",
"value": "",
"valueType": "eventData"
},
{
"property": "topic",
"propertyType": "msg",
"value": "",
"valueType": "triggerId"
}
],
"x": 280,
"y": 220,
"wires": [
[
"b4504d264bd97134"
]
]
},
{
"id": "b4504d264bd97134",
"type": "join",
"z": "14d1fc0e74f50efc",
"name": "",
"mode": "custom",
"build": "object",
"property": "payload",
"propertyType": "msg",
"key": "topic",
"joiner": "\\n",
"joinerType": "str",
"useparts": false,
"accumulate": true,
"timeout": "",
"count": "3",
"reduceRight": false,
"reduceExp": "",
"reduceInit": "",
"reduceInitType": "",
"reduceFixup": "",
"x": 670,
"y": 140,
"wires": [
[
"a3043b410c3b98f9"
]
]
},
{
"id": "a3043b410c3b98f9",
"type": "function",
"z": "14d1fc0e74f50efc",
"name": "Daten aufbereiten",
"func": "msg.payload = {\n temperatur: msg.payload[\"sensor.temperature_and_humidity_sensor_temperatur\"] + \" °C\",\n luftfeuchtigkeit: msg.payload[\"sensor.temperature_and_humidity_sensor_luftfeuchtigkeit\"] + \" %\",\n batteriestand: msg.payload[\"sensor.temperature_and_humidity_sensor_batterie\"] + \" %\"\n};\n\nreturn msg;",
"outputs": 1,
"timeout": 0,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 830,
"y": 140,
"wires": [
[
"034b34f9d7c73607"
]
]
},
{
"id": "034b34f9d7c73607",
"type": "link out",
"z": "14d1fc0e74f50efc",
"name": "link out 1",
"mode": "link",
"links": [
"753a03ecbfc1e6b3"
],
"x": 985,
"y": 140,
"wires": []
},
{
"id": "753a03ecbfc1e6b3",
"type": "link in",
"z": "14d1fc0e74f50efc",
"name": "Daten an Claude senden",
"links": [
"034b34f9d7c73607"
],
"x": 45,
"y": 340,
"wires": [
[
"61bff54d98c40f5f",
"31aca9da3508b39c"
]
]
},
{
"id": "61bff54d98c40f5f",
"type": "function",
"z": "14d1fc0e74f50efc",
"name": "Daten für Claude aufbereiten",
"func": "const d = msg.payload;\n\nconst prompt = `Bewerte kurz die aktuellen Raumwerte meiner Wohnstube.\n\nTemperatur: ${d.temperatur}\nLuftfeuchtigkeit: ${d.luftfeuchtigkeit}\nBatteriestand des Sensors: ${d.batteriestand}\n\nBewerte Temperatur und Luftfeuchtigkeit in 2 bis 3 Sätzen.\nFalls die Werte unauffällig sind, sage das ausdrücklich.\nGib keine allgemeinen langen Erklärungen.`;\n\nmsg.payload = Buffer.from(prompt, \"utf8\").toString(\"base64\");\n\nreturn msg;",
"outputs": 1,
"timeout": 0,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 240,
"y": 340,
"wires": [
[
"9fd7113980777b9e"
]
]
},
{
"id": "68080cdf583b012c",
"type": "debug",
"z": "14d1fc0e74f50efc",
"name": "debug 2",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "false",
"statusVal": "",
"statusType": "auto",
"x": 520,
"y": 340,
"wires": []
},
{
"id": "9fd7113980777b9e",
"type": "exec",
"z": "14d1fc0e74f50efc",
"command": "/home/stefandraeger/claude-temp/call-claude.sh",
"addpay": "payload",
"append": "",
"useSpawn": "false",
"timer": "",
"winHide": false,
"oldrc": false,
"name": "",
"x": 580,
"y": 340,
"wires": [
[
"26011a806c1254a5"
],
[
"02e7f6e5f0e5d358"
],
[
"b02285126a2fe781"
]
]
},
{
"id": "26011a806c1254a5",
"type": "debug",
"z": "14d1fc0e74f50efc",
"name": "stdout",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "payload",
"targetType": "msg",
"statusVal": "",
"statusType": "auto",
"x": 870,
"y": 280,
"wires": []
},
{
"id": "31aca9da3508b39c",
"type": "debug",
"z": "14d1fc0e74f50efc",
"name": "Ausgabe der Sensordaten",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "payload",
"targetType": "msg",
"statusVal": "",
"statusType": "auto",
"x": 230,
"y": 420,
"wires": []
},
{
"id": "02e7f6e5f0e5d358",
"type": "function",
"z": "14d1fc0e74f50efc",
"name": "Fehler loggen",
"func": "node.error(msg.payload);\nreturn msg;",
"outputs": 1,
"timeout": 0,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 900,
"y": 340,
"wires": [
[]
]
},
{
"id": "b02285126a2fe781",
"type": "function",
"z": "14d1fc0e74f50efc",
"name": "Rückgabecode loggen",
"func": "let returncode = msg.payload.code;\n\nif (returncode == 0){\n node.status({fill:\"green\",shape:\"ring\",text:\"OK\"});\n} else {\n node.status({fill:\"red\",shape:\"ring\",text:\"NOK\"});\n}\n\nreturn msg;",
"outputs": 1,
"timeout": 0,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 920,
"y": 400,
"wires": [
[]
]
},
{
"id": "7e0937d34f528bf7",
"type": "server",
"name": "Home Assistant",
"version": 6,
"addon": false,
"rejectUnauthorizedCerts": true,
"ha_boolean": [
"y",
"yes",
"true",
"on",
"home",
"open"
],
"connectionDelay": true,
"cacheJson": true,
"heartbeat": false,
"heartbeatInterval": 30,
"areaSelector": "friendlyName",
"deviceSelector": "friendlyName",
"entitySelector": "friendlyName",
"statusSeparator": ": ",
"statusYear": "hidden",
"statusMonth": "short",
"statusDay": "numeric",
"statusHourCycle": "default",
"statusTimeFormat": "h:m",
"enableGlobalContextStore": false
},
{
"id": "3f1d0559ca784f13",
"type": "global-config",
"env": [],
"modules": {
"node-red-contrib-home-assistant-websocket": "0.82.0"
}
}
]
Die drei Home-Assistant-Sensoren werden zunächst gesammelt und anschließend als gemeinsames Objekt weiterverarbeitet.
Anschließend erzeugt Node-RED den Prompt und übergibt ihn über den Exec-Node an das Shell-Skript.
Warum verwende ich nicht direkt die Anthropic API?
Für dieses Projekt verwende ich bewusst nicht die Anthropic API.
Der wichtigste Grund dafür ist, dass die API-Nutzung separat abgerechnet wird. Meine normale Claude-Subscription deckt diese API-Aufrufe nicht ab.
Da die Claude CLI auf meinem System ohnehin bereits eingerichtet ist, kann ich sie stattdessen über ein Shell-Skript aus Node-RED heraus starten. Damit nutze ich Claude über meine bestehende Subscription und brauche für diesen Entwicklungsstand keine zusätzlichen API-Kosten einzuplanen.
Der CLI-Weg hat allerdings einen Nachteil: Die Anmeldung läuft nach einiger Zeit ab.
Die Claude CLI arbeitet mit einer Anmeldung, deren Session nach einiger Zeit ablaufen kann. In meinem Test musste ich den Login deshalb bereits erneut durchführen.
Für ein Projekt, das sich noch in der Entwicklung befindet, ist das für mich aktuell akzeptabel.
Für einen produktiven oder dauerhaft unbeaufsichtigten Einsatz würde ich diesen Aufbau allerdings nicht verwenden. In diesem Fall wäre die direkte Anbindung über die Anthropic API die robustere Lösung, da dort keine regelmäßig zu erneuernde interaktive Anmeldung erforderlich ist.
Warum nicht bei jeder Änderung Claude aufrufen?
Mein aktueller Flow reagiert auf Änderungen der Home-Assistant-Entities.
Für einen Test ist das vollkommen ausreichend.
Für den späteren Dauerbetrieb möchte ich Claude allerdings nicht bei jeder kleinen Änderung der Luftfeuchtigkeit aufrufen.
Eine Änderung von: 54 % auf: 55 % rechtfertigt schließlich nicht unbedingt eine neue KI-Analyse.
Denkbar wären beispielsweise folgende Regeln: Claude alle 30 Minuten aufrufen oder: nur bei deutlich veränderten Messwerten oder sogar: nur bei auffälligen Werten
Beispielsweise könnte Node-RED zunächst selbst einfache Grenzwerte prüfen und Claude erst dann hinzuziehen, wenn eine detailliertere Bewertung sinnvoll ist.
Damit bleibt die einfache Logik in Node-RED und Claude wird nur dann aufgerufen, wenn wirklich eine Bewertung benötigt wird.
Node-RED übernimmt weiterhin die klar definierten Regeln.
Claude wird erst dort eingesetzt, wo aus mehreren Informationen eine verständliche Bewertung erzeugt werden soll.
Wie geht es weiter?
Der aktuelle Flow ist für mich zunächst ein Proof of Concept.
Im nächsten Schritt möchte ich die von Claude erzeugte Bewertung nicht nur im Debug-Fenster von Node-RED anzeigen, sondern visuell aufbereiten und auf einem ePaper-Display darstellen.
Die Idee dahinter: Node-RED sammelt die Sensordaten, übergibt diese an Claude und erhält eine kurze textliche Einschätzung zurück. Diese Bewertung soll anschließend in ein ansprechend aufbereitetes Bild überführt werden, das sich auf einem ePaper-Display anzeigen lässt.
Für die Bildgenerierung bietet sich in meinem Fall ein Ansatz an, den ich bereits in einem anderen Projekt verwende: die Erzeugung einer PNG-Datei mit XSLT und Apache FOP. Damit könnte ich die KI-Auswertung zusammen mit Temperatur, Luftfeuchtigkeit, Zeitstempel und eventuell kleinen Statussymbolen in ein festes Layout bringen.
Der Ablauf könnte dann später so aussehen:
Technisch sind dabei zwei Wege denkbar:
- Die PNG-Datei wird auf einem Linux-System oder Raspberry Pi erzeugt und anschließend von einem ePaper-Display per WLAN geladen.
- Alternativ wird die generierte Auswertung direkt auf einem Display dargestellt, sofern die verwendete Hardware und Bibliothek dies zulassen.



Für meinen Aufbau bevorzuge ich die erste Variante, weil sich Layout und Display-Logik sauber voneinander trennen lassen. Das Layout kann komplett getrennt von der Display-Logik entwickelt werden. Node-RED und Claude liefern den Inhalt, ein Skript erzeugt daraus ein Bild und das Display ruft dieses anschließend regelmäßig ab.
Dadurch ließe sich aus dem aktuellen Testaufbau eine kleine, stromsparende Statusanzeige bauen, die nicht nur rohe Messwerte darstellt, sondern bereits eine verständliche KI-Bewertung liefert – zum Beispiel:
- Temperatur angenehm
- Luftfeuchtigkeit leicht erhöht
- Lüften empfohlen
Gerade auf einem ePaper-Display wäre das eine schöne Möglichkeit, die Daten nicht nur technisch, sondern auch alltagstauglich und gut lesbar aufzubereiten.
Fazit
Der aktuelle Aufbau zeigt, dass sich Claude mit relativ wenig Aufwand in einen bestehenden Node-RED-Flow integrieren lässt.
Home Assistant stellt die Messwerte meines ZigBee-Sensors bereit, Node-RED fasst Temperatur, Luftfeuchtigkeit und Batteriestand zusammen und erzeugt daraus einen Prompt. Über ein Shell-Skript wird anschließend die lokal installierte Claude CLI aufgerufen und die Bewertung wieder an Node-RED zurückgegeben.
Der Datenfluss ist damit überschaubar:
Für mich ist dieser Aufbau aktuell vor allem ein funktionierender Proof of Concept. Die eigentliche Automatisierung bleibt weiterhin in Node-RED, während Claude nur die aufbereiteten Sensordaten bewertet.
Als nächstes möchte ich die Antwort nicht nur im Debug-Fenster anzeigen, sondern grafisch aufbereiten und auf einem ePaper-Display darstellen. Damit würde aus dem Testaufbau eine kompakte Statusanzeige, die Messwerte und KI-Bewertung direkt zusammenführt.
Für einen späteren produktiven Dauerbetrieb würde ich außerdem die direkte Anthropic API bevorzugen. Während der Entwicklung ist der Weg über die Claude CLI für mich jedoch eine einfache Möglichkeit, die Idee ohne zusätzliche API-Kosten auszuprobieren.
Letzte Aktualisierung am: 24. Oktober 2026








