[NODE-RED] Steuerung des Empfangs des Zigbee-Sensorsignals

Ich teile Ihnen einen Node-Red-Flow mit, um die Empfangszeit des Signals eines Zigbee-Sensors zu messen.
Wenn die Zeit zwischen jedem Signalempfang länger als 24 Stunden ist (einstellbar), wird eine Telegramm-Benachrichtigung gesendet.

Ich habe diese Funktion erstellt, weil die Batterieanzeige der Zigbee-Sensoren nicht zuverlässig ist und ich mehrmals auf eine Temperatur hereingefallen bin, obwohl der Sensor nicht mehr funktionierte. Was ärgerlich sein kann, wenn man diesen Wert in einer Szene benötigt. Jetzt weiß ich, wenn einer meiner Sensoren nach 24 Stunden keine Daten mehr sendet. Und das in einer Schleife alle 24 Stunden. Getestet mit Türöffnungssensor, Temperatur-/Feuchtigkeitssensor, Schalter, Vibrationssensor, Bewegungssensor.

Die Funktionsanfrage in Gladys

Für die Installation von NODE-RED kennen Sie bereits alle das Video-Tutorial von @pierre-gilles. https://www.youtube.com/watch?v=bpmHzR8_S5g.

Voraussetzungen:

  • Konfigurieren Sie den MQTT-Broker, um sich mit dem Zigbee2mqtt-Broker zu verbinden


    Das Passwort wird von Gladys generiert, Sie können es in der Datenbank finden.

  • Installieren Sie in Node-Red: « node-red–contrib-telegrambot »

  • Konfigurieren Sie einen Telegram-Bot für Node-Red

  • Holen Sie sich eine ChatId-Nummer, um Nachrichten mit Telegram in Node-Red senden zu können (ich werde später hinzufügen, wie man diese Nummer abrufen kann, falls erforderlich)
    Dieser Teil mit Telegram ist optional, Sie können auch einen Zustand in einem Fake-MQTT-Gerät an Gladys senden. Und dann die Telegramm-Benachrichtigung mit Gladys durchführen.

  • Konfiguration von Node-Red zum Schreiben/Lesen von Dateien:
    Bearbeiten Sie die Datei /var/lib/node-red/settings.js (siehe das Video von @pierre-gilles zur Installation von Node-Red, er erklärt, wie man diese Datei bearbeitet)
    Fügen Sie die folgenden Zeilen hinzu:

contextStorage: {
   default: "memoryOnly",
   memoryOnly: { module: 'memory' },
   file: { module: 'localfilesystem' }
},

Hier ist ein Überblick über den Flow in Node-red für einen meiner Sensoren:

Hier ist der Flow zum Importieren in Node-Red: Ich habe einen meiner Flows so geändert, dass er generisch ist und meine Verbindungsdaten gelöscht wurden
image

