Anwesenheitserkennung über WLAN mit einer Fritzbox und Perl
Diese Anleitung beschreibt, wie man mit einer Fritz!Box 7490 eine Smartphone-Anwesenheitserkennung via WLAN-Erkennung bauen kann.
Voraussetzung
Die Voraussetzung zur Nutzung des Scripts ist eine Server mit einer laufenden Perl-Installation und einem Cron-Daemon.
Schritt-für-Schritt-Anleitung
Zuerst wird das Perl Script fritzbox-bewohner.pl mit folgendem Inhalt erstellt:
fritzbox-bewohner.pl
#!/usr/bin/perl -w ##################################################### # Script zur Anwesenheitserkennung von WLAN-Geräten # # an einer Fritz!Box 7490 in Verbindung mit einem # # Loxone Miniserver # # Version: 2016.02.27.15.09.14 # ##################################################### # Miniserver UDP Information my $mshost = 'miniserver'; # Miniserver-IP oder Hostname my $msport = 7007; # Miniserver UDP-Empfangsport my $msprotocol = 'udp'; # Fritz!Box Information my $ip = 'fritz.box'; # Fritz!Box-IP oder Hostname my $port = 49443; # Fritz!Box Port # Flushing to STDOUT after each write $| = 1; use strict; use warnings; use LWP::Simple; use LWP::UserAgent; use XML::Simple; use Getopt::Long; use open qw(:std :utf8); use POSIX qw/ strftime /; use IO::Socket; # Check Command Line parameters # Minumum 2 - Resident and at least one Phone-MAC address my $num_args = $#ARGV + 1; if ($num_args < 2) { print "Missing command line argument!"; print "\nUsage: fritzbox-bewohner.pl <BewohnerN> <Handy-MAC1> [<Handy-MACn>]"; print "\nExample: fritzbox-bewohner.pl Bewohner1 00:00:00:00:00:00\n"; exit; } # Alle MACs sammeln und im Array macs_to_check speichern my @macs_to_check =($ARGV[1]); my $bewohner = $ARGV[0]; print "Checking presence for User ".$bewohner." with MAC ".$ARGV[1]; for (my $mac=1; $mac <= $num_args-2; $mac++) { push @macs_to_check, $ARGV[$mac+1]; print " or MAC ".$ARGV[($mac+1)]; } print " at " . $ip ." on port " . $port . "...\n"; # disable SSL checks. No signed certificate! $ENV{'PERL_LWP_SSL_VERIFY_HOSTNAME'} = 0; $ENV{HTTPS_DEBUG} = 1; # Discover Service Parameters my $ua = new LWP::UserAgent; $ua->default_headers; $ua->ssl_opts( verify_hostname => 0 ,SSL_verify_mode => 0x00); # Read all available services 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} detected...\n"; # Parse XML service response, get needed parameters for LAN host service my $control_url = "not set"; my $service_type = "not set"; my $service_command = "GetSpecificHostEntry"; # fixed command for requesting info of specific MAC 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!"; } # Prepare request for query LAN host $ua->default_header( 'SOAPACTION' => "$service_type#$service_command" ); my $xml_mac_resp; my $any_online = 0; # if arg any specified 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 not found in FritzBox Database!\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) is online with IP $ip on $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) is offline\n"; } } } # Creating UDP socket to Miniserver my $client = IO::Socket::INET->new ( PeerAddr => $mshost, PeerPort => $msport, Proto => $msprotocol, Type => SOCK_DGRAM ) or die "Socket could not be created, failed with error: $!\n"; print "Sending Data '".$bewohner.":".$any_online."' to $mshost on port $msport ...\n"; $client->send($bewohner.":".$any_online); $client->close();
Danach wird die Konfiguration dieses Scripts angepasst. Es ist die Adresse der Fritzbox und des Miniservers anzupassen. Auf Wunsch auch der UDP-Port des Miniservers welcher die Info empfängt.
Nun wird ein Eintrag in der Crontab des Servers angelegt. Ich rufe das Script jede Minute auf. Die Fritz!Box liefert die Daten sowieso bis zu 15 Minuten verzögert aus.
crontab
* * * * * /usr/bin/perl /home/pfad/zum/script/fritzbox-bewohner.pl Bewohner1 54:4E:90:11:22:33 >/dev/null 2>&1 * * * * * /usr/bin/perl /home/pfad/zum/script/fritzbox-bewohner.pl Bewohner2 14:A3:64:44:55:66 >/dev/null 2>&1
In der Loxone Config muss nun ein Virtueller UDP Eingang ersellt werden:
Die Senderadresse ist optional aber beim UDP Empfangsport muss die Port-Nummer vom $msport eingetragen werden. Hier im Beispiel ist es die 7007.Ausserdem wird nun für jeden Bewohner in der In der Loxone Config ein Virtueller UDP Eingang Befehl angelegt.
Als Befehlserkennung ist\iBewohner1:\i\v
zu verwenden wobei Bewohner1 für jeden Bewohner anzupassen ist und dem ersten Parameter des Scripts in der Crontab entspricht.Ich gehe dann mit den Daten durch einen Status-Baustein auf einen Tracker.
Dank an @jdlwguard-loxone und svethi welche mich motiviert haben, das umzusetzen und von wo ich Teile des Codes übernommen habe.
Erfolgreich getestet mit einer Fritz!Box 7490 mit Fritz!OS 06.51 an einem Miniserver Version 7.3.2.24 und Iphone 6 sowie Samsung S3 Neo.