Anwesenheitserkennung über Beacons
Oftmals würde man gerne alle Geräte automatisch ausschalten wollen oder die Heizung zurückregeln, wenn niemand zuhause ist. Aber wie können wir feststellen, ob das eigene Heim wirklich verwaist ist?
Im Artikel Anwesenheitserkennung über GPS-Position haben wir einen anderen Ansatz verfolgt.
Wir wollen hier nicht auf die Mittel von Geofencing zurückgreifen, sondern mit sog. Bluetooth Beacons oder Bluetooth LE Tags arbeiten. In diesem Falle handelt es sich um einen sehr kleinen Sender, den man normalerweise am Schlüsselbund bei sich trägt. Jedenfalls hat man dieses Gerät immer bei sich, also könnte die Haussteuerung diesen Beacon über Bluetooth erkennen und somit die Anwesenheit feststellen.
Der Vorteil dieses Ansatzes ist, dass der Beacon eigenständig ist. Man muss ihn im Gegensatz zum Smartphone nie laden und die Batterie hält ewig. Die Reichweite beträgt in etwa 30m (ohne Hindernisse).
Hard-und Software-Voraussetzungen
Zuerst brauchen wir einen Raspberry Pi mit entsprechendem Netzteil. Hierzu habe ich auch entsprechende Empfehlungen gegeben.
Genauso sollte man sich über die Vernetzung Gedanken machen, ob man die Raspberry Pis per Netzkabel oder per WLAN anbinden möchte. Dafür habe ich hier in meinen Artikeln über die Grundkonfiguration Tipps gegeben:
- Checkliste für Raspberry Pi mit FHEM
- Raspberry Pi Grundkonfiguration
- FHEM Grundkonfiguration
- um bei Änderung des Anwesenheitsstatus etwas schalten zu können: CUL Integration in FHEM
Bluetooth-Hardware-Voraussetzungen
Um mit Beacons arbeiten zu können müssen wir den Raspberry Pi mit einem entsprechenden Bluetooth USB-Dongle nachrüsten. Nicht jeder Bluetooth Dongle funktioniert hier, er muss Bluetooth 4.0 beherrschen, denn wir wollen die LE (Low Energy) Technologie benutzen. Gute Erfahrungen habe ich mit folgendem USB-Dongle gemacht:
Dieser lässt sich problemlos in den Raspberry Pi einbinden und wird von Raspbian Linux-Derivat erkannt.
Bluetooth-Dongle installieren
Sobald wir den Bluetooth-Dongle in den Raspberry Pi in einen USB-Port gesteckt haben, sollte dieser als USB-Gerät auftauchen. Hierfür schreiben wir auf der Konsole
lsusb
und sehen uns das Ergebnis an:
In unserem Falle taucht der Bluetooth-Dongle als Device 004 auf. Ohne Installation der notwendigen Pakete können wir jetzt aber noch nicht wirklich viel damit anfangen. Mit folgenden Kommandos holen wir uns die notwendige Software auf den Raspberry Pi:
sudo apt-get install bluetooth bluez-hcidump bluez blueman
Nach einem Restart über
sudo shutdown -r now
sollte die notwendige Software auf dem Rapberry Pi installiert sein. Aber das reicht noch nicht aus, um Beacons wie im Folgenden entsprechend zu lokalisieren. Dazu gleich mehr.
Passende Beacons
Grundsätzlich sollten alle Beacons bzw. Bluetooth LE Tags für unsere Funktionalität passen. Meistens werden diese als Schlüsselfinder verkauft. Ich habe hier nach einer sehr günstigen Variante gesucht, die auch nicht wirklich viel können muss. Das Entscheidende ist eher das Thema Reichweite. Ich habe es mit folgenden Beacons erfolgreich getestet:
Ferner gibt es diese auch in günstigeren Mehrfachpacks. Damit das Ganze funktioniert, sollte auch jeder in der Familie so einen Beacon an seinem Schlüsselanhänger haben.
Um die gezeigten Beacons in Betrieb zu nehmen, zieht man die durchsichtige Plastiklasche aus dem Gerät und setzt die Batterie damit aktiv.
Inbetriebnahme der Beacons
Zuerst sollten wir uns mit der Grundlage unserer neuen Funktionalität beschäftigen. Dies ist das hcitool
, das einen permanenten LE-Scan durchführt. Führen wir einmal in der Konsole das Kommando
sudo hcitool lescan
aus. Nachdem hier Zeile für Zeile mit MAC-Adressen gescannter Bluetooth-Geräte nach unten scrollt, halten wir einmal diesen Endlos-Prozess mit der Tastenkombination STRG
+C
bzw. auf dem Mac mit ctrl
+C
an.
Sobald unser Beacon aufgelistet wurde, halten wir die Endlosausgabe wie gerade beschrieben an. Solange wir nur einen Beacon haben, ist dieser leicht zu identifizieren. Sobald wir mehrere in Betrieb nehmen, müssen wir diesen Schritt wiederholen und uns jeweils die MAC-Adresse des Beacons aufschreiben und dem richtigen zuordnen. In unserem Falle merken wir uns die MAC-Adresse 7C:2F:80:AD:C2:0D
.
Hilfsskript für Erreichbarkeit eines speziellen Beacons
Da das hcitool nur einen Endlos-Scan ausführen kann und manuell unterbrochen werden muss, müssen wir uns ein Hilfsskript basteln, das für eine spezielle MAC-Adress nachsehen kann, ob der Beacon erreichbar ist oder nicht.
Dazu kommt noch ein weiteres Problem, das erst auftritt, sobald wir mehr als einen Beacon besitzen. Damit habe ich mich eine Weile herumgeplagt, denn dann läuft der Scan mehr als einmal gleichzeitig. Folglich kommt es zu einem Fehler, weil der Bluetooth USB-Dongle im Raspberry Pi bereits mit dem Scan auf LE Geräte beschäftigt ist. Denn dann meldet uns das hcitool
folgenden Fehler:
Set scan parameters failed: Input/output error
Dies müssen wir in unserem Hilfsskript entsprechend berücksichtigen. Hierzu erstellen wir ein Shell-Skript, das wir im Verzeichnis /opt/fhem
erstellen. Dementsprechend kopieren wir uns den unten aufgelisteten Code in eine leere Datei namens lescan.sh
. Hierfür können wir den kleinen Editor nano
benutzen und die neue Datei mit
cd /opt/fhem nano lescan.sh
erstellen. Weiters kopieren wir den unten stehenden Code in den geöffneten Editor und speichern ihn mit STRG
+O
ab. Schließlich beenden wir den Editor mit STRG
+X
.
Im Anschluß der Code:
#!/bin/bash devicefile=/tmp/le_device.$$ hcitool lescan 2>&1 > $devicefile & sleep 7 pkill --signal SIGINT hcitool sleep 1 errorresult=$(grep -c "Input/output error" $devicefile) if [ $errorresult -gt 0 ]; then echo 2 else scanresult=$(grep -c $1 $devicefile) if [ $scanresult -gt 0 ]; then echo 1 else echo 0 fi fi rm $devicefile
Wir müssen aber dieses Skript noch ausführbar machen und dafür sorgen, dass der User fhem
das hcitool
ausführen darf. Zuerst setzen wir die Zugriffsrechte für die neue Datei mit:
chown fhem:dialout lescan.sh chmod 777 lescan.sh
Daraufhin brauchen wir noch den Eintrag in der sudoers-Datei, was wir mit dem Befehl
sudo visudo
erledigen. Dort fügen wir die Zeilen an
fhem ALL=(ALL) NOPASSWD: /usr/bin/hcitool fhem ALL=(ALL) NOPASSWD: /opt/fhem/lescan.sh
und speichern die Datei wieder mit STRG
+O
ab. Schließlich beenden wir den Editor mit STRG
+X.
Dadurch ist der Benutzer fhem
, unter dem die Haussteuerung läuft, berechtigt, das gerade erstellte Shell-Skript auszuführen. Da im Skript lescan.sh aber das hcitool ausgeführt wird, muss dieses ebenfalls berechtigt werden. Das erledigt die erste Zeile.
Einbinden in FHEM
Im Folgenden müssen wir jetzt nur noch eine Hilfsfunktion für FHEM erstellen, die das gerade erzeugte Shell-Skript aufruft. Hierzu erstellen wir ein neues Include, das wir im Verzeichnis /opt/fhem/FHEM
erstellen. Dementsprechend kopieren wir uns den unten aufgelisteten Code in eine leere Datei namens 99_BTLEscanUtils.pm
. Hierfür können wir den kleinen Editor nano
benutzen und die neue Datei mit
cd /opt/fhem/FHEM nano 99_BTLEscanUtils.pm
erstellen. Weiters kopieren wir den unten stehenden Code in den geöffneten Editor und speichern ihn mit STRG
+O
ab. Schließlich beenden wir den Editor mit STRG
+X
.
Im Anschluß der Code:
############################################## # $Id: 99_BTLEscanUtils.pm 2691 2017-01-06 Martin Koch $ package main; use strict; use warnings; use Digest::MD5 "md5_hex"; use HttpUtils; sub BTLEscanUtils_Initialize($$) { my ($hash) = @_; } sub get_LEBT_Status($$) { my ($le_device,$statusvar) = @_; my $semaphor_file ="/opt/fhem/log/lestatus_semaphor"; my $oldvalue = ReadingsVal($statusvar,"state","absent"); my @resultstrings = (); my $resultstring = ""; my $exitcount = 0; my $return = 2; # wait for unique le process while(-e $semaphor_file) { sleep(5); $exitcount += 1; if($exitcount > 10) { unlink $semaphor_file; return $oldvalue; } } open(SEM,">$semaphor_file"); print SEM "Semaphor written by $le_device for $statusvar"; close(SEM); @resultstrings = `sudo /opt/fhem/lescan.sh $le_device`; foreach my $resultstring (@resultstrings) { if($resultstring =~ /(\d)*/) { $return = $1; } } if($return > 1) { $return = $oldvalue; } # delete semaphor unlink $semaphor_file; return $return; } 1;
Nun müssen wir nur noch eine Definition für die Anwesenheitsüberwachung per PRESENCE
-Konstrukt in die Konfigurationsdatei fhem.cfg
einbauen, die bei regelmässig die Anwesenheit durch Aufrufen der gerade erstellten Hilfsmethode überprüft:
define Martin_BT_Tag PRESENCE function {get_LEBT_Status("7C:2F:80:AD:C2:0D","Martin_BT_Tag")} 60 180 attr Martin_BT_Tag devStateIcon present:rc_dot@green absent:rc_dot@red attr Martin_BT_Tag group Anwesenheitsstatus_Martin attr Martin_BT_Tag room Anwesenheit define FileLog_Martin_BT_Tag FileLog ./log/Martin_BT_Tag-%Y-%m-%V.log Martin_BT_Tag.* attr FileLog_Martin_BT_Tag logtype text
Nun schaltet sich die Variable Martin_BT_Tag
auf present
, sobald der Beacon in Reichweite des Raspberry Pi ist. Falls unser Wunderkästchen den Bluetooth LE-Dongle nicht per Scan findet, schaltet sich die Variable Martin_BT_Tag
auf absent
.
Wir zeigen dies mit unseren Define-Attributen mit einem grünen bzw. roten Punkt an.
Zusammenfassung
In unserem Beispiel für die Anwesenheitserkennung per Beacon passiert also folgendes: Sobald sich dieser Bluetooth LE-Dongle in Reichweite unseres Raspberry Pi befindet, soll eine Variable schalten. Nun könnten wir natürlich auf diese Wertänderung genauso reagieren wir bei der Anwesenheitserkennung über GPS-Position und beispielsweise die Heizung einschalten. Oder wir könnten eine Steckdose automatisch einschalten.
Damit das Ganze zuverlässiger wird, könnten wir jetzt noch die Anwesenheit zusätzlich über den WLAN-Status unseres Smartphones überprüfen und die beiden Status kombinieren. Sobald beide Variablen auf absent
stehen, können wir sicher gehen, dass sich die Person wirklich nicht mehr zuhause befindet. Aber dazu mehr in einem weiteren Artikel.
Vielen Dank für diesen Beitrag.
Am „Input/output error“ bin ich fast verzweifelt.
Ich hab mich da auch Tage gespielt…
Hi,
die Beschreibung ist super leider bekomme ich nur einen error .
Ich versuche drei gigaset keeper einzubinden.
Logauszug:
2018.02.02 16:53:33 2: PRESENCE (Giga_Marko_bluetooth) – error while processing check: unexpected function output (expected 0 or 1): error
sudo: /opt/fhem/lescan.sh: Befehl nicht gefunden
2018.02.02 16:54:26 2: PRESENCE (Giga_Marko_bluetooth) – error while processing check: unexpected function output (expected 0 or 1): error
Hast du eine Idee ?
Hallo,
sudo: /opt/fhem/lescan.sh: Befehl nicht gefunden
das deutet darauf hin, dass entweder der User fhem keinen Zugriff auf das unter /opt/fhem erstellte Shellskript lescan.sh hat oder das Shellskript dort wirklich nicht existiert.
Was passiert denn, wenn Du als User pi das Skript manuell startest, etwa mit
sudo /opt/fhem/lescan.sh <MAC-Adresse eines der Gigaset Keeper>
ausführst?
Bekommst Du den selben Fehler wie im Log, dass er schreibt „Befehl nicht gefunden“, dann heißt das Shellskript bei Dir evtl. auch anders…
Gruß
Martin
Hallo Martin,
funktioniert alles super. Danke für diese Anleitung. Habe 3 G-Tags am laufen.
Ich hatte den gleichen Fehler wie Marko..
Die Datei „/opt/fhem/lescan.sh“ musste nochmal die richtigen Berechtigungen haben!
Habe „chmod 777“ und „chown fhem:root“ auf die Datei durchgeführt.. Danach war der Fehler dann weg (auch in der Console).
Gruß,
SteRa
Hallo,
wunderbar. Vielen Dank für das Feedback!
Den chmod 777 habe ich noch in die Beschreibung mit eingebaut, eigentlich hätte aber ein chmod +x gereicht. Die Datei darf aber ruhig jeder auf dem Raspi ausführen…
Gruß
Martin
da ich kein FHEM habe, aber 3 G-Tags, möchte ich gerne wissen, ob man das auch in die Raspberrymatic integrieren kann.
Gruß,
Mathias
leider findet tante „G“ da nicht viel.
Gruß,
Mathias
Hallo Mathias,
ich sage nicht, dass es unmöglich wäre, die G-Tags auch in die Raspberrymatic zu integrieren. Aber mein Fokus ist ganz klar auf Raspberry Pi mit FHEM. Dies ist die flexibelste Variante, da man sich sehr schnell eigene Module zusammenbauen kann. In die Raspberry Homematic Variante kann man das nicht so einfach bewerkstelligen.
Wenn ich selbst eine Raspberrymatic hätte, würde ich mich mal spielen und sehen, was hier geht. Ohne Erfahrung kann ich hier leider keine Tipps geben…
Sorry for the bad news…
Gruß
Martin
Hey, mal eine Verständnisfrage: die G-tags bleiben quasi die ganze Zeit im Advertise Mode und können nicht anderweitig (z.B. App) verwendet werden?? Wäre verbinden vielleicht sparsamer?
Wie lange laufen die bei euch so bis die Batterie durch ist?
Hi,
ich habe schon mal versucht, sie parallel mit der App zu verwenden. Dann sind sie aber ausschließlich an die App gebunden und können nicht mehr gefunden werden.
Ich habe die 3 G-Tags, die ich im Einsatz habe, am 07.01.2017 bei Amazon gekauft. Alle drei funktionieren noch. Bei Amazon steht, dass sie 1 Jahr halten würden. Nun halten sie aber schon 1 Jahr und 2 Monate.
Und zur Frage: Auch wenn Verbinden vielleicht sparsamer wäre, so habe ich einen entscheidenen Nachteil entdeckt: Sie wurden viel schlechter erkannt bzw. gefunden als im Advertise Mode. Jedenfalls haben sie schon länger gehalten als angegeben.
Gruß
Martin
Danke für die fixe Antwort. Dann lass ich meine auch im Advertise-Modus, sind sie ja nur zur Anwesenheits-Steuerung gedacht. Ich bastel bereits an einem pyhton-Skript und werde es letztlich an homee anbinden.
Heute hatte ich einen Testlauf, allerdings hab ich auch immer das Input/Output Problem. Aber selbst auf der Konsole, wenn ich „lescan“ mehrmals nacheinander starte. Wobei ich nicht so richtig weiß wieso. Dann wird es ein down/up vor jedem Durchlauf richten müssen, aber wirklich gut erscheint mir das nicht.
Achso und in welcher Taktung hast du das laufen?
Danke – vor allem für den Artikel, so wusste ich wenigstens welche Tags und welcher USB-BT Stick direkt am Pi gehen. 🙂
Hi,
zur Taktung:
define Martin_BT_Tag PRESENCE function {get_LEBT_Status(„XX:XX:XX:XX:XX:XX“,“Martin_BT_Tag“)} 60 180
Also Interval 60 s, Anwesend-Interval 180 s
Bei mehreren G-Tags versuche ich, Werte zu verwenden, die kein Vielfaches der anderen sind. Beispielsweise 67 s vs. 60 s…
Gruß
Martin
Ich lese das auch in eine Datei und prüfe einfach direkt nacheinander ob die verwendeten G-tags in der Ausgabe stecken. Intern führe ich zwei Status-Dictionarys: letzter Stand und aktueller Stand – diese vergleiche ich dann noch direkt, schreibe Änderungen in ein log und melde via HTTP-Request (Webhook) die Veränderung ans SmartHome, aktuell nicht nutzerspezifisch, sondern nur ist (irgend)jemand anwesend/ist niemand anwesend.
Auf Grund der Dictionary-Struktur könnte ich aber auch einzelne User entsprechend melden, allerdings geht das in Homee nur etwas umständlich. 🙂
Hallo Martin,
vielen Dank für deinen Beitrag. Ich habe ihn erfolgreich für die TrackR Pixel verwendet.
Die Tags liegen jetzt etwa 50 cm neben dem Raspberry und im Laufe des Tages Wechsel sie öfters zwischen anwesend und anwesend hin und her. Denke das die Verbindung nicht immer steht. Kann man irgendwo eine kleine Schleife einbauen dass quasi der State erst geändert wird sobald Status zweimal absent ist und umgekehrt.
Oder siehst du da eine andere Möglichkeit?
Viele Grüße
Sascha
Hallo Sascha,
ja, in der Tat, das Problem kommt hin und wieder mal vor, dass sich Raspberry und ein Tag nicht verstehen, obwohl die Verbindung optimal sein müsste.
Ich habe mir hier mit einem Job beholfen. Jede Minute läuft dieser FHEM Job und prüft, ob sich vor 180 sec = 3 min etwas geändert hat. Durch entsprechende Anpassung kann man das nach Bedarf fine tunen und ausprobieren, ob man hierdurch eine Glättung der wilden Schalterei bekommt. Bei mir klappt das mit dem Code ganz gut.
define Job_Anwesenheit_Martin_check at +*00:01 { \
if(Value("Martin_BT_Tag") eq "present" ) eq "present") { \
if(Value("Anwesenheit_Martin") ne "present") { \
if( time() - Value("Anwesenheit_Martin_time_cmp") > 180) { \
fhem("set Anwesenheit_Martin present");; \
fhem("set Anwesenheit_Martin_time_cmp ".time());; \
fhem("set Anwesenheit_Martin_entry ".My_Jetzt());; \
} \
} \
else { \
fhem("set Anwesenheit_Martin present");; \
fhem("set Anwesenheit_Martin_time_cmp ".time());; \
} \
} \
if(Value("Martin_BT_Tag") eq "absent") ne "present") { \
if(Value("Anwesenheit_Martin") ne "absent") { \
if( time() - Value("Anwesenheit_Martin_time_cmp") > 180) { \
fhem("set Anwesenheit_Martin absent");; \
fhem("set Anwesenheit_Martin_time_cmp ".time());; \
fhem("set Anwesenheit_Martin_exit ".My_Jetzt());; \
} \
} \
else { \
fhem("set Anwesenheit_Martin absent");; \
fhem("set Anwesenheit_Martin_time_cmp ".time());; \
} \
} \
}
Vielen Dank für die schnelle Antwort. Top
Super Anleitung und Skripte! Danke 🙂
Leider habe ich grade noch ein Problem bei der Ausführung in FHEM. Ich musste die das .pm Skript noch anpassen, da ich Bananian Linux verwende:
Ich habe also:
@resultstrings = `sudo /opt/fhem/lescan.sh $le_device`;
durch:
@resultstrings = `/opt/fhem/lescan.sh $le_device`;
Soweit so gut nur leider bekomme ich in FHEM immer absent (0), obwohl ich bei manuellem ausführen in der Konsole ein present bekomme (1)
Hast du vll. eine Idee?
Hallo,
ich nehme an, Bananian Linux ist ein speziell an den Banana Pi angepasstes Linux-Derivat, allerdings weiß ich nicht, ob der User fhem, der den lescan durchführt auch die Rechte hat, hcitool auszuführen. Deswegen der sudo. Ich vermute, dass das Skript deswegen scheitert und somit 0 zurückgibt, weil das der Default-Wert ist. auszuführen. Klappt das? Wenn nicht, dann muss man erst dem User die Rechte geben, das hcitool ausführen zu dürfen. Das mache ich im Raspian Linux mit Eintrag in die /etc/sudoers.
Probiere doch mal, dich manuell als User fhem anzumelden (su fhem – ) und dann dort /opt/fhem/lescan.sh
Vielleicht kommst Du mit dem Hinweis etwas weiter.
Gruß
Martin
Ich habe nun Mal sudo nachinstalliert und konfiguriert und nun gehts 🙂 kurioserweise ging es auf der Konsole mit su fhem auch ohne Probleme 😀
Hat mir auf jeden Fall weiter geholfen.
Vielen Dank! 🙂