[
    {
        "id": "d5482843ef207659",
        "type": "function",
        "z": "d76f072c1559266b",
        "g": "87c2f560ca6c5a98",
        "name": "Nachricht an Telegram senden",
        "func": "msg.payload={};\nmsg.payload.chatId =\"******\";\nmsg.payload.content = \"[ALARM] Verbindung zum Thermometer verloren! Bitte die Batterie dieses Geräts überprüfen!\";\nmsg.payload.type = \"message\";\nreturn msg;",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 1180,
        "y": 120,
        "wires": [
            [
                "f57a85488266a794"
            ]
        ]
    },
    {
        "id": "2c780ff8e1b28a9e",
        "type": "mqtt in",
        "z": "d76f072c1559266b",
        "g": "87c2f560ca6c5a98",
        "name": "Thermometer",
        "topic": "zigbee2mqtt/Thermometer",
        "qos": "2",
        "datatype": "auto",
        "broker": "2a159ac23236c7e2",
        "nl": false,
        "rap": true,
        "rh": 0,
        "inputs": 0,
        "x": 150,
        "y": 120,
        "wires": [
            [
                "47ee6c1dd9132535"
            ]
        ]
    },
    {
        "id": "47ee6c1dd9132535",
        "type": "change",
        "z": "d76f072c1559266b",
        "g": "87c2f560ca6c5a98",
        "name": "",
        "rules": [
            {
                "t": "set",
                "p": "topic",
                "pt": "msg",
                "to": "sensor",
                "tot": "str"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 490,
        "y": 120,
        "wires": [
            [
                "147c4e952052bad4"
            ]
        ]
    },
    {
        "id": "cfd94c8353c2c6f9",
        "type": "inject",
        "z": "d76f072c1559266b",
        "g": "87c2f560ca6c5a98",
        "name": "Task 1000",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "60",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "task",
        "payload": "",
        "payloadType": "date",
        "x": 490,
        "y": 80,
        "wires": [
            [
                "147c4e952052bad4"
            ]
        ]
    },
    {
        "id": "147c4e952052bad4",
        "type": "function",
        "z": "d76f072c1559266b",
        "g": "87c2f560ca6c5a98",
        "name": "TimeOut",
        "func": "/*\n * Time-out Sensor\n */\n\n\nconst TIME_OUT = 1440; /* 24 Stunden */\n\n/*\n * Time-out Ausgabe\n */\nvar msg_TimeOut = null;\n\n/*\n * Verzögerung vor dem Time-out des Sensors zwischen zwei Empfängen\n */\nvar delayTimeOut = context.get(\"delayTimeOut\", \"file\") || TIME_OUT;  // Variablen initialisieren\n\n\nif (msg.topic === \"sensor\")  // Initialisierung des Zählers bei jedem Sensorempfang\n{\n    delayTimeOut = TIME_OUT;\n    context.set(\"delayTimeOut\", delayTimeOut, \"file\");  // Speichern des letzten Werts im lokalen Kontext\n\n    node.status({ fill: \"green\", shape: \"ring\", text: \"Verbleibend: \" + delayTimeOut + \" Minuten\" });\n\n    //msg_TimeOut = { topic: \"temp\", payload: delayTimeOut };\n}\n\nif (msg.topic === \"task\")   // Aktualisierung alle Minuten\n{\n    if (delayTimeOut > 0)\n    {\n        delayTimeOut--;\n        context.set(\"delayTimeOut\", delayTimeOut, \"file\");  // Speichern des letzten Werts im lokalen Kontext\n        \n        node.status({ fill: \"green\", shape: \"ring\", text: \"Verbleibend: \" + delayTimeOut + \" Minuten\" });\n\n    }\n\n    if (delayTimeOut === 0)\n    {\n        delayTimeOut = 0;    \n        context.set(\"delayTimeOut\", delayTimeOut, \"file\");  // Speichern des letzten Werts im lokalen Kontext\n\n        node.status({ fill: \"red\", shape: \"ring\", text: \"TIME OUT\" });\n\n        msg_TimeOut = { topic: \"timeout\", payload: \"TIME OUT\" };\n    }\n    \n} \n\nreturn msg_TimeOut;",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 900,
        "y": 120,
        "wires": [
            [
                "d5482843ef207659"
            ]
        ],
        "outputLabels": [
            "TimeOut"
        ]
    },
    {
        "id": "f57a85488266a794",
        "type": "telegram sender",
        "z": "d76f072c1559266b",
        "g": "87c2f560ca6c5a98",
        "name": "",
        "bot": "e1f028778559de40",
        "haserroroutput": false,
        "outputs": 1,
        "x": 1510,
        "y": 120,
        "wires": [
            []
        ]
    },
    {
        "id": "2a159ac23236c7e2",
        "type": "mqtt-broker",
        "name": "zigbee2mqtt2",
        "broker": "192.168.*.*",
        "port": "1884",
        "clientid": "",
        "autoConnect": true,
        "usetls": false,
        "protocolVersion": "4",
        "keepalive": "60",
        "cleansession": true,
        "birthTopic": "",
        "birthQos": "0",
        "birthPayload": "",
        "birthMsg": {},
        "closeTopic": "",
        "closeQos": "0",
        "closePayload": "",
        "closeMsg": {},
        "willTopic": "",
        "willQos": "0",
        "willPayload": "",
        "willMsg": {},
        "userProps": "",
        "sessionExpiry": "",
        "credentials": {}
    },
    {
        "id": "e1f028778559de40",
        "type": "telegram bot",
        "botname": "******",
        "usernames": "",
        "chatids": "",
        "baseapiurl": "",
        "updatemode": "polling",
        "pollinterval": "300",
        "usesocks": false,
        "sockshost": "",
        "socksprotocol": "socks5",
        "socksport": "6667",
        "socksusername": "anonymous",
        "sockspassword": "",
        "bothost": "",
        "botpath": "",
        "localbotport": "8443",
        "publicbotport": "8443",
        "privatekey": "",
        "certificate": "",
        "useselfsignedcertificate": false,
        "sslterminated": false,
        "verboselogging": false
    }
]

1. mqtt in : Konfiguration Zigbee-Sensor
image

Ein mqtt in-Objekt einfügen

Geben Sie in Server Ihren Zigbee2mqtt-Broker an
Geben Sie im Topic das Topic Ihres Sensors an (Sie können eine Anwendung wie MQTT Explorer verwenden, um Ihr Netzwerk zu erkunden und das Topic zu finden). Normalerweise lautet das Topic zigbee2mqtt/nameihresensors

2. change: Transformation des Topics
image

Ein change-Objekt einfügen, um das Topic zu ändern und so zu vermeiden, das Topic für jeden Sensor anschließend in der folgenden Funktion zu ändern.

3. function : Berechnung der Zeit zwischen zwei Zigbee-Signalen des Sensors
image

Ein function-Objekt einfügen

Im folgenden Code können Sie die Konstante TIME_OUT (in Minuten) ändern. Ich habe 1440 Minuten (24 Stunden) eingestellt

/*
 * Sensor Time-out
 */


const TIME_OUT = 1440; /* 24 Stunden */

/*
 * Time-out-Ausgabe
 */
var msg_TimeOut = null;

/*
 * Verzögerung vor dem Sensor Time-out zwischen zwei Empfängen
 */
var delayTimeOut = context.get("delayTimeOut", "file") || TIME_OUT;  // Variablen initialisieren


if (msg.topic === "sensor")  // Zählerinitialisierung bei jedem Sensorempfang
{
    delayTimeOut = TIME_OUT;
    context.set("delayTimeOut", delayTimeOut, "file");  // letzten Wert im lokalen Kontext speichern

    node.status({ fill: "green", shape: "ring", text: "Verbleibend: " + delayTimeOut + " Minuten" });

    //msg_TimeOut = { topic: "temp", payload: delayTimeOut };
}

if (msg.topic === "task")   // alle Minuten aktualisieren
{
    if (delayTimeOut > 0)
    {
        delayTimeOut--;
        context.set("delayTimeOut", delayTimeOut, "file");  // letzten Wert im lokalen Kontext speichern
        
        node.status({ fill: "green", shape: "ring", text: "Verbleibend: " + delayTimeOut + " Minuten" });

    }

    if (delayTimeOut === 0)
    {
        delayTimeOut = 0;    
        context.set("delayTimeOut", delayTimeOut, "file");  // letzten Wert im lokalen Kontext speichern

        node.status({ fill: "red", shape: "ring", text: "TIME OUT" });

        msg_TimeOut = { topic: "timeout", payload: "TIME OUT" };
    }
    
} 

return msg_TimeOut;

Ich habe die verbleibende Zeit in die Funktion eingefügt, bevor die Warnung gesendet wird

4. inject: Senden eines Payloads alle Minuten, um die Zeit in der Funktion neu zu berechnen
image

Ein inject-Objekt einfügen

Geben Sie im Topic task an. Dieses Topic wird in der Funktion verwendet. Wenn Sie es ändern, müssen Sie den Code der Funktion anpassen.
Geben Sie dann ein Intervall von jeder Minute an

5. function: Erstellung des Textes für Telegram
image

Ein function-Objekt einfügen

Sie müssen Ihre Chat-ID einfügen und können Ihre Nachricht anpassen

msg.payload={};
msg.payload.chatId ="******";
msg.payload.content = "[ALERT] Kommunikation mit dem Thermometer verloren! Bitte die Batterie dieses Geräts überprüfen!";
msg.payload.type = "message";
return msg;

6. sender telegram: Senden der Nachricht
image

Ein Telegram-Sender-Objekt hinzufügen

Geben Sie Ihren Telegram-Bot für NodeRed an

Super das!!! Ich werde das schnell bei mir umsetzen :wink: Vielen Dank!

Nur zur Info, als ich diesen Teil hinzugefügt habe, habe ich es ganz am Ende der Datei gemacht…

Und dann hat Node-red angefangen, in einer Endlosschleife neu zu starten.
Letztlich habe ich den bereits vorhandenen Teil in der Datei bearbeitet, indem ich ihn entkommentiert habe. Also sieht es bei mir so aus, mit einem Komma am Ende:

contextStorage: {
   default: "memoryOnly",
   memoryOnly: { module: 'memory' },
   file: { module: 'localfilesystem' }
},

Okay, ich werde es überprüfen, aber es könnte sein, dass ich beim Kopieren des Codes ein Komma vergessen habe.
Ich werde es korrigieren, entschuldige…

Lass mich wissen, wenn du es eingerichtet hast… und zum Testen kannst du die Konstante TIME_OUT ändern und nur ein paar Minuten einstellen und einen deiner Sensoren von der Batterie trennen.

Du hast recht, ich habe das Komma beim Abschreiben tatsächlich vergessen!
image

Ich habe es in meinem Tutorial korrigiert.

Und es ist ein Erfolg!

Ich habe den ersten Block durch einen ersetzt, den ich gewohnt bin zu verwenden:

Vielen Dank @Will_71 :+1: :wink:

Ich sehe die Zählung unter der TimeOut-Funktion nicht!
Hast du versucht, deinen Sensor zu trennen, indem du das TIME_OUT verringerst?

Oui oui, das war so, weil ich es für die Tests auf 1 Minute eingestellt hatte :wink:
Jetzt ist es da:

Aber eine kleine Frage: Ich habe gerade das hier gemacht:

Und danach dachte ich, dass ich vielleicht alle Telegram-Sender in einem einzigen zusammenfassen könnte, oder?

Ok perfekt!

Ja, ich habe es auch mit den Links gruppiert

Ah cool, ich habe nie verstanden, wofür die LINKs gut sind, danke!

Aber ändert das in diesem Fall etwas, ob man einen Link oder eine Telegram-Box verwendet, ich weiß nicht.

Normalerweise verwende ich einen Link, wenn ich das Ergebnis einer anderen Funktion in einem anderen Flow wiederverwenden möchte.

Zum Beispiel habe ich einen Flow mit einer HTTP-Anfrage, um die Außentemperatur abzurufen (ich sollte in einen Außensensor investieren) und verwende dann diese Temperatur in meinen Heizleistungskalkulationen für meine Heizkörper. In diesem Fall mache ich einen Link, um die HTTP-Anfrage nicht für alle meine Heizkörper durchzuführen. Es ist nicht sinnvoll, mehrere HTTP-Anfragen in diesem Fall durchzuführen, der Link ist nützlich.

Ich komme hierher zurück, weil meine ersten Tests zwar vielversprechend schienen… aber dem ist nicht so.

Ich habe einen Timeout von 12 Stunden eingestellt und zweimal täglich erhalte ich Telegram-Nachrichten, dass meine Geräte offline sind… :thinking:

Ich verstehe nicht wirklich, woher das Problem kommt.
Übrigens, da ich sehe, dass du @Will_71 in NodeRed sehr gut bist, wäre es nicht einfacher, den Status der Verfügbarkeit Online und Offline von z2m zu überprüfen?

Oder im schlimmsten Fall eine Berechnung mit dem Datenpunkt last_seen durchzuführen?

Und sind die Geräte, auf denen du die Telegram-Benachrichtigung erhältst, offline oder nicht?
Denn auf deinen Screenshots sind zwei Sensoren offline mit „Zuletzt gesehen“-Zeiten von mehr als 12 Stunden. Daher ist das Verhalten normal.

Nein nein, ich habe einfach mal einen Screenshot gemacht, ich habe ein paar Geräte offline.
Aber die Sensoren, die ich überprüfen möchte, sind alle online und haben last_seen von maximal 1 Stunde.

Bei mir funktioniert alles gut, aber du hast nicht alle dieselben Elemente wie ich verwendet
Weiß ich nicht, ob das zigbee2mqtt-Objekt die Infos wie das MQTT-Objekt, das ich verwende, hochlädt?
In meinem Fall verbinde ich mich direkt mit dem MQTT-Broker und lese bei jeder Sendung des Sensors den Wert. Vielleicht sendet zigbee2mqtt in deinem Fall die Daten nicht, wenn sich der Wert nicht ändert. Ich denke, man sollte dieser Spur nachgehen.

Ah okay… dann werde ich mal deinen MQTT-Block ausprobieren und sehen, was dabei herauskommt, danke für den Tipp!!

Das ist nur ein Vorschlag, da ich deinen Block noch nie verwendet habe.
Ich werde versuchen, einen Test mit diesem zigbee2mqtt-Block durchzuführen, um zu sehen, welches Ergebnis ich erhalte.

Super Idee! Es stimmt, ein defekter Zigbee-Sensor wird nicht als Fehler gemeldet, was schon problematisch ist!
Ich habe gerade den Flow von @Will_71 ausprobiert und festgestellt, dass ich das gleiche Problem wie @guim31 habe.

Ich habe msg.topic durch msg.payload im ‹ change ›-Block und im Skript der Funktion ‹ Timeout › (Zeilen 19 und 29) ersetzt.

Seit dieser Änderung scheint es bei mir richtig zu funktionieren.

Ich bin nicht deiner Meinung.

Warum habe ich das Topic und nicht den Payload gewählt? Weil ich den Payload nicht brauche, da ich nicht die Sensordaten verwenden möchte.

Daher habe ich einen Change-Block verwendet, um das Topic des Sensors zu standardisieren, damit ich es nicht im Skript meiner Timeout-Funktion ändern muss. Dieser Schritt ist vielleicht optional, aber in diesem Fall müsste das Topic in der Funktion angepasst werden.

Dein Sensor sendet immer auf ein dediziertes Topic einen Payload mit seinen Daten. (siehe Bild unten)

Dann hast du die Zeilen 19 und 29 meiner Funktion geändert. Okay für Zeile 19, wenn du den Payload verwendest, aber warum Zeile 29?
Zeile 29 ist ein Test, um das Topic des Inject-Blocks zu erhalten, das ich jede Minute sende.

Warum es bei dir nicht funktioniert, weiß ich nicht, vielleicht hast du es falsch kopiert oder nicht die gleichen Blöcke wie ich verwendet. Ich weiß es nicht!!