# $Id$ package main; use strict; use warnings; use Blocking; my $speedtest_hasJSON = 1; sub speedtest_Initialize($) { my ($hash) = @_; $hash->{DefFn} = "speedtest_Define"; $hash->{UndefFn} = "speedtest_Undefine"; $hash->{SetFn} = "speedtest_Set"; $hash->{AttrList} = "checks-till-disable ". "disable:0,1 ". "ookla:1,0 ". "path ". $readingFnAttributes; eval "use JSON"; $speedtest_hasJSON = 0 if($@); } ##################################### sub speedtest_Define($$) { my ($hash, $def) = @_; my @a = split("[ \t][ \t]*", $def); return "Usage: define speedtest [interval [server]]" if(@a < 2); my $name = $a[0]; my $interval = 60*60; $interval = $a[2] if(int(@a)>=3); $interval = 30*60 if( $interval < 30*60 ); my $server; $server = $a[3] if(int(@a)>=4); delete( $hash->{SERVER} ); $hash->{NAME} = $name; $hash->{STATE} = "Initialized"; $hash->{INTERVAL} = $interval; $hash->{SERVER} = $server if( defined( $server ) ); RemoveInternalTimer($hash); InternalTimer(gettimeofday()+$hash->{INTERVAL}, "speedtest_GetUpdate", $hash, 0); return undef; } sub speedtest_Undefine($$) { my ($hash, $arg) = @_; RemoveInternalTimer($hash); BlockingKill($hash->{helper}{RUNNING_PID}) if(defined($hash->{helper}{RUNNING_PID})); return undef; } sub speedtest_Set($$@) { my ($hash, $name, $cmd) = @_; if($cmd eq 'statusRequest') { $hash->{LOCAL} = 1; speedtest_GetUpdate($hash); $hash->{LOCAL} = 0; return undef; } my $list = "statusRequest:noArg"; return "Unknown argument $cmd, choose one of $list"; } sub speedtest_GetUpdate($) { my ($hash) = @_; my $name = $hash->{NAME}; if(!$hash->{LOCAL}) { RemoveInternalTimer($hash); InternalTimer(gettimeofday()+$hash->{INTERVAL}, "speedtest_GetUpdate", $hash, 0); } my $server =""; $server = $hash->{SERVER} if( defined($hash->{SERVER}) ); if( !$hash->{LOCAL} ) { return undef if( AttrVal($name, "disable", 0 ) == 1 ); my $checks = AttrVal($name, "checks-till-disable", undef ); if( defined($checks) ) { $checks -= 1; $attr{$name}{"checks-till-disable"} = max(0,$checks); $attr{$name}{"disable"} = 1 if( $checks <= 0 ); } } readingsSingleUpdate($hash,"state", "running", 1); $hash->{helper}{RUNNING_PID} = BlockingCall("speedtest_DoSpeedtest", $name."|".$server, "speedtest_SpeedtestDone", 300, "speedtest_SpeedtestAborted", $hash) unless(exists($hash->{helper}{RUNNING_PID})); } sub speedtest_DoSpeedtest($) { my ($string) = @_; my ($name, $server) = split("\\|", $string); my $ookla = AttrVal($name, "ookla", undef); my $cmd; if( $ookla ) { $cmd = AttrVal($name, "path", "/usr/local/bin" ); $cmd .= "/speedtest --accept-license --accept-gdpr -f json"; $cmd .= " -s $server" if( $server ); } else { $cmd = AttrVal($name, "path", "/usr/local/speedtest-cli" ); $cmd .= "/speedtest-cli --simple"; $cmd .= " --server $server" if( $server ); } Log3 $name, 5, "starting speedtest"; my $speedstr = qx($cmd); Log3 $name, 5, "speedtest done"; if( $ookla ) { $speedstr =~ s/\n//g; return "$name|$speedstr"; } my @speedarr = split(/\n/, $speedstr); for( my $i = 0; $i < 3; ++$i ) { $speedarr[$i] = $1 if( $speedarr[$i] && $speedarr[$i] =~ m/^\w+: (.*)/i ); } return "$name|$speedarr[0]|$speedarr[1]|$speedarr[2]"; } sub speedtest_SpeedtestDone($) { my ($string) = @_; return unless(defined($string)); my @a = split("\\|",$string); my $name = $a[0]; my $hash = $defs{$name}; delete($hash->{helper}{RUNNING_PID}); return if($hash->{helper}{DISABLED}); Log3 $hash, 5, "speedtest_SpeedtestDone: $string"; if( $a[1] =~ m/^\{.*\}$/ ) { if( !$speedtest_hasJSON ) { Log3 $name, 1, "json needed for ookla speedtest"; return; } my $decoded = eval { decode_json($a[1]) }; readingsBeginUpdate($hash); if( $decoded->{message} ) { readingsBulkUpdate($hash,"state", "failed" ); readingsBulkUpdate($hash,"lastError", $decoded->{message} ); } else { readingsBulkUpdate($hash,"ping",$decoded->{ping}{latency}) if( defined( $decoded->{ping}) ); readingsBulkUpdate($hash,"upload",round($decoded->{upload}{bandwidth}*8/1024/1024,1)) if( defined( $decoded->{upload}) ); readingsBulkUpdate($hash,"download",round($decoded->{download}{bandwidth}*8/1024/1024,1)) if( defined( $decoded->{download}) ); readingsBulkUpdate($hash,"packetLoss",$decoded->{packetLoss}) if( defined( $decoded->{packetLoss}) ); readingsBulkUpdate($hash,"id",$decoded->{server}{id}) if( defined( $decoded->{server}) ); readingsBulkUpdate($hash,"name",$decoded->{server}{name}) if( defined( $decoded->{server}) ); readingsBulkUpdate($hash,"location",$decoded->{server}{location}) if( defined( $decoded->{server}) ); readingsBulkUpdate($hash,"state","ok"); } readingsEndUpdate($hash,1); return; } if( $a[1] eq "Invalid server ID" ) { readingsSingleUpdate($hash,"state", "failed", 1); return; } $a[1] =~ s/\s.*// if( defined($a[1]) ); $a[2] =~ s/\s.*// if( defined($a[2]) ); $a[3] =~ s/\s.*// if( defined($a[3]) ); readingsBeginUpdate($hash); readingsBulkUpdate($hash,"ping",$a[1]); readingsBulkUpdate($hash,"download",$a[2]); readingsBulkUpdate($hash,"upload",$a[3]); readingsBulkUpdate($hash,"state",defined($a[3])?"ok":"failed"); readingsEndUpdate($hash,1); } sub speedtest_SpeedtestAborted($) { my ($hash) = @_; delete($hash->{helper}{RUNNING_PID}); } 1; =pod =item device =item summary internet speedtest data =item summary_DE Internet Speedtest überwachung =begin html

speedtest

=end html =cut