Anwesenheitserkennung mit WLAN über eine Fritzbox
Hiermit soll beschrieben werden, wie mit einem Linux-Server (raspberry pi oder ähnlich), einer Fritzbox und einem iPhone oder Android-Handy die Anwesenheit eines oder mehrere Bewohner geprüft werden kann um verschiedene Stadien im Haus zu aktivieren.
Und vor allem kommt diese Lösung ohne Zugriffe auf Cloud-Dienste aus.
Im Forum wurde angekündigt, dass es Probleme geben könnte mit iPhone-Geräten.
Ich habe nun seit dem 09. Januar 2016 die Anwesenheitsdaten immer mitgeschrieben um nachzuvollziehen, wann das Gerät aus der Fritzbox verschwindet.
Es gab nur einmal in der Nacht einen Aussetzer von ca. 1 1/2 Std. Das ist aber aus meiner Sicht vernachlässigbar, da
Die Nachtabsenkung aktiv war
Und noch zwei weitere Handys für die Fritzbox sichtbar waren
Aktualisierung am 17.02.2016:
Ich habe durch einen Tipp aus dem Forum (direktes schreiben des Status auf dem Server) die Datei angepasst.
Was mir jedoch nicht gefiel, dass die Daten (u.a. das Loxone-Kennwort) unverschlüsselt durch das heimische Netz geschickt werden. Das soll mit der neuen Methode meines Erachtens besser sein. Denn der Miniserver empfängt nur noch einen Status den der raspberry sendet.
Als Inspiration diente mir folgender Forumeintrag:
Anwesenheit per WLAN feststellen
Dort habe ich die Grunddatei vom User Dott verwendet.
Das Perlskript zur Fritzbox-Abfrage kommt von Bock-Systems. Bitte auch von dort bitte die notwendigen Einstellungen für die Fritzbox nachschauen.
Benötigte Hard- und Software
Linux-Server (raspberry pi ist ausreichend)
Fritzbox
iPhone oder Android mit aktiviertem WLAN
Fritzbox MAC-Adressen
Es gilt über die Fritzbox die MAC-Adressen der betroffenen Mobilfunkgeräte herauszufinden.
Die Übersicht für alle Netzwerkgeräte befindet sich bei FRITZ!OS 6.50 unter folgendem Pfad, wobei Android meistens mit "android-" beginnen und einer alphanumerischen Kombination enden.
Bei aktuell eingebuchten Geräten
Heimnetz -> Heimnetzübersicht -> Details beim jeweiligen Netzgerät
Bei vergangenen eingebuchten Geräten
Heimnetz -> Heimnetzübersicht -> Netzwerkverbindungen -> Details beim jeweiligen Netzgerät
Als Beispiel haben wir nun die MAC-Adressen erhalten
11:22:33:44:55:66 und 22:33:44:55:66:77, wobei eins davon ein iPhone und das andere ein Android-Handy ist.
Installieren der Perl-Umgebung auf dem Linux-Server
Installieren der Perl-Umgebung auf dem Linux-Server
sudo apt-get install libxml-simple-perl libio-all-lwp-perl libgetopt-long-descriptive-perl
Einrichten des Perl-Skriptes
Anschließend folgendes Perl-Skript in einem beliebigen Verzeichnis unter etwa folgendem Namen speichern, /home/pi/fritzbox-bewohner1.pl.
Funktionsweise des Skripts
Das Perl-Skript fragt die Fritzbox die MAC-Adressen ab und sendet per UDP "xxx-1" für online und "xxx"-0 für offline.
Die Information kann dann wie auf der Seite UDP Eingang definieren ausgewertet werden.
Perl-Skript zur Fritzbox-Abfrage
#!/usr/bin/perl -w
use strict;
use warnings;
use LWP::Simple;
use LWP::UserAgent;
use XML::Simple;
use Getopt::Long;
use open qw(:std :utf8);
use HTTP::Request::Common;
use IO::Socket::INET;
# ------------- Konfiguration -------------
my $ip = "192.168.xxx.xxx"; # IP Fritzbox
my $port = "49443"; # Port Fritzbox
my @macs_to_check = ("AA:BB:CC:DD:EE:FF"); # MAC Adresse der Geraete, es können mehrere eingetragen werden
my $LOXIP = "192.168.xxx.xxx"; # IP Miniserver
my $udpport = "34435"; # UDP-Port
my $online = "xxx-1"; # Person Online
my $offline = "xxx-0"; # Person Offline
# ---------------- Programm ----------------
$ENV{'PERL_LWP_SSL_VERIFY_HOSTNAME'} = 0;
$ENV{HTTPS_DEBUG} = 1;
my $ua = new LWP::UserAgent;
$ua->default_headers;
$ua->ssl_opts( verify_hostname => 0 ,SSL_verify_mode => 0x00);
my $resp_discover = $ua->get("https://$ip:$port/tr64desc.xml");
my $xml_discover;
if ( $resp_discover->is_success ) {
$xml_discover = $resp_discover->decoded_content;
}
else {
die $resp_discover->status_line;
}
my $discover = XMLin($xml_discover);
print "$discover->{device}->{modelName} gefunden...\n";
my $control_url = "not set";
my $service_type = "not set";
my $service_command = "GetSpecificHostEntry";
foreach(@{$discover->{device}->{deviceList}->{device}->[0]->{serviceList}->{service}}){
if("urn:LanDeviceHosts-com:serviceId:Hosts1" =~ m/.*$_->{serviceId}.*/){
$control_url = $_->{controlURL};
$service_type = $_->{serviceType};
}
}
if ($control_url eq "not set" or $service_type eq "not set"){
die "control URL/service type not found. Cannot request host info!";
}
$ua->default_header( 'SOAPACTION' => "$service_type#$service_command" );
my $xml_mac_resp;
my $any_online = 0;
foreach my $mac (@macs_to_check){
my $init_request = <<EOD;
<?xml version="1.0" encoding="utf-8"?>
<s:Envelope s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" >
<s:Header>
</s:Header>
<s:Body>
<u:$service_command xmlns:u="$service_type">
<NewMACAddress>$mac</NewMACAddress>
</u:$service_command>
</s:Body>
</s:Envelope>
EOD
my $init_url = "https://$ip:$port$control_url";
my $resp_init = $ua->post($init_url, Content_Type => 'text/xml; charset=utf-8', Content => $init_request);
$xml_mac_resp = XMLin($resp_init->decoded_content);
if(exists $xml_mac_resp->{'s:Body'}->{'s:Fault'}){
if($xml_mac_resp->{'s:Body'}->{'s:Fault'}->{detail}->{UPnPError}->{errorCode} eq "714"){
print "MAC $mac nicht in der FritzBox gefunden!\n";
}
}
if(exists $xml_mac_resp->{'s:Body'}->{'u:GetSpecificHostEntryResponse'}){
if($xml_mac_resp->{'s:Body'}->{'u:GetSpecificHostEntryResponse'}->{NewActive} eq "1"){
my $name = $xml_mac_resp->{'s:Body'}->{'u:GetSpecificHostEntryResponse'}->{NewHostName};
my $ip = $xml_mac_resp->{'s:Body'}->{'u:GetSpecificHostEntryResponse'}->{NewIPAddress};
my $iftype = $xml_mac_resp->{'s:Body'}->{'u:GetSpecificHostEntryResponse'}->{NewInterfaceType};
print "MAC $mac ($name) ist online mit der IP $ip mit $iftype\n";
$any_online = 1;
}
if($xml_mac_resp->{'s:Body'}->{'u:GetSpecificHostEntryResponse'}->{NewActive} eq "0"){
my $name = $xml_mac_resp->{'s:Body'}->{'u:GetSpecificHostEntryResponse'}->{NewHostName};
print "MAC $mac ($name) ist offline\n";
}
}
}
if ($any_online){
my $sock = new IO::Socket::INET(PeerAddr => $LOXIP,
PeerPort => $udpport,
Proto => 'udp', Timeout => 1) or die('Error opening socket.');
print $sock $online;
exit 1;
}
else{
my $sock = new IO::Socket::INET(PeerAddr => $LOXIP,
PeerPort => $udpport,
Proto => 'udp', Timeout => 1) or die('Error opening socket.');
print $sock $offline;
exit 0;
}
Trennung des Skriptes
Um eine getrennte Abfrage für jeden Bewohner zu machen sind mehrere Skripte erforderlich. Also fritzbox-Bewohner1.pl, fritzbox-Bewohner2.pl, usw.
Automatisierung der Abfrage
Um das ganze automatisiert abfragen zu können ist wieder ein cronjob und die Ausführberechtigung für das Skript notwendig.
chmod +x /home/pi/fritzbox-Bewohner1.pl
cronjob anlegen
Folgende Zeilen am Ende einfügen:
Nun fragt der raspberry pi jede Minute den Status der anwesenden WLAN-Geräte bei der Fritzbox ab.
Den Empfang der UDP - Telegramme und die Verwertung im Miniserver ist unter UDP Eingang definieren beschrieben.