############################################## # $Id$ package main; # Documentation: AHA-HTTP-Interface.pdf, AVM_Technical_Note_-_Session_ID.pdf use strict; use warnings; use Time::HiRes qw(gettimeofday); use FritzBoxUtils; sub FBAHAHTTP_Initialize($) { my ($hash) = @_; $hash->{WriteFn} = "FBAHAHTTP_Write"; $hash->{DefFn} = "FBAHAHTTP_Define"; $hash->{SetFn} = "FBAHAHTTP_Set"; $hash->{GetFn} = "FBAHAHTTP_Get"; $hash->{AttrFn} = "FBAHAHTTP_Attr"; $hash->{ReadyFn} = "FBAHAHTTP_Ready"; $hash->{RenameFn} = "FBAHAHTTP_RenameFn"; $hash->{DeleteFn} = "FBAHAHTTP_Delete"; $hash->{AttrList} = "dummy:1,0 fritzbox-user polltime async_delay ". "disable:0,1 disabledForIntervals fbTimeout"; } ##################################### sub FBAHAHTTP_Define($$) { my ($hash, $def) = @_; my @a = split("[ \t][ \t]*", $def); return "wrong syntax: define FBAHAHTTP hostname" if(@a != 3); $hash->{Clients} = ":FBDECT:"; my %matchList = ( "1:FBDECT" => ".*" ); $hash->{MatchList} = \%matchList; # Moving definition from FBAHA to FBAHAHTTP for my $d (devspec2array("TYPE=FBDECT")) { if($defs{$d}{IODev} && $defs{$d}{IODev}{TYPE} eq "FBAHA") { my $n = $defs{$d}{IODev}{NAME}; CommandAttr(undef, "$d IODev $hash->{NAME}"); CommandDelete(undef, $n) if($defs{$n}); $defs{$d}{IODev} = $hash; my $oldNr = $defs{$d}{IODev}{NR}; # Forum #92286 $hash->{NR} = $oldNr if($hash->{NR} > $oldNr); } } $hash->{CmdStack} = (); return undef if($hash->{DEF} eq "none"); # DEBUGGING InternalTimer(1, "FBAHAHTTP_Poll", $hash); $hash->{STATE} = "defined"; return undef; } ##################################### sub FBAHAHTTP_Delete($) { my ($hash) = @_; my $name = $hash->{NAME}; my ($err, $fb_pw) = setKeyValue("FBAHAHTTP_PASSWORD_$name", undef); return $err; } sub FBAHAHTTP_connect($$) { my ($hash, $doProcess) = @_; my $name = $hash->{NAME}; my $dev = $hash->{DEF}; my $dr = sub { $hash->{STATE} = $_[0]; Log 2, $hash->{STATE}; $hash->{CmdStack} = (); return $hash->{STATE}; }; my $fb_user = AttrVal($name, "fritzbox-user", ''); return $dr->("MISSING: attr $name fritzbox-user") if(!$fb_user); my ($err, $fb_pw) = getKeyValue("FBAHAHTTP_PASSWORD_$name"); return $dr->("ERROR: $err") if($err); return $dr->("MISSING: set $name password") if(!$fb_pw); my $sid = FB_doCheckPW($hash->{DEF}, $fb_user, $fb_pw); if(!$sid) { $hash->{NEXT_OPEN} = time()+60; $readyfnlist{"$name.$dev"} = $hash; return $dr->("$name error: cannot get SID, ". "check connection/hostname/fritzbox-user/password") } delete($hash->{RetriedCmd}); delete($readyfnlist{"$name.$dev"}); $hash->{".SID"} = $sid; $hash->{STATE} = "connected"; Log3 $name, 4, "FBAHAHTTP_connect $name: got SID $sid"; FBAHAHTTP_ProcessStack($hash) if($doProcess && $hash->{CmdStack} && int(@{$hash->{CmdStack}})); return undef; } sub FBAHAHTTP_RenameFn($$) { my ($new, $old) = @_; for my $d (devspec2array("TYPE=FBDECT")) { my $hash = $defs{$d}; next if(!$hash); $hash->{DEF} =~ s/^$old:/$new:/; $attr{$d}{IODev} = $new if(AttrVal($d,"IODev","") eq $old); } FBDECT_renameIoDev($new, $old); } ##################################### sub FBAHAHTTP_Poll($) { my ($hash) = @_; my $name = $hash->{NAME}; return if(IsDisabled($name)); if(!$hash->{".SID"}) { my $ret = FBAHAHTTP_connect($hash, 0); return $ret if($ret); } my $sid = $hash->{".SID"}; my $host = ($hash->{DEF} =~ m/^http/i ? $hash->{DEF} : "http://$hash->{DEF}"); HttpUtils_NonblockingGet({ url=>"$host/webservices/homeautoswitch.lua?sid=$sid". "&switchcmd=getdevicelistinfos", loglevel => AttrVal($name, "verbose", 4), timeout => AttrVal($name, "fbTimeout", 4), callback => sub { if($_[1]) { Log3 $name, 3, "$name: $_[1]"; delete $hash->{".SID"}; return; } Log 5, $_[2] if(AttrVal($name, "verbose", 1) >= 5); if($_[2] !~ m,^$,s) { Log3 $name, 3, "$name: unexpected reply from device: $_[2]"; delete $hash->{".SID"}; return; } $_[2] =~ s+<(device|group) (.*?)+ Dispatch($hash, "<$1 $2", undef);""+gse; # Quick&Hack } }); my $polltime = AttrVal($name, "polltime", 300); RemoveInternalTimer($hash); InternalTimer(gettimeofday()+$polltime, "FBAHAHTTP_Poll", $hash); return; } ##################################### sub FBAHAHTTP_Ready($) { my ($hash) = @_; return if($hash->{NEXT_OPEN} && time() < $hash->{NEXT_OPEN}); FBAHAHTTP_Poll($hash); } ##################################### sub FBAHAHTTP_Attr($@) { my ($type, $devName, $attrName, @param) = @_; my $hash = $defs{$devName}; if($attrName eq "fritzbox-user") { return "Cannot delete fritzbox-user" if($type eq "del"); if($init_done) { delete($hash->{".SID"}); InternalTimer(1, sub { FBAHAHTTP_Poll($hash); }, 0); } } return undef; } ##################################### sub FBAHAHTTP_Set($@) { my ($hash, @a) = @_; my $name = shift @a; my %sets = (password=>2, refreshstate=>1,template=>2); return "set $name needs at least one parameter" if(@a < 1); my $type = shift @a; my $tl = ReadingsVal($name,"templateList",""); my $cmdList = "refreshstate:noArg password".($tl ? " template:$tl" : ""); return "Unknown argument $type, choose one of $cmdList" if(!defined($sets{$type})); return "Missing argument for $type" if(int(@a) < $sets{$type}-1); if($type eq "password") { setKeyValue("FBAHAHTTP_PASSWORD_$name", $a[0]); delete($hash->{".SID"}); FBAHAHTTP_Poll($hash); } elsif($type eq "refreshstate") { FBAHAHTTP_Poll($hash); } elsif($type eq "template") { my $cl = $hash->{CL}; my $doRet = sub($) { if($cl) { asyncOutput($cl, $_[0]); } else { Log3 $hash, 4, "$_"; } }; FBAHAHTTP_GetTemplateList($hash, sub($$){ my ($err, $r) = @_; return $doRet->($err) if($err); return $doRet->("Unknown template $a[0]") if(!defined($r->{$a[0]})); FBAHAHTTP_Write($hash, $r->{$a[0]}, "applytemplate"); }); } return undef; } sub FBAHAHTTP_GetTemplateList($$) { my ($hash, $callbackFn) = @_; my $host = ($hash->{DEF} =~ m/^http/i ? $hash->{DEF}:"http://$hash->{DEF}"); my $sid = $hash->{".SID"}; my $name = $hash->{NAME}; return "No SID found" if(!$sid); HttpUtils_NonblockingGet({ url=>"$host/webservices/homeautoswitch.lua?". "sid=$sid&switchcmd=gettemplatelistinfos", loglevel => AttrVal($name, "verbose", 4), timeout => AttrVal($name, "fbTimeout", 4), callback => sub { if($_[1]) { delete $hash->{".SID"}; return $callbackFn->("$name: $_[1]"); } my $ret = (defined($_[2]) ? $_[2] : "") ; my %r; $ret =~ s: