Skip to content

Technik Blog

IoT, Smart Home & Mikrocontroller einfach erklärt

Menu
  • Smarthome
  • Gartenautomation
  • Mikrocontroller
    • Arduino
    • ESP32 & Co.
    • Calliope Mini
    • Raspberry Pi & Pico
  • Solo Mining
  • Über mich
Menu

Claude mit Node-RED verbinden: Sensordaten aus Home Assistant per KI auswerten

Veröffentlicht am 26. Oktober 202624. Oktober 2026 von Stefan Draeger

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:

Datenfluss der KI-Auswertung
Datenfluss der KI-Auswertung

In diesem Beitrag zeige ich dir, wie ich diesen Flow aufgebaut habe und welche kleinen Stolpersteine dabei aufgetreten sind.

Home Assistant + Node-RED + Claude: Sensordaten automatisch mit KI auswerten
Dieses Video auf YouTube ansehen.

Inhaltsverzeichnis

  • Mein Aufbau
  • Home Assistant mit Node-RED verbinden
    • Long-Lived Access Token für Node-RED erstellen
    • Sensorwerte in Node-RED auslesen
  • Drei Sensorwerte mit dem Join-Node zusammenführen
  • Sensordaten aufbereiten
    • Warum die eckigen Klammern?
  • Einen Prompt für Claude erstellen
  • Warum wird der Prompt mit Base64 kodiert?
  • Claude aus Node-RED starten
  • Das Shell-Skript
  • Ein wichtiger Stolperstein: Node-RED läuft als anderer Benutzer
  • Die drei Ausgänge des Exec-Nodes
  • Der komplette Flow
  • Warum verwende ich nicht direkt die Anthropic API?
  • Warum nicht bei jeder Änderung Claude aufrufen?
  • Wie geht es weiter?
  • Fazit

Mein Aufbau

Für das Projekt verwende ich einen einfachen ZigBee-Temperatur- und Luftfeuchtigkeitssensor.

Lieferumfang - ZigBee Temperatur & Luftfeuchtigkeitssensor
Lieferumfang – ZigBee Temperatur & Luftfeuchtigkeitssensor
Rückseite mit Batteriefach und Reset-Knopf des ZigBee Sensors
Rückseite mit Batteriefach und Reset-Knopf des ZigBee Sensors
eingelegte AAA Batterien im ZigBee Temperatursensor
eingelegte AAA Batterien im ZigBee Temperatursensor

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
Home Assistant - Sensordaten des ZigBee Sensors
Home Assistant – Sensordaten des ZigBee Sensors

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.

Einstellung für Home Assistant Server in Node-RED
Einstellung für Home Assistant Server in Node-RED

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.

Auswahl des Sensorwertes in der Event-State Node
Auswahl des Sensorwertes in der Event-State Node

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.

Join Node
join Node in Node-RED zum zusammenfassen von mehreren Nachrichten
join Node in Node-RED zum zusammenfassen von mehreren Nachrichten

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.

Einstellung der join Node zum zusammenfassen von drei Nachrichten
Einstellung der join Node zum zusammenfassen von drei Nachrichten

Sensordaten aufbereiten

Die Property-Namen im Objekt sind ziemlich lang.

Deshalb bereite ich sie in einem Function-Node zunächst etwas komfortabler auf.

Node-RED - function-Node

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 %"
}
Sensordaten im JSON-Format
Sensordaten im JSON-Format

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.

Einstellung der exec Node für die übergabe eines Payloads
Einstellung der exec Node für die übergabe eines Payloads

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
Ausgabe call-claude Skript
Ausgabe call-claude Skript

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.

exec Node in Node-RED mit drei Ausgängen
exec Node in Node-RED mit drei Ausgängen

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 Auswerten von Sensorwerten via Claude AI
Node-RED Flow zum Auswerten von Sensorwerten via Claude AI
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:

Datenfluss zum ePaper-Display
Datenfluss zum ePaper-Display

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.
ePaper Displays mit Analyse der Sensorwerte von Claude
ePaper Displays mit Analyse der Sensorwerte von Claude
6 Zoll ePaper Display mit analyse der Sensorwerte von Claude
6 Zoll ePaper Display mit analyse der Sensorwerte von Claude
10 Zoll graustufen ePaper Display mit Sensorwerten und analyse dieser von Claude
10 Zoll graustufen ePaper Display mit Sensorwerten und analyse dieser von Claude

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:

Datenfluss der KI-Auswertung
Datenfluss der KI-Auswertung

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.

Transparenzhinweis: Dieser Beitrag wurde mit Unterstützung künstlicher Intelligenz erstellt und redaktionell geprüft. Mehr zur Nutzung von KI

Letzte Aktualisierung am: 24. Oktober 2026

Foto von Stefan Draeger
Über den Autor

Stefan Draeger — Entwickler & Tech-Blogger

Ich zeige praxisnah, wie du Projekte mit Arduino, ESP32 und Smarthome-Komponenten umsetzt – Schritt für Schritt, mit Code und Schaltplänen.

Mehr Artikel von Stefan →

Schreibe einen Kommentar Antwort abbrechen

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert

Kategorien

Kontakt & Hilfe

Fragen oder Feedback?

Du hast eine Idee, brauchst Hilfe oder möchtest Feedback loswerden? Erstelle einfach ein Support-Ticket.

Support-Ticket erstellen
Keine Beiträge verpassen

Newsletter abonnieren

Erhalte regelmäßig Updates zu neuen Projekten, Tutorials und Tipps rund um Arduino, ESP32, Smarthome und IoT direkt in dein Postfach.

Newsletter abonnieren
Videos & Tutorials

Draeger-IT auf YouTube

Entdecke meine Videos zu Arduino, ESP32, Raspberry Pi, Smarthome und spannenden Elektronikprojekten – mit Tutorials, Tests und Praxisbeispielen.

YouTube-Kanal besuchen
Freiwillige Unterstützung

Unterstütze meinen Blog

Wenn dir meine Inhalte gefallen, freue ich mich über deine Unterstützung auf Tipeee. So hilfst du dabei, den Blog am Leben zu halten und neue Beiträge zu ermöglichen.

Auf Tipeee unterstützen

Vielen Dank für deinen Support!
– Stefan Draeger

Kostenlose Online-Werkzeuge

Tools

Praktische Generatoren, Rechner und Hilfsmittel für deine Elektronik- und Entwicklungsprojekte.

Generatoren & Konverter

  • QR-Code Generator
  • Passwort Generator
  • PNG zu WebP konvertieren
  • Bilder online komprimieren
  • Code online formatieren

Rechner & Tabellen

  • Unix-Zeitstempel-Rechner
  • ASCII-Tabelle
  • ASCII Code umrechnen – Text, Dezimal, Hex & Binär
  • Spannung, Strom, Widerstand und Leistung berechnen
  • Widerstandsrechner

LED-Matrix

  • 8×8 LED Matrix Tool
  • 8×16 LED Matrix Modul von Keyestudio
  • 16×16 LED Matrix Generator
Kontakt

Anschrift & Kontaktdaten

Stefan Draeger

Königsberger Str. 13
38364 Schöningen

Telefon 01556 5432686

E-Mail info@draeger-it.blog

Blogverzeichnis Bloggerei.de TopBlogs.de das Original - Blogverzeichnis | Blog Top Liste Blogverzeichnis trusted-blogs.com
Social Media

Folge mir auf

Weitere Projekte, Videos und Neuigkeiten findest du auch auf meinen Social-Media-Kanälen.

  • f Facebook
  • in LinkedIn
  • ▶ YouTube
  • ♪ TikTok
  • P Pinterest
  • ◎ Instagram
Rechtliche Informationen

Rechtliches

  • Impressum
  • Datenschutzerklärung
  • Disclaimer
  • Cookie-Richtlinie (EU)
  • KI-Transparenz bei draeger-it.blog
©2026 Technik Blog | Built using WordPress and Responsive Blogily theme by Superb
Cookie-Zustimmung verwalten
Wir verwenden Technologien wie Cookies, um Geräteinformationen zu speichern und/oder darauf zuzugreifen. Wir tun dies, um das Surferlebnis zu verbessern und um personalisierte Werbung anzuzeigen. Wenn Sie diesen Technologien zustimmen, können wir Daten wie das Surfverhalten oder eindeutige IDs auf dieser Website verarbeiten. Wenn Sie Ihre Zustimmung nicht erteilen oder zurückziehen, können bestimmte Funktionen beeinträchtigt werden.
Funktional Immer aktiv
Die technische Speicherung oder der Zugang ist unbedingt erforderlich für den rechtmäßigen Zweck, die Nutzung eines bestimmten Dienstes zu ermöglichen, der vom Teilnehmer oder Nutzer ausdrücklich gewünscht wird, oder für den alleinigen Zweck, die Übertragung einer Nachricht über ein elektronisches Kommunikationsnetz durchzuführen.
Vorlieben
Die technische Speicherung oder der Zugriff ist für den rechtmäßigen Zweck der Speicherung von Präferenzen erforderlich, die nicht vom Abonnenten oder Benutzer angefordert wurden.
Statistiken
Die technische Speicherung oder der Zugriff, der ausschließlich zu statistischen Zwecken erfolgt. Die technische Speicherung oder der Zugriff, der ausschließlich zu anonymen statistischen Zwecken verwendet wird. Ohne eine Vorladung, die freiwillige Zustimmung deines Internetdienstanbieters oder zusätzliche Aufzeichnungen von Dritten können die zu diesem Zweck gespeicherten oder abgerufenen Informationen allein in der Regel nicht dazu verwendet werden, dich zu identifizieren.
Marketing
Die technische Speicherung oder der Zugriff ist erforderlich, um Nutzerprofile zu erstellen, um Werbung zu versenden oder um den Nutzer auf einer Website oder über mehrere Websites hinweg zu ähnlichen Marketingzwecken zu verfolgen.
  • Optionen verwalten
  • Dienste verwalten
  • Verwalten von {vendor_count}-Lieferanten
  • Lese mehr über diese Zwecke
Einstellungen anzeigen
  • {title}
  • {title}
  • {title}