Meine bisheringen Rauchmelder arbeiten alle Standalone, die sind zwar recht laut wenn ein Alarm gemeldet wird, aber nicht vernetzbar, so dass immer nur der Melder in dem Raum einen Alarm ausgibt wo Rauch erkannt wird.
Da ich ein IoT Smarthome System einsetze lag es nahe vernetzbare Rauchmelder einzusetzen. Nach einer ausfürlichen Suche bin ich auf die “Heimann HS1SA-E Rauchmelder mit ZigBee2MQTT” aufmerksam geworden und habe mir einige davon beschafft. Es gibt da verschiedene Typen, die einen haben ZigBee, andere nicht wichtig: HS1SA-E der kann ZigBee. Nach meiner Recherche ist wohl gerade ein Nachfolgemodell in Entwicklung, das auch einen Testalarm per ZigBee unterstützt, was das Modell HS1SA-E nicht kann!
Meine Intergartion basiert auf Node-Red mit ZigBee2MQTT (die Voraussetzungen siehe weiter unten), ich habe mir dazu Schritt für Schritt einen Flow erstellt, der den Zustand der Rauchmelder erfasst und Berichte per Mail, ntfy und Pushover verschickt. Falls es einen Alarm gibt habe ich eine ZigBee Kerzenbirne in zentraler Lage eingebunden, die dann blinkt. Mit einer aktustische Meldung muss ich mir noch überlegen wie das inntegriert werden könnte.
Die Rauchmelder sind etwas zickig bei der Integration in ZB2M, wenn man ein Interview startet muss man am Besten alle zwei drei Sekunden den Reset Knopf kurz drücken, damit sie nicht sofort wieder in den Schlafmodus gehen. Musste das mehrmals machen, damit die Daten sauber ausgelesen werden konnten, seit dem ist es ok.
Das Ganze ist natürlich noch V 0.8, also da gibt es noch Potential für Verbesserungen, wer will kann sich ja mal mit dem Status Quo versuchen und mir Rückmeldung geben für Verbesserungen, Korrekturen oder was auch immer. Schon mal viel Sapß damit.
Gruß
Edi
Eine Beschreibung des Heimann Rauchmelders:
HEIMAN Zigbee Rauchmelder
Der HEIMAN Zigbee Rauchmelder besitzt eine genaue Raucherkennung und Sie werden akustisch und visuell alarmiert. In einer ernsten Situation alarmiert der Rauchsensor mit einer roten LED, die schnell und mit einem lauten Alarm blinkt. Darüber hinaus besteht die Möglichkeit, eine Warnung auf Ihrem Smartphone zu erhalten, wenn Sie nicht zu Hause sind. So wird verhindert, dass ein Brand durch Rauch entsteht und teure Folgeschäden verursacht werden.
Durch den Einsatz der Funktechnologie Zigbee 3.0 kann der Sensor an jedes Zigbee-Gateway angeschlossen werden. Dank des neuesten Zigbee 3.0 Standards gewährleistet der Rauchsensor eine Batterielebensdauer von 3 Jahren.
Eigenschaften:
Erkennt Rauch und sichtbare Partikel, welche durch einen Schwelbrand entstehen
Benachrichtigung über Feueralarm, auch unterwegs
Lautstärke des Alarms: 85 dB/3m
Anzeige des Batteriestandes
Batterie notwendig: Ja (1x Batterie CR123A)
Lebensdauer der Batterie: 3 Jahre
Funktechnologie: Zigbee 3.0
Rauchmelder entspricht DIN EN14604 Norm
Voraussetzung für den gesamten Ablauf:
- Raspberrypi
- MQTT Broker
- ZigBee2mQTT mit Sonoff Dongle oder Ähnlichem
- Heimann Rauchmelder
- ZigBee Kerzenbirne oder was sonst zum Alarmieren verwendet wird
- Heimann Rauchmelder
- Node-Red mit Dashboard 2.0 und diversen anderen Paletten
- ntfy Account
- Pushover Account
- Mailaccount
- für die Zustellung der Meldungen werden ntfy, Pushover und Mail verwendet
Der komplette Node-Red Flow für die Rauchmelder Intergration:
[
{
"id": "f19c1232b5818c2a",
"type": "group",
"z": "a906f7d4665bf705",
"style": {
"stroke": "#999999",
"stroke-opacity": "1",
"fill": "none",
"fill-opacity": "1",
"label": true,
"label-position": "nw",
"color": "#a4a4a4"
},
"nodes": [
"a6ea58dab388b29c",
"765bf0769705a2ff",
"g7h8i9j0k1l2m3n4",
"i9j0k1l2m3n4o5p6",
"fac5633b6eba8601",
"7a1904f924795633",
"1fc2a6b8f60b088d",
"f8b1b27956f09b3e",
"147944ef03bfde4c",
"4ab2f6e25855efce",
"9d6eebcf795ee86e",
"ac1607f8f0db6b5d",
"24495c060d2a8756",
"2bb5b7ad91154a04",
"06fda76032ceaefb",
"296a847a7c85c981",
"3deb8b862d1238e4",
"acbb6ccfa4615f05",
"2cf42ba58b153108",
"0f916dab840c7276",
"fd8f8ffdf77a106c",
"0469b6708a6631f2",
"e45e871a10a710b2",
"95efe3c5faf4ab5b",
"6f243fcb4da873d4",
"b69dfaa826fb2d91",
"685b6cb6ac3300e8",
"efd4d502334fbc36",
"6247c2a58286fc73",
"ab3764d33b337c6f",
"0f3d4c079b703181",
"cb8dcc5798bb85a4",
"551b4d13bc9b505f",
"191ef45fc6c07a97",
"d4e9451a20607f2f"
],
"x": 34,
"y": 19,
"w": 1312,
"h": 522
},
{
"id": "a6ea58dab388b29c",
"type": "zigbee2mqtt-get",
"z": "a906f7d4665bf705",
"g": "f19c1232b5818c2a",
"name": "",
"server": "1bfcccb11dd30d19",
"friendly_name": "KellerWerkstatt (HS1SA-E)",
"device_id": "0xf84477fffe123ed0",
"state": "0",
"enableMultiple": false,
"x": 400,
"y": 100,
"wires": [
[
"06fda76032ceaefb"
]
]
},
{
"id": "765bf0769705a2ff",
"type": "inject",
"z": "a906f7d4665bf705",
"g": "f19c1232b5818c2a",
"name": "8:00 Tag",
"props": [
{
"p": "topic",
"vt": "str"
}
],
"repeat": "",
"crontab": "00 08 * * *",
"once": false,
"onceDelay": 0.1,
"topic": "",
"x": 140,
"y": 140,
"wires": [
[
"a6ea58dab388b29c"
]
]
},
{
"id": "g7h8i9j0k1l2m3n4",
"type": "function",
"z": "a906f7d4665bf705",
"g": "f19c1232b5818c2a",
"name": "E-Mail Formatierung",
"func": "let data = msg.payload;\n\n// E-Mail Inhalt erstellen\nlet emailContent = `\n<h2>🏠 🚨 Rauchmelder Statusbericht</h2>\n<p><strong>Gerät:</strong> ${data.friendly_name}</p>\n<p><strong>Datum/Uhrzeit:</strong> ${new Date(data.timestamp).toLocaleString('de-DE')}</p>\n<hr>\n<h3>Status:</h3>\n<table border=\"1\" cellpadding=\"5\" cellspacing=\"0\" style=\"border-collapse: collapse;\">\n <tr>\n <td><strong>Batteriestatus:</strong></td>\n <td>${data.battery}%</td>\n </tr>\n <tr>\n <td><strong>Batterie OK:</strong></td>\n <td>${data.battery_ok ? '✅ Ja' : '❌ Nein - Bitte wechseln!'}</td>\n </tr>\n <tr>\n <td><strong>Test-Modus:</strong></td>\n <td>${data.test_mode ? '🔴 Aktiv' : '⚪ Inaktiv'}</td>\n </tr>\n <tr>\n <td><strong>Rauchmelder:</strong></td>\n <td>${data.smoke ? '🚨 ALARM' : '✅ Normal'}</td>\n </tr>\n <tr>\n <td><strong>Signalstärke:</strong></td>\n <td>${data.linkquality} LQI</td>\n </tr>\n</table>\n\n<p><em>Automatisierte Statusmeldung ZigBee Rauchmelder ${data.friendly_name}.</em></p>\n`;\n\n// Warnung bei niedrigem Batteriestand\nif (!data.battery_ok) {\n emailContent = emailContent.replace('<h2>', '<h2 style=\"color: red;\">⚠️ WARNUNG - ');\n emailContent += '<p style=\"color: red; font-weight: bold;\">⚠️ Der Batteriestand ist niedrig! Bitte tauschen Sie die Batterie aus.</p>';\n}\n\nmsg.payload = emailContent;\nmsg.topic = `Rauchmelder ${data.friendly_name} Statusbericht`;\n\nreturn msg;",
"outputs": 1,
"timeout": "",
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 820,
"y": 240,
"wires": [
[
"fac5633b6eba8601"
]
]
},
{
"id": "i9j0k1l2m3n4o5p6",
"type": "inject",
"z": "a906f7d4665bf705",
"g": "f19c1232b5818c2a",
"name": "Manueller Test",
"props": [
{
"p": "payload"
},
{
"p": "topic",
"vt": "str"
}
],
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "",
"payload": "",
"payloadType": "date",
"x": 160,
"y": 100,
"wires": [
[
"a6ea58dab388b29c"
]
]
},
{
"id": "fac5633b6eba8601",
"type": "e-mail",
"z": "a906f7d4665bf705",
"g": "f19c1232b5818c2a",
"server": "smtp.mail.de",
"port": "465",
"secure": true,
"tls": true,
"name": "rauchmelder@domain.com",
"dname": "Rauchmelder Mail Bericht",
"output": false,
"outputs": 0,
"x": 890,
"y": 300,
"wires": []
},
{
"id": "7a1904f924795633",
"type": "debug",
"z": "a906f7d4665bf705",
"g": "f19c1232b5818c2a",
"name": "Daten aufbereitet",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "payload",
"targetType": "msg",
"statusVal": "",
"statusType": "auto",
"x": 850,
"y": 60,
"wires": []
},
{
"id": "1fc2a6b8f60b088d",
"type": "ui-gauge",
"z": "a906f7d4665bf705",
"g": "f19c1232b5818c2a",
"name": "Rauchmelder Batterie",
"group": "5673b51cc6b259c0",
"order": 1,
"value": "payload.battery",
"valueType": "msg",
"width": 3,
"height": 3,
"gtype": "gauge-half",
"gstyle": "needle",
"title": "Batterie",
"alwaysShowTitle": false,
"units": "%",
"icon": "",
"prefix": "",
"suffix": "",
"segments": [
{
"from": "0",
"color": "#ff2600",
"text": "",
"textType": "label"
},
{
"from": "40",
"color": "#ffc800",
"text": "",
"textType": "label"
},
{
"from": "100",
"color": "#4e8f00",
"text": "",
"textType": "label"
}
],
"min": 0,
"max": 100,
"sizeThickness": 16,
"sizeGap": 4,
"sizeKeyThickness": 8,
"styleRounded": true,
"styleGlow": false,
"className": "",
"x": 1220,
"y": 60,
"wires": [
[]
]
},
{
"id": "f8b1b27956f09b3e",
"type": "ui-text",
"z": "a906f7d4665bf705",
"g": "f19c1232b5818c2a",
"group": "5673b51cc6b259c0",
"order": 7,
"width": "3",
"height": 1,
"name": "Batterie Anzeige",
"label": "Batteriestatus",
"format": "{{msg.payload.battery}}%",
"layout": "row-spread",
"style": false,
"font": "",
"fontSize": "",
"color": "#000000",
"wrapText": false,
"className": "",
"value": "payload.battery",
"valueType": "msg",
"x": 1200,
"y": 100,
"wires": []
},
{
"id": "147944ef03bfde4c",
"type": "ui-text",
"z": "a906f7d4665bf705",
"g": "f19c1232b5818c2a",
"group": "5673b51cc6b259c0",
"order": 6,
"width": "3",
"height": 1,
"name": "Batterie OK Status",
"label": "Batterie OK",
"format": "{{msg.payload.battery_ok ? '✅ Ja' : '❌ Nein'}}",
"layout": "row-spread",
"style": false,
"font": "",
"fontSize": "",
"color": "#000000",
"wrapText": false,
"className": "",
"value": "payload.battery_ok",
"valueType": "msg",
"x": 1210,
"y": 140,
"wires": []
},
{
"id": "4ab2f6e25855efce",
"type": "ui-text",
"z": "a906f7d4665bf705",
"g": "f19c1232b5818c2a",
"group": "5673b51cc6b259c0",
"order": 5,
"width": "3",
"height": 1,
"name": "Test Modus",
"label": "Test-Modus",
"format": "{{msg.payload.test_mode ? '🔴 Aktiv' : '⚪ Inaktiv'}}",
"layout": "row-spread",
"style": false,
"font": "",
"fontSize": "",
"color": "#000000",
"wrapText": false,
"className": "",
"value": "payload.test_mode",
"valueType": "msg",
"x": 1190,
"y": 180,
"wires": []
},
{
"id": "9d6eebcf795ee86e",
"type": "ui-text",
"z": "a906f7d4665bf705",
"g": "f19c1232b5818c2a",
"group": "5673b51cc6b259c0",
"order": 4,
"width": "3",
"height": 1,
"name": "Rauch Status",
"label": "Rauchmelder",
"format": "{{msg.payload.smoke ? '🚨 ALARM' : '✅ Normal'}}",
"layout": "row-spread",
"style": false,
"font": "",
"fontSize": "",
"color": "#000000",
"wrapText": false,
"className": "",
"value": "payload.smoke",
"valueType": "msg",
"x": 1200,
"y": 220,
"wires": []
},
{
"id": "ac1607f8f0db6b5d",
"type": "ui-text",
"z": "a906f7d4665bf705",
"g": "f19c1232b5818c2a",
"group": "5673b51cc6b259c0",
"order": 3,
"width": "3",
"height": 1,
"name": "Signalstärke",
"label": "Signalstärke",
"format": "{{msg.payload.linkquality}} LQI",
"layout": "row-spread",
"style": false,
"font": "",
"fontSize": "",
"color": "#000000",
"wrapText": false,
"className": "",
"value": "payload.linkquality",
"valueType": "msg",
"x": 1190,
"y": 260,
"wires": []
},
{
"id": "24495c060d2a8756",
"type": "ui-text",
"z": "a906f7d4665bf705",
"g": "f19c1232b5818c2a",
"group": "5673b51cc6b259c0",
"order": 2,
"width": "3",
"height": 1,
"name": "Letztes Update",
"label": "Letztes Update",
"format": "{{msg.payload.timestamp | date:'DD.MM.YYYY HH:mm:ss'}}",
"layout": "row-spread",
"style": false,
"font": "",
"fontSize": "",
"color": "#000000",
"wrapText": false,
"className": "",
"value": "payload.timestamp_formatted",
"valueType": "msg",
"x": 1200,
"y": 300,
"wires": []
},
{
"id": "2bb5b7ad91154a04",
"type": "comment",
"z": "a906f7d4665bf705",
"g": "f19c1232b5818c2a",
"name": "Rauchmelder Keller Werkstatt - ntfy, Pushover und Mail Meldungen und Statusbericht",
"info": "",
"x": 360,
"y": 60,
"wires": []
},
{
"id": "06fda76032ceaefb",
"type": "function",
"z": "a906f7d4665bf705",
"g": "f19c1232b5818c2a",
"name": "Rauchmelder Daten aufbereiten",
"func": "// ZigBee2MQTT Daten parsen\nlet data = msg.payload;\nlet deviceInfo = {};\n\n// Prüfen ob Daten vorhanden sind\nif (typeof data === 'object') {\n // Batteriestatus (in % oder Spannung)\n deviceInfo.battery = data.battery || data.battery_percentage || 0;\n deviceInfo.battery_low = data.battery_low || false;\n \n // Test-Modus (falls vorhanden)\n deviceInfo.test_mode = data.test_mode || false;\n \n // Rauchmelder Status (Alarm, Normal, etc.)\n deviceInfo.smoke = data.smoke || false;\n deviceInfo.smoke_detected = data.smoke_detected || false;\n \n // Zusätzliche Status-Felder für HS1SA-E\n deviceInfo.trouble = data.trouble || false;\n deviceInfo.tamper = data.tamper || false;\n \n // Link Quality\n deviceInfo.linkquality = data.linkquality || 0;\n \n // Last seen aus ZigBee2MQTT\n deviceInfo.last_seen = data.last_seen || new Date().toISOString();\n \n // Batterie OK Status berechnen\n deviceInfo.battery_ok = true;\n if (deviceInfo.battery_low === true || \n (typeof deviceInfo.battery === 'number' && deviceInfo.battery < 20)) {\n deviceInfo.battery_ok = false;\n }\n \n // Timestamp für die aktuelle Abfrage\n deviceInfo.timestamp = new Date().toISOString();\n deviceInfo.timestamp_formatted = new Date().toLocaleString('de-DE');\n \n deviceInfo.friendly_name = \"KellerWerkstatt\";\n \n msg.payload = deviceInfo;\n \n return msg;\n}\n\nreturn msg;",
"outputs": 1,
"timeout": 0,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 650,
"y": 140,
"wires": [
[
"3deb8b862d1238e4",
"1fc2a6b8f60b088d",
"f8b1b27956f09b3e",
"147944ef03bfde4c",
"4ab2f6e25855efce",
"9d6eebcf795ee86e",
"ac1607f8f0db6b5d",
"24495c060d2a8756",
"7a1904f924795633",
"g7h8i9j0k1l2m3n4"
]
]
},
{
"id": "296a847a7c85c981",
"type": "pushover api",
"z": "a906f7d4665bf705",
"g": "f19c1232b5818c2a",
"keys": "13d7f9e6fd4a0fe1",
"title": "Rauchmelder Keller Werkstatt",
"name": "Rauchmelder Keller Werkstatt",
"x": 570,
"y": 300,
"wires": []
},
{
"id": "3deb8b862d1238e4",
"type": "function",
"z": "a906f7d4665bf705",
"g": "f19c1232b5818c2a",
"name": "Statusbericht formatieren",
"func": "// Statusbericht formatieren\nconst data = msg.payload;\nconst friendlyName = data.friendly_name || \"Rauchmelder Keller Werkstatt\";\nconst timestamp = new Date().toLocaleString('de-DE', {\n weekday: 'long',\n year: 'numeric',\n month: '2-digit',\n day: '2-digit',\n hour: '2-digit',\n minute: '2-digit',\n second: '2-digit'\n});\n\n// Batteriestatus\nlet batteryEmoji = \"🔋\";\nlet batteryStatus = \"\";\nif (data.battery <= 20) {\n batteryEmoji = \"⚠️🔴\";\n batteryStatus = \"KRITISCH - SOFORT WECHSELN!\";\n} else if (data.battery <= 50) {\n batteryEmoji = \"⚠️🟡\";\n batteryStatus = \"Bald wechseln\";\n} else if (data.battery <= 80) {\n batteryEmoji = \"🟢\";\n batteryStatus = \"Gut\";\n} else {\n batteryEmoji = \"💚\";\n batteryStatus = \"Sehr gut\";\n}\n\n// Geräte-Status\nlet deviceStatus = \"✅ OK\";\nif (data.battery_low) deviceStatus = \"⚠️ Batterie schwach\";\nif (data.trouble) deviceStatus = \"🔴 Störung\";\nif (data.tamper) deviceStatus = \"🔧 Manipulation\";\nif (data.smoke) deviceStatus = \"🚨 RAUCHALARM!\";\n\n// Linkqualität (LQI: 0-255)\nlet linkQualityText = \"🔴 Schlecht\";\nif (data.linkquality >= 200) linkQualityText = \"🟢 Sehr gut\";\nelse if (data.linkquality >= 150) linkQualityText = \"🟢 Gut\";\nelse if (data.linkquality >= 100) linkQualityText = \"🟡 Mittel\";\nelse if (data.linkquality >= 50) linkQualityText = \"🟠 Schwach\";\n\n// Last seen formatieren\nlet lastSeenStr = \"Unbekannt\";\nif (data.last_seen) {\n try {\n lastSeenStr = new Date(data.last_seen).toLocaleString('de-DE');\n } catch (e) {\n lastSeenStr = data.last_seen;\n }\n}\n\n// Statusbericht\nconst reportTitle = `Statusbericht - Rauchmelder`; // Ohne Emoji für Header\nconst reportMessage = \n `📋 ${reportTitle}\\n` +\n `Gerät: ${friendlyName}\\n` +\n `${batteryEmoji} Batterie: ${data.battery}% - ${batteryStatus}\\n` +\n `📶 Signal: ${linkQualityText} (${data.linkquality} LQI)\\n` +\n `🔄 Gerätestatus: ${deviceStatus}\\n` +\n `🕒 Letzter Kontakt: ${lastSeenStr}\\n` +\n `📊 Bericht erstellt: ${timestamp}\\n\\n` +\n `Letzte Werte:\\n` +\n `• Rauch gemeldet: ${data.smoke ? 'Ja 🚨' : 'Nein ✅'}\\n` +\n `• Batterie schwach: ${data.battery_low ? 'Ja ⚠️' : 'Nein ✅'}\\n` +\n `• Manipulation: ${data.tamper ? 'Ja 🔧' : 'Nein ✅'}\\n` +\n `• Störung: ${data.trouble ? 'Ja 🔴' : 'Nein ✅'}\\n` +\n `• Test-Modus: ${data.test_mode ? 'Ja 🔧' : 'Nein ✅'}\\n\\n`;\n\n// Für Pushover\nmsg.pushover = {\n // url: \"https://ntfy.sh/SIDf9wci139aaFXG-Rauchmelder\",\n // method: \"POST\",\n headers: {\n \"Title\": reportTitle.replace(/[^a-zA-Z0-9\\s-]/g, ''), // Entfernt alle Sonderzeichen\n \"Tags\": data.battery <= 20 ? \"warning,rotating_light,battery\" : \"battery,rotating_light\",\n \"Priority\": data.battery <= 20 ? \"high\" : \"default\"\n },\n payload: reportMessage\n};\n\n// Für ntfy - Header müssen ASCII-konform sein (keine Umlaute oder Emojis)\nmsg.ntfy = {\n url: \"https://ntfy.sh/SIDf9wci139aaFXG-Rauchmelder\",\n method: \"POST\",\n headers: {\n \"Title\": reportTitle.replace(/[^a-zA-Z0-9\\s-]/g, ''), // Entfernt alle Sonderzeichen\n \"Tags\": data.battery <= 20 ? \"warning,rotating_light,battery\" : \"battery,rotating_light\",\n \"Priority\": data.battery <= 20 ? \"high\" : \"default\"\n },\n payload: reportMessage\n};\n\n// Kennzeichnung für Debug\nmsg.is_battery_report = true;\n\nreturn msg;",
"outputs": 1,
"timeout": 0,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 230,
"y": 200,
"wires": [
[
"acbb6ccfa4615f05",
"0f916dab840c7276"
]
]
},
{
"id": "acbb6ccfa4615f05",
"type": "function",
"z": "a906f7d4665bf705",
"g": "f19c1232b5818c2a",
"name": "ntfy vorbereiten",
"func": "// ntfy Request vorbereiten\nconst ntfyConfig = msg.ntfy;\n\n// HTTP Request für ntfy erstellen\nmsg.url = ntfyConfig.url;\nmsg.method = ntfyConfig.method;\nmsg.headers = ntfyConfig.headers;\nmsg.payload = ntfyConfig.payload;\n\n// Debug-Ausgabe (optional, kann später entfernt werden)\nnode.warn(\"ntfy Header: \" + JSON.stringify(msg.headers));\n\nreturn msg;",
"outputs": 1,
"timeout": 0,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 500,
"y": 200,
"wires": [
[
"2cf42ba58b153108"
]
]
},
{
"id": "2cf42ba58b153108",
"type": "http request",
"z": "a906f7d4665bf705",
"g": "f19c1232b5818c2a",
"name": "Keller Werkstatt ntfy senden",
"method": "use",
"ret": "txt",
"paytoqs": "ignore",
"url": "",
"tls": "",
"persist": false,
"proxy": "",
"insecureHTTPParser": false,
"authType": "",
"senderr": false,
"headers": [],
"x": 560,
"y": 260,
"wires": [
[]
]
},
{
"id": "0f916dab840c7276",
"type": "function",
"z": "a906f7d4665bf705",
"g": "f19c1232b5818c2a",
"name": "Pushover vorbereiten",
"func": "// Pushover Request vorbereiten\nconst pushoverConfig = msg.pushover;\n\n// HTTP Request für Pushover erstellen\nmsg.headers = pushoverConfig.headers;\nmsg.payload = pushoverConfig.payload;\n\nreturn msg;",
"outputs": 1,
"timeout": 0,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 300,
"y": 300,
"wires": [
[
"296a847a7c85c981"
]
]
},
{
"id": "fd8f8ffdf77a106c",
"type": "function",
"z": "a906f7d4665bf705",
"g": "f19c1232b5818c2a",
"name": "Rauchmelder Daten aufbereiten",
"func": "// ZigBee2MQTT Daten parsen\nlet data = msg.payload;\nlet deviceInfo = {};\nlet alarmStatus = false;\n\n// Prüfen ob Daten vorhanden sind\nif (typeof data === 'object') {\n // Batteriestatus (in % oder Spannung)\n deviceInfo.battery = data.battery || data.battery_percentage || 0;\n deviceInfo.battery_low = data.battery_low || false;\n \n // Test-Modus (falls vorhanden)\n deviceInfo.test_mode = data.test || false; // Wichtig: heißt 'test' im HS1SA-E\n \n // Rauchmelder Status (Alarm!)\n deviceInfo.smoke = data.smoke || false;\n alarmStatus = deviceInfo.smoke; // Für Alarmerkennung\n \n // Link Quality\n deviceInfo.linkquality = data.linkquality || 0;\n \n // Last seen aus ZigBee2MQTT\n deviceInfo.last_seen = data.last_seen || new Date().toISOString();\n \n // Batterie OK Status berechnen\n deviceInfo.battery_ok = true;\n if (deviceInfo.battery_low === true || \n (typeof deviceInfo.battery === 'number' && deviceInfo.battery < 20)) {\n deviceInfo.battery_ok = false;\n }\n \n // Timestamp für die aktuelle Abfrage\n deviceInfo.timestamp = new Date().toISOString();\n deviceInfo.timestamp_formatted = new Date().toLocaleString('de-DE');\n deviceInfo.friendly_name = \"Keller Werkstatt\";\n \n // Alarm-Status für nachfolgende Nodes\n msg.payload = deviceInfo;\n msg.alarm_active = alarmStatus; // Explizites Flag für Alarm\n msg.alarm_triggered = alarmStatus; // Alternative Bezeichnung\n \n return msg;\n}\n\nreturn msg;",
"outputs": 1,
"timeout": 0,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 570,
"y": 400,
"wires": [
[
"0469b6708a6631f2"
]
]
},
{
"id": "0469b6708a6631f2",
"type": "switch",
"z": "a906f7d4665bf705",
"g": "f19c1232b5818c2a",
"name": "Alarm-Status prüfen",
"property": "alarm_active",
"propertyType": "msg",
"rules": [
{
"t": "true"
}
],
"checkall": "true",
"repair": false,
"outputs": 1,
"x": 240,
"y": 460,
"wires": [
[
"e45e871a10a710b2",
"685b6cb6ac3300e8"
]
]
},
{
"id": "e45e871a10a710b2",
"type": "trigger",
"z": "a906f7d4665bf705",
"g": "f19c1232b5818c2a",
"name": "30s Blinksteuerung",
"op1": "start",
"op2": "stop",
"op1type": "str",
"op2type": "str",
"duration": "30",
"extend": false,
"overrideDelay": false,
"units": "s",
"reset": "",
"bytopic": "all",
"topic": "topic",
"outputs": 2,
"x": 910,
"y": 460,
"wires": [
[
"95efe3c5faf4ab5b"
],
[
"b69dfaa826fb2d91"
]
]
},
{
"id": "95efe3c5faf4ab5b",
"type": "function",
"z": "a906f7d4665bf705",
"g": "f19c1232b5818c2a",
"name": "Blink-Modus vorbereiten",
"func": "// Blink-Modus für die Kerzenbirne\n// Sekundentakt: 1s an, 1s aus\n\n// Aktuellen Zustand der Birne abfragen und speichern\n// Dazu müssten Sie den aktuellen Status kennen - hier als Platzhalter\nflow.set(\"light_previous_state\", {\n state: \"ON\", // Hier müsste der echte Status ermittelt werden\n brightness: 254\n});\n\n// Blinken aktivieren\nflow.set(\"blink_active\", true);\nflow.set(\"blink_counter\", 0);\nflow.set(\"blink_state\", \"OFF\"); // Startet mit OFF, dann wechselt zu ON\n\n// Ersten Befehl senden (Licht an in Rot)\nmsg.topic = \"zigbee2mqtt/KerzenbirneBuero/set\";\nmsg.payload = {\n \"state\": \"ON\",\n \"brightness\": 254,\n \"color\": { \"hex\": \"#FF0000\" }\n};\n\nnode.warn(\"Alarm-Blinken gestartet\");\n\nreturn msg;",
"outputs": 1,
"timeout": 0,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 930,
"y": 400,
"wires": [
[
"6f243fcb4da873d4"
]
]
},
{
"id": "6f243fcb4da873d4",
"type": "function",
"z": "a906f7d4665bf705",
"g": "f19c1232b5818c2a",
"name": "Toggle-Blinken",
"func": "// Diese Funktion erzeugt den Sekundentakt\n// Sie wird von einem Inject-Node im Sekundentakt aufgerufen\n\n// Aktuellen Zustand aus Flow-Kontext holen\nlet currentState = flow.get(\"blink_state\") || \"OFF\";\nlet counter = flow.get(\"blink_counter\") || 0;\nlet blinkActive = flow.get(\"blink_active\") || false;\n\n// Prüfen ob Blinken aktiv ist (vom Trigger gesetzt)\nif (!blinkActive) {\n // Wenn nicht aktiv, nichts tun\n return null;\n}\n\n// Zustand umschalten\nlet newState = (currentState === \"ON\") ? \"OFF\" : \"ON\";\nflow.set(\"blink_state\", newState);\n\n// Zähler erhöhen\ncounter++;\nflow.set(\"blink_counter\", counter);\n\n// Nach 30 Sekunden automatisch beenden (30 Toggles = 30 Sekunden bei 1s Intervall)\nif (counter >= 30) {\n // Blinken beenden\n flow.set(\"blink_counter\", 0);\n flow.set(\"blink_state\", \"OFF\");\n flow.set(\"blink_active\", false);\n\n // Vorherigen Zustand wiederherstellen\n let previousState = flow.get(\"light_previous_state\") || { state: \"OFF\" };\n\n msg.topic = \"zigbee2mqtt/KerzenbirneBuero/set\";\n msg.payload = {\n \"state\": previousState.state || \"OFF\"\n };\n\n node.warn(\"Alarm-Blinken nach 30s beendet\");\n return msg;\n}\n\n// Befehl für die Kerzenbirne\nmsg.topic = \"zigbee2mqtt/KerzenbirneBuero/set\";\nif (newState === \"ON\") {\n msg.payload = {\n \"state\": \"ON\",\n \"brightness\": 254,\n \"color\": { \"hex\": \"#FF0000\" } // Rot\n };\n} else {\n msg.payload = {\n \"state\": \"OFF\"\n };\n}\n\nreturn msg;",
"outputs": 1,
"timeout": 0,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 1200,
"y": 440,
"wires": [
[
"191ef45fc6c07a97"
]
]
},
{
"id": "b69dfaa826fb2d91",
"type": "function",
"z": "a906f7d4665bf705",
"g": "f19c1232b5818c2a",
"name": "Blinken beenden",
"func": "// Blinken stoppen\nflow.set(\"blink_active\", false);\nflow.set(\"blink_counter\", 0);\nflow.set(\"blink_state\", \"OFF\");\n\n// Vorherigen Zustand wiederherstellen\nlet previousState = flow.get(\"light_previous_state\") || { state: \"OFF\" };\n\nmsg.topic = \"zigbee2mqtt/KerzenbirneBuero/set\";\nmsg.payload = {\n \"state\": previousState.state || \"OFF\"\n};\n\nnode.warn(\"Alarm-Blinken gestoppt\");\n\nreturn msg;",
"outputs": 1,
"timeout": 0,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 1210,
"y": 400,
"wires": [
[
"191ef45fc6c07a97"
]
]
},
{
"id": "685b6cb6ac3300e8",
"type": "function",
"z": "a906f7d4665bf705",
"g": "f19c1232b5818c2a",
"name": "Alarm-Pushover",
"func": "// Alarm-Benachrichtigung für Pushover\nconst data = msg.payload;\nconst timestamp = new Date().toLocaleString('de-DE', {\n weekday: 'long',\n year: 'numeric',\n month: '2-digit',\n day: '2-digit',\n hour: '2-digit',\n minute: '2-digit',\n second: '2-digit'\n});\n\n// Gerätename ermitteln (mit Fallback)\nconst deviceName = data.friendly_name || 'Rauchmelder Werkstatt';\n\n// Alarm-Nachricht formatieren\nlet alarmMessage =\n `🚨 RAUCHALARM! 🚨\n\nGerät: ${deviceName}\nZeit: ${timestamp}\n\n📍 Bereich: ${deviceName}\n⚠️ Es wurde RAUCH detektiert!\n\nBitte überprüfen Sie den Bereich SOFORT!`;\n\n// Pushover-spezifische Felder\nmsg.title = `🚨 RAUCHALARM - ${deviceName}`;\nmsg.payload = alarmMessage;\nmsg.priority = 1; // High Priority\nmsg.sound = \"persistent\"; // Alarmton\nmsg.timestamp = Math.floor(Date.now() / 1000);\n\nreturn msg;",
"outputs": 1,
"timeout": 0,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 460,
"y": 500,
"wires": [
[
"6247c2a58286fc73"
]
]
},
{
"id": "efd4d502334fbc36",
"type": "inject",
"z": "a906f7d4665bf705",
"g": "f19c1232b5818c2a",
"name": "Sekundentakt für Blinken",
"props": [
{
"p": "payload"
}
],
"repeat": "1",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "",
"payload": "",
"payloadType": "date",
"x": 930,
"y": 500,
"wires": [
[
"6f243fcb4da873d4"
]
]
},
{
"id": "6247c2a58286fc73",
"type": "pushover api",
"z": "a906f7d4665bf705",
"g": "f19c1232b5818c2a",
"keys": "13d7f9e6fd4a0fe1",
"title": "Rauchmelder Werkstatt Alarm",
"name": "Pushover Werkstatt",
"x": 670,
"y": 500,
"wires": []
},
{
"id": "ab3764d33b337c6f",
"type": "comment",
"z": "a906f7d4665bf705",
"g": "f19c1232b5818c2a",
"name": "=== ALARM-BENACHRICHTIGUNG & BLINKEN ===",
"info": "",
"x": 280,
"y": 360,
"wires": []
},
{
"id": "0f3d4c079b703181",
"type": "zigbee2mqtt-in",
"z": "a906f7d4665bf705",
"g": "f19c1232b5818c2a",
"name": "",
"server": "1bfcccb11dd30d19",
"friendly_name": "KellerWerkstatt (HS1SA-E)",
"device_id": "0xf84477fffe123ed0",
"state": "0",
"outputAtStartup": true,
"filterChanges": true,
"enableMultiple": false,
"x": 190,
"y": 400,
"wires": [
[
"fd8f8ffdf77a106c",
"d4e9451a20607f2f"
]
]
},
{
"id": "cb8dcc5798bb85a4",
"type": "inject",
"z": "a906f7d4665bf705",
"g": "f19c1232b5818c2a",
"name": "Manueller Test",
"props": [
{
"p": "payload"
},
{
"p": "topic",
"vt": "str"
}
],
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "",
"payload": "",
"payloadType": "date",
"x": 260,
"y": 500,
"wires": [
[
"685b6cb6ac3300e8",
"e45e871a10a710b2"
]
]
},
{
"id": "551b4d13bc9b505f",
"type": "inject",
"z": "a906f7d4665bf705",
"g": "f19c1232b5818c2a",
"name": "",
"props": [
{
"p": "payload"
},
{
"p": "topic",
"vt": "str"
}
],
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "",
"payload": "",
"payloadType": "date",
"x": 1070,
"y": 360,
"wires": [
[
"b69dfaa826fb2d91"
]
]
},
{
"id": "191ef45fc6c07a97",
"type": "zigbee2mqtt-out",
"z": "a906f7d4665bf705",
"g": "f19c1232b5818c2a",
"name": "Kerzenbirne",
"server": "1bfcccb11dd30d19",
"friendly_name": "KerzenbirneBuero (HG07834B/HG09155B/HG08131B)",
"device_id": "0x8cf681fffeaf1b37",
"command": "state",
"commandType": "z2m_cmd",
"payload": "payload",
"payloadType": "msg",
"optionsValue": "",
"optionsType": "nothing",
"x": 1210,
"y": 500,
"wires": []
},
{
"id": "d4e9451a20607f2f",
"type": "debug",
"z": "a906f7d4665bf705",
"g": "f19c1232b5818c2a",
"name": "Werkstatt",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": true,
"complete": "payload",
"targetType": "msg",
"statusVal": "payload",
"statusType": "auto",
"x": 600,
"y": 360,
"wires": []
},
{
"id": "1bfcccb11dd30d19",
"type": "zigbee2mqtt-server",
"name": "Sonoff ZigBee Raspi",
"host": "Raspi",
"mqtt_port": "1883",
"mqtt_username": "raspi",
"mqtt_password": "password",
"mqtt_qos": "2",
"tls": "",
"usetls": false,
"base_topic": "zigbee2mqtt"
},
{
"id": "5673b51cc6b259c0",
"type": "ui-group",
"name": "Keller Werkstatt",
"page": "5515adb8b2455e2c",
"width": "3",
"height": 1,
"order": 2,
"showTitle": true,
"className": "",
"visible": "true",
"disabled": "false",
"groupType": "default"
},
{
"id": "13d7f9e6fd4a0fe1",
"type": "pushover-keys",
"name": "Rauchmelder Berichte, Warnungen"
},
{
"id": "5515adb8b2455e2c",
"type": "ui-page",
"name": "Rauchmelder",
"ui": "2e2c9c04099535bf",
"path": "/page3",
"icon": "smoke",
"layout": "grid",
"theme": "12f619e2761c4ce7",
"breakpoints": [
{
"name": "Default",
"px": "0",
"cols": "3"
},
{
"name": "Tablet",
"px": "576",
"cols": "6"
},
{
"name": "Small Desktop",
"px": "768",
"cols": "9"
},
{
"name": "Desktop",
"px": "1024",
"cols": "12"
}
],
"order": 1,
"className": "",
"visible": "true",
"disabled": "false"
},
{
"id": "2e2c9c04099535bf",
"type": "ui-base",
"name": "Dashboard @flowuse",
"path": "/dashboard",
"appIcon": "",
"includeClientData": true,
"acceptsClientConfig": [
"ui-notification",
"ui-control"
],
"showPathInSidebar": false,
"headerContent": "dashboard",
"navigationStyle": "default",
"titleBarStyle": "default",
"showReconnectNotification": true,
"notificationDisplayTime": 1,
"showDisconnectNotification": true,
"allowInstall": false
},
{
"id": "12f619e2761c4ce7",
"type": "ui-theme",
"name": "Standardthema",
"colors": {
"surface": "#5734fc",
"primary": "#e3b450",
"bgPage": "#eeeeee",
"groupBg": "#ffffff",
"groupOutline": "#cccccc"
},
"sizes": {
"density": "default",
"pagePadding": "12px",
"groupGap": "12px",
"groupBorderRadius": "4px",
"widgetGap": "12px"
}
},
{
"id": "c812ee4757f928dd",
"type": "global-config",
"env": [],
"modules": {
"node-red-contrib-zigbee2mqtt": "2.7.5",
"node-red-node-email-variable": "1.1.7",
"@flowfuse/node-red-dashboard": "1.30.2",
"node-red-contrib-pushover": "0.2.0"
}
}
]
