NEWS
Fensterkontakte mit offen/geschlossen-Erkennung - preisgünstige Umsetzung
-
Moin,
im Bereich günstige Fensterkontakte habe ich hier bisher nur entweder Ideen gelesen, die
-
nicht wirklich günstig
-
nur offen-Meldung
-
selbstgelötet
-
mit Firmware-flashen
umgesetzt sind. Das Alles wollte ich so nicht haben.
Die jetzt umgesetzt Lösung funktioniert komplett ohne die oben genannten Punkte.
Hardware
- Fenstermelder:
Zum Einsatz kommen günstige Reedkontakte mit EV1527-Chip. Meine sind vom Ali, 6Stck. für 22€ (versandkostenfrei). Link gerne per PN, ich weiss nicht ob das hier gern gesehen wird. Sie können auf/zu senden, sind recht klein, haben eine normale AA-Batterie.
- Empfänger
Ein USB-DVB-T-Empfänger mit RTL2832U. Gibt es bei Amazon für 10-15€, bei Ali deutlich billiger.
- Ein freier USB-Port an einem unter Linux laufenden Rechner, der 24h an ist. Das kann der iobroker-Raspi sein, muss es aber nicht
Wie funktionierts?
Der Signalweg ist grob gesagt wie folgt:
EV1527 –> RTL2832U --> RTL_433 (Software) --> MQTT --> iobroker --> abgewandeltes Fensterkontakte-Script
Ergebnis:
voUmsetzung
- Der Empfänger wird über das geniale Programm RTL_433 eingebunden. Zu finden unter https://github.com/merbanan/rtl_433
Die Installationsanleitung ist prima, könnte ich hier nur abschreiben. Hier: https://github.com/merbanan/rtl_433/blo … UILDING.md
- Die Anbindung an MQTT ist nicht wie des öfteren zu lesen über mosquitto_pub. Das geht zwar, aber man bekommt halt nur die komplette JSON-Info in ein Topic im MQTT. Ziel ist aber ein netter Zustand der genau sagt ob auf oder zu..
Es wird stattdessen eine abgewandelte Version des netten python-Scriptes von mervleun (https://github.com/mverleun/RTL433-to-mqtt) verwendet.
Ich habe mir für die beiden Dateien ein Verzeichnis rtl2mqtt unter /usr/local/lib angelegt, aber alles wie man selber möchte...
Es wird das python-Modul paho.mqtt benötigt:
pip3 install paho-mqtt
Sofern man noch kein pip3 installiert hat, ggf. vorneweg noch
sudo apt install python3-pip sudo pip3 install wheel setuptools
Hier die angepasste Version des Scriptes. Unterschiede:
-
Pro Fensterkontakt (also ID) wird ein eigenes Topic generiert. Das Original sendet alles unter demselben Topic.
-
Ich "höre" nur mit dem Decoder 30, der für die EV1527 gut ist. Wenn man weitere Sachen will, dann nimmt man -G in der Zeile mit dem rtl_433_cmd
-
Gleiche Zeilen werden nicht gesendet.
! Datei rtl2mqtt
#!/usr/bin/env python3 # -*- coding: UTF-8 -*- >! import subprocess import sys import time import paho.mqtt.client as mqtt import os import json >! from config import * >! rtl_433_cmd = "/usr/local/bin/rtl_433 -R 30 -F json" # linux >! # Define MQTT event callbacks def on_connect(client, userdata, flags, rc): print("Connected with result code "+str(rc)) >! def on_disconnect(client, userdata, rc): if rc != 0: print("Unexpected disconnection.") >! def on_message(client, obj, msg): print(msg.topic + " " + str(msg.qos) + " " + str(msg.payload)) >! def on_publish(client, obj, mid): print("mid: " + str(mid)) >! def on_subscribe(client, obj, mid, granted_qos): print("Subscribed: " + str(mid) + " " + str(granted_qos)) >! def on_log(client, obj, level, string): print(string) >! # Setup MQTT connection >! mqttc = mqtt.Client(MQTT_CLIENT_ID) # Assign event callbacks #mqttc.on_message = on_message mqttc.on_connect = on_connect #mqttc.on_publish = on_publish mqttc.on_subscribe = on_subscribe mqttc.on_disconnect = on_disconnect >! # Uncomment to enable debug messages #mqttc.on_log = on_log >! # Uncomment the next line if your MQTT server requires authentication #mqttc.username_pw_set(MQTT_USER, password=MQTT_PASS) mqttc.connect(MQTT_HOST, MQTT_PORT, 60) >! mqttc.loop_start() >! # Start RTL433 listener rtl433_proc = subprocess.Popen(rtl_433_cmd.split(),stdout=subprocess.PIPE,stderr=subprocess.STDOUT,universal_newlines=True) >! while True: lastline/ for line in iter(rtl433_proc.stdout.readline, '\n'): if line == lastline: continue >! if "time" in line: mqttc.publish(MQTT_TOPIC, payload=line,qos=MQTT_QOS) json_dict = json.loads(line) for item in json_dict: value = json_dict[item] if "model" in item: subtopic=value elif "id" in item: id=value >! for item in json_dict: value = json_dict[item] if not "model" in item and not "id" in item: mqttc.publish(MQTT_TOPIC+"/"+subtopic+"/"+str(id)+"/"+item, payload=value,qos=MQTT_QOS) >! lastline=line >! ```` >! und als config.py:
Config section
Uncomment these lines if your MQTT server requires authentication
#MQTT_USER="mqtt-user"
#MQTT_PASS="mqtt-password"
MQTT_HOST="iobroker-adresse"
MQTT_PORT=1883
MQTT_TOPIC="sensors/rtl_433"
MQTT_QOS=0
MQTT_CLIENT_ID="RTL_433"End config section
! ````
Um das ganze als Service laufen zu lassen, dient die folgende service-Datei. Diese als rtl_433-mqtt.service in /etc/systemd/system anlegen.
!
[Unit] Description=rtl_433 to MQTT publisher After=network.target [Service] ExecStart=/usr/bin/python3 /usr/local/lib/rtl2mqtt/rtl2mqtt.py Restart=always RestartSec=5 [Install] WantedBy=multi-user.target !
- Das ganze via
sudo systemctl daemon-reload sudo systemctl start rtl_433-mqtt
starten.
In ioBroker sollte der mqtt-Adapter als broker laufen. Sofern es auf demselben Host wie der rest ist oder in einem gesicherten Netz, sind auch keine Passwörter/SSL etc. notwendig.
Wenn ihr die Reeds jetzt öffnet und schließt, solltet ihr in iobroker schon sehen, dass neue States unterhalb des mqtt-Adapters angelegt werden und sich die Werte ändern. Jeder Kontakt hat eine eigene 4-stellige ID.
Das Fensterscript
Es kommt das Fensterscript von http://www.iobroker.net/docu/?page_id=5158&lang=de zum Einsatz.
Dazu sind Vorarbeiten und Anpassungen nötig.
Vorarbeiten: Der MQTT-Adapter legt nur States, aber keine Channels an. Das Script arbeitet aber mit Channels. Das macht aber nicht, man kann die entsprechenden Channel einfach selbst anlegen. Unterhalb von mqtt.0.sensors.rtl_433.Generic_Remote Einfach einen channel mit der id = Ordner-Name des Fensterkontaktes anlegen, dieser "ersetzt" dann den vorhandenen Ordner und die States (cmd, time, tristate) werden darunter einsortiert.
Rolle ist switch, als Name kann z.B. Das Zimmer angegeben werden.
Die Channel sollten lt. Script dem Gewerk Fenster zugeordnet werden. Damit funktioniert bei mir der Trigger nicht, warum kann ich nicht sagen.
Jetzt das abgewandelte Script.
! ````
/* System Zahl Fensterzählt die Fenster in allen Räumen und meldet die offenen Fenster namentlich
Daten kommen vom Gewerk 'Fenster'erstellt: 17.06.2015 nach diesem Vorbild: http://forum.iobroker.net/viewtopic.php?f=21&t=869&sid=f9ba5657489ff431a1990884f90779c1#p6564
05.08.2015 STATE aus der Ausgabe entfernt nach Idee von stephan61 http://forum.iobroker.org/viewtopic.php?f=21&t=473&start=20#p9267
02.12.2015 Fehler beseitigt bei Anzahl Fenster, wenn offen (Unterscheidung RHS und TFK)
Überprüfung des Zustandes (function fensterstatus) und Übergabe an Text
25.01.2016 Fenster Nummer in Log korrigiert (+1)
02.03.2016 Ansage für TTS aufbereitet
02.07.2016 Regulärer Ausdruck (gierig) bei Aufbereitung für Ansage
*/function fensterstatus(zustand) {
var meldung;
switch (zustand) {
case 10:
meldung = 'TFK offen';
break;
case 14:
meldung = 'geschlossen';
break;
}
return(meldung);
}createState('zählen_Fenster.anzahlFenster', { // wenn benötigt: Anzahl der vorhandenen Fenster
type: 'number',
min: 0,
def: 0,
role: 'value'
});
createState('zählen_Fenster.anzahlFensterauf', { // Anzahl der Fenster, die auf sind als Variable unter Javascript.0 anlegen
type: 'number',
min: 0,
def: 0,
role: 'value'
});
createState('zählen_Fenster.textFensterauf', { // Anzahl der offenen Fenster und deren Namen als Variable unter Javascript.0 anlegen
type: 'string',
def: ' ',
role: 'value'
});var cacheSelectorState = $('channel[role=switch][state.id=*.cmd]'); // funktioniert leider aktuell nicht bei Einschränkung mit functions=Fenster
function countFenster(obj) {
// Setzt die Zähler vor dem Durchlauf aller Elemente *.cmd im Gewerk Fenster auf 0
var anzahlFenster = 0;
var anzahlFensterauf = 0;
var textFensterauf = [];log('Auslösender Aktor: ' + obj.id + ': ' + obj.newState.val); // Info im Log, welcher Zustand sich geändert hat cacheSelectorState.each(function (id, i) { // Schleife für jedes gefundenen Element *.cmd im Gewerk Fenster var status = getState(id).val; // Zustand *.cmd abfragen (jedes Element) var obj = getObject(id); var name = getObject(id).common.name; var devicename = id.substring(0, id.indexOf(".cmd")); //.state aus Text entfernen var channame = getObject(devicename).common.name; if (status === 10) { // wenn Zustand offen, dann wird die Anzahl der Fenster hochgezählt ++anzahlFensterauf; textFensterauf.push(channame + ' (' + fensterstatus(status) + ')'); // Name und Zustand zum Array hinzufügen } log('Fenster #' + (i+1) + ': ' + channame + ' ' + fensterstatus(status)/* + ' (' + status + ' / ' + typeof status + ')'*/); ++anzahlFenster; // Zählt die Anzahl der vorhandenen Fenster unabhängig vom Status }); // Schleife ist durchlaufen. Im Log wird der aktuelle Status (Anzahl, davon an) ausgegeben log("Text: " + textFensterauf); log("Anzahl Fenster: " + anzahlFenster + " - davon Fenster auf: " + anzahlFensterauf); // die ermittelten Werte werden als javascript.0\. Variable in ioBroker gespeichert (z.B. für die Verarbeitung in VIS) setState("zählen_Fenster.textFensterauf", textFensterauf.join(',
')); // Schreibt die aktuelle Namen der offenen Fenster
setState("zählen_Fenster.anzahlFensterauf", textFensterauf.length); // Schreibt die aktuelle Anzahl der offenen Fenster
setState("zählen_Fenster.anzahlFenster", anzahlFenster); // Schreibt die aktuelle Anzahl der vorhandene Elemente im Gewerk Fenster
}cacheSelectorState.on(function(obj) { // bei Zustandänderung *. cmd im Gewerk Fenster
countFenster(obj);
});// Variable für Ansage aufbereiten
createState('zählen_Fenster.textFensteraufAnsage', {
type: 'string',
def: ' ',
role: 'value'
});
// Anzahl der Fenster, die auf sind, für Ansage aufbereitet
var idQuelle = 'javascript.0.zählen_Fenster.textFensterauf',
idAnsage = 'javascript.0.zählen_Fenster.textFensteraufAnsage';on(idQuelle, function (obj) {
var text = obj.state.val;
text = text.replace(/RHS/g, 'Drehgriff');
text = text.replace(/TFK/g, 'Reedkontakt');
text = (text.length > 1) ? 'Geöffnete Fenster: ' + text : 'Alle Fenster sind verschlossen';
setState(idAnsage, text);
});! ````
Anregungen, Vorschläge, Verbesserungen gerne hier.
Gruß
Stef
-
-
Und wenn Dir den Aufwand mit der Einbindung des Empfänger sparen willst, dann nimmst die Sonoff RF Bridge und es läuft ohne irgendwelche extra Scrips unterm Sonoff oder MQTT Adapter. Die Bridge kostet ebenfalls um die 9€
So wie das die meisten hier schon am Laufen haben. Den Link zu den Kontakten kannst aber mal posten, da die wirklich sehr günstig sind. Ob das mit der AA Batterie ein Vorteil ist, wird sich an der Lebensdauer zeigen. Poste auf alle Fälle mal wie lange die gehalten haben.
-
Jo, nur halt mit Tasmota, also flashen. Und (soweit ich es verstanden habe), ist das flashen nicht mal nur USB anstecken und los, sondern mit löten..
Vorteil ist natürlich das es auch senden kann, Vorteil meiner Umsetzung wäre, dass es auch noch die Wetterstation vom Nachbarn oder die Reifendruckkontrolle übernehmen kann. RTL_433 ist einfach prima.
Aber jeder wie er möchte. Die Kontakte sind von https://de.aliexpress.com/item/6pcs-great-promotion-433MHz-EV1527-two-way-door-sensor-window-detector-door-gap-sensor-for-alarma/32713418101.html?spm=a2g0x.search0104.3.1.502242f2bPwYqk&ws_ab_test=searchweb0_0,searchweb201602_3_10320_10065_10068_10843_10547_10059_10548_10696_100031_10319_10084_10083_10103_451_10618_452_10304_10307_10820_10821_10302,searchweb201603_16,ppcSwitch_5&algo_expid=477ae2db-bd1f-469f-94b1-48cd9de943d6-0&algo_pvid=477ae2db-bd1f-469f-94b1-48cd9de943d6&priceBeautifyAB=0. Stehen jetzt leider bei 26,32€
-
Löten ist nicht nötig, kann man einfach mit ner Stiftleiste ran halten. Die Kontakte sind selbst für den Preis noch recht günstig
-
Hallo,
super Sache das mit MQTT zu machen.
Ich habe ebenfalls rtl_433 laufen und nutze dein abgeändertes Skript.
Allerdings versuche ich damit Temperatursensoren abzurufen - klappt bis auf eine Kleinigkeit echt gut.
Mir fehlt in den Datenpunkten (states) die Luftfeuchtigkeit also "humidity".
Kannst du mir sagen, wie ich die bekomme - mit dem Code werde ich so schnell nicht schlau
Danke vorab…