From 5551c6730544a2879ce6788a2bed048c0abedc78 Mon Sep 17 00:00:00 2001
From: Ellert <>
Date: Fri, 16 Jun 2023 22:04:30 +0000
Subject: [PATCH] 74_AutomowerConnect: Common.pm, automowerconnect.js, handle
userattr for API timeouts and logleveldevio, measure response time, prevent
some warnings, update map via FW extension, reduce side effects while maually
update mower data via polling
git-svn-id: https://svn.fhem.de/fhem/trunk@27684 2b470e98-0d58-463d-a4d8-8e2adae1ed80
---
fhem/CHANGED | 4 +
fhem/FHEM/74_AutomowerConnect.pm | 60 +++++-
fhem/lib/FHEM/Devices/AMConnect/Common.pm | 216 ++++++++++++++++------
fhem/www/pgm2/automowerconnect.js | 23 ++-
4 files changed, 240 insertions(+), 63 deletions(-)
diff --git a/fhem/CHANGED b/fhem/CHANGED
index 077398575..a35ed2bae 100644
--- a/fhem/CHANGED
+++ b/fhem/CHANGED
@@ -1,5 +1,9 @@
# Add changes at the top of the list. Keep it in ASCII, and 80-char wide.
# Do not insert empty lines here, update check depends on it.
+ - change: 74_AutomowerConnect: Common.pm, automowerconnect.js
+ handle userattr for API timeouts and logleveldevio, measure
+ response time, prevent some warnings, update map via FW extension,
+ reduce side effects while maually update mower data via polling
- feature: 72_FRITZBOX: Attribut: disableHostIPv4check
- bugfix: 72_FRITZBOX: Parameter Fehlerbehandlung: get fritzLog
- change: 10_KNX: moved KNX_scan function to KNXIO-module, see Forum #122582
diff --git a/fhem/FHEM/74_AutomowerConnect.pm b/fhem/FHEM/74_AutomowerConnect.pm
index e7438346f..d3a03b8ba 100644
--- a/fhem/FHEM/74_AutomowerConnect.pm
+++ b/fhem/FHEM/74_AutomowerConnect.pm
@@ -378,11 +378,39 @@ __END__
disable
+
disabledForIntervals
+
+
+ userattr
+
+ The following user attributes are taken into account.
+
+ - loglevelDevIo
+ attr <name> loglevelDevIo <[012345]>
+ Set internal deviologlevel, DevIo: Wichtige_Internals_zur_Konfiguration
+
+ - timeoutGetMower
+ attr <name> timeoutGetMower <[6 to 60]>
+ Set timeout for API call, default 5 s.
+
+ - ApiAuth
+ attr <name> timeoutApiAuth <[6 to 60]>
+ Set timeout for API call, default 5 s.
+
+ - timeoutCMD
+ attr <name> timeoutCMD <[6 to 60]>
+ Set timeout for API call, default 5 s.
+ The response time is meassured and logged if a timeout ist set to 60 s.
+
+
+
+
+
Readings
@@ -407,7 +435,7 @@ __END__
- settings_cuttingHeight - actual cutting height from API
- settings_headlight - actual headlight mode from API
- statistics_newGeoDataSets - number of new data sets between the last two different time stamps
- - statistics_numberOfCollisions - Number of collisions (last day/all days)
+ - statistics_numberOfCollisions - Number of collisions (current day/last day/all days)
- status_connected - state of connetion between mower and Husqvarna Cloud.
- status_statusTimestamp - local time of last status update
- status_statusTimestampDiff - time difference in seconds between the last and second last status update
@@ -708,11 +736,39 @@ __END__
- disable
+
- disabledForIntervals
+
+
+ userattr
+
+ Die folgenden Benutzerattribute werden unterstützt.
+
+ - loglevelDevIo
+ attr <name> loglevelDevIo <[012345]>
+ Setzt das Internal deviologlevel, DevIo: Wichtige_Internals_zur_Konfiguration
+
+ - timeoutGetMower
+ attr <name> timeoutGetMower <[6 to 60]>
+ Setzt den Timeout für das Lesen der Mäherdaten, default 5 s.
+
+ - timeoutApiAuth
+ attr <name> timeoutApiAuth <[6 to 60]>
+ Setzt den Timeout für die Authentifikation, default 5 s.
+
+ - timeoutCMD
+ attr <name> timeoutCMD <[6 to 60]>
+ Setzt den Timeout für Befehl senden, default 15 s.
+ Wird ein Timeout auf 60 s gesetzt, wird die Antwortzeit gemessen und geloggt.
+
+
+
+
+
Readings
@@ -737,7 +793,7 @@ __END__
- settings_cuttingHeight - aktuelle Schnitthöhe aus der API
- settings_headlight - aktueller Scheinwerfermode aus der API
- statistics_newGeoDataSets - Anzahl der neuen Datensätze zwischen den letzten zwei unterschiedlichen Zeitstempeln
- - statistics_numberOfCollisions - Anzahl der Kollisionen (letzter Tag/alle Tage)
+ - statistics_numberOfCollisions - Anzahl der Kollisionen (laufender Tag/letzter Tag/alle Tage)
- status_connected - Status der Verbindung zwischen dem Automower und der Husqvarna Cloud.
- status_statusTimestamp - Lokalzeit des letzten Statusupdates in der API
- status_statusTimestampDiff - Zeitdifferenz zwischen dem letzten und vorletzten Statusupdate.
diff --git a/fhem/lib/FHEM/Devices/AMConnect/Common.pm b/fhem/lib/FHEM/Devices/AMConnect/Common.pm
index bdbdc1b6b..4e8bb5565 100644
--- a/fhem/lib/FHEM/Devices/AMConnect/Common.pm
+++ b/fhem/lib/FHEM/Devices/AMConnect/Common.pm
@@ -47,6 +47,7 @@ BEGIN {
CommandAttr
CommandDeleteReading
FmtDateTime
+ FW_ME
getKeyValue
InternalTimer
InternalVal
@@ -174,10 +175,14 @@ my $mapZonesTpl = '{
interval_ping => 60,
retry_interval_apiauth => 840,
retry_interval_getmower => 840,
+ timeout_apiauth => 5,
+ timeout_getmower => 5,
+ timeout_cmd => 15,
midnightCycle => 1,
client_id => $client_id,
grant_type => 'client_credentials',
mowerNumber => $mowerNumber,
+ detailFnFirst => 0,
scaleToMeterLongitude => 67425,
scaleToMeterLatitude => 108886,
minLon => 180,
@@ -187,7 +192,6 @@ my $mapZonesTpl = '{
imageHeight => 650,
imageWidthHeight => '350 650',
mapdesign => $mapAttr,
- detailFnFirst => 0,
mapZonesTpl => $mapZonesTpl,
posMinMax => "-180 90\n180 -90",
newdatasets => 0,
@@ -201,7 +205,8 @@ my $mapZonesTpl = '{
positions => [],
timestamp => 0,
errordesc => '-',
- errordate => ''
+ errordate => '',
+ errorstate => ''
},
UNKNOWN => {
short => 'U',
@@ -289,7 +294,7 @@ my $mapZonesTpl = '{
AddExtension( $name, \&GetMap, "$type/$name/map" );
- # AddExtension( $name, \&GetJson, "$type/$name/json" );
+ AddExtension( $name, \&GetJson, "$type/$name/json" );
if ( $::init_done ) {
@@ -339,7 +344,9 @@ sub Undefine {
my $type = $hash->{TYPE};
RemoveInternalTimer( $hash );
- RemoveExtension("$type/$name/map");
+ RemoveExtension( "$type/$name/map" );
+ RemoveExtension( "$type/$name/json" );
+
return undef;
}
@@ -363,8 +370,10 @@ sub Rename {
my $hash = $defs{$newname};
my $type = $hash->{TYPE};
- RemoveExtension("$type/$oldname/map");
+ RemoveExtension( "$type/$oldname/map" );
+ RemoveExtension( "$type/$oldname/json" );
AddExtension( $newname, \&GetMap, "$type/$newname/map" );
+ AddExtension( $newname, \&GetJson, "$type/$newname/json" );
if ( $type eq 'AutomowerConnect' ) {
@@ -436,7 +445,7 @@ sub FW_detailFn {
if ( $hash->{helper} && $hash->{helper}{mower} && $hash->{helper}{mower}{attributes} && $hash->{helper}{mower}{attributes}{positions} && @{$hash->{helper}{mower}{attributes}{positions}} > 0 ) {
- my $img = "./fhem/$type/$name/map";
+ my $img = "$FW_ME/$type/$name/map";
my $zoom=AttrVal( $name,"mapImageZoom", 0.7 );
my $backgroundcolor = AttrVal($name, 'mapBackgroundColor','');
my $bgstyle = $backgroundcolor ? " background-color:$backgroundcolor;" : '';
@@ -544,46 +553,96 @@ sub FW_detailFn_Update {
my $mapy = $latlo-$latru;
# MOWING PATH
- my $posxy = '';
+ my @posxy = ();
if ( @pos > 0 ) {
+ my $k = 0;
+ for ( my $i = 0; $i < @pos; $i++ ){
- $posxy = int( ( $lonlo-$pos[ 0 ]{longitude} ) * $picx / $mapx ).",".int( ( $latlo-$pos[ 0 ]{latitude} ) * $picy / $mapy ).",'".$pos[ 0 ]{act}."'";
-
- for ( my $i = 1; $i < @pos; $i++ ){
-
- $posxy .= ",".int( ( $lonlo - $pos[ $i ]{longitude} ) * $picx / $mapx ).",".int( ( $latlo - $pos[ $i ]{latitude} ) * $picy / $mapy ).",'".$pos[ $i ]{act}."'";
+ $posxy[ $k++ ] = int( ( $lonlo - $pos[ $i ]{longitude} ) * $picx / $mapx );
+ $posxy[ $k++ ] = int( ( $latlo - $pos[ $i ]{latitude} ) * $picy / $mapy );
+ $posxy[ $k++ ] = $pos[ $i ]{act};
}
}
+ # MOWING PATH OLD
+ # my $posxy = '';
+
+ # if ( @pos > 0 ) {
+
+ # $posxy = int( ( $lonlo-$pos[ 0 ]{longitude} ) * $picx / $mapx ).",".int( ( $latlo-$pos[ 0 ]{latitude} ) * $picy / $mapy ).",'".$pos[ 0 ]{act}."'";
+
+ # for ( my $i = 1; $i < @pos; $i++ ){
+
+ # $posxy .= ",".int( ( $lonlo - $pos[ $i ]{longitude} ) * $picx / $mapx ).",".int( ( $latlo - $pos[ $i ]{latitude} ) * $picy / $mapy ).",'".$pos[ $i ]{act}."'";
+
+ # }
+
+ # }
+
# ERROR MESSAGE
my $errdesc = $hash->{helper}{lasterror}{errordesc};
my $errdate = $hash->{helper}{lasterror}{errordate};
+ my $errstate = $hash->{helper}{lasterror}{errorstate};
# ERROR PATH
- my $poserrxy = int( ( $lonru-$lonlo ) / 2 * $picx / $mapx ).",".int( ( $latlo - $latru ) / 2 * $picy / $mapy );;
+
+ my @poserrxy = ( int( ( $lonru-$lonlo ) / 2 * $picx / $mapx ), int( ( $latlo - $latru ) / 2 * $picy / $mapy ) );
if ( @poserr > 0 ) {
+ my $k = 0;
+ for ( my $i = 0; $i < @poserr; $i++ ){
- $poserrxy = int( ( $lonlo - $poserr[ 0 ]{longitude} ) * $picx / $mapx ) . "," . int( ( $latlo - $poserr[ 0 ]{latitude} ) * $picy / $mapy );
+ $poserrxy[ $k++ ] = int( ( $lonlo - $poserr[ $i ]{longitude} ) * $picx / $mapx );
+ $poserrxy[ $k++ ] = int( ( $latlo - $poserr[ $i ]{latitude} ) * $picy / $mapy );
- for ( my $i = 1; $i < @poserr; $i++ ){
- $poserrxy .= ",".int( ( $lonlo - $poserr[ $i ]{longitude} ) * $picx / $mapx) . "," . int( ( $latlo - $poserr[ $i ]{latitude} ) * $picy / $mapy );
}
}
+ # ERROR PATH OLD
+ # my $poserrxy = int( ( $lonru-$lonlo ) / 2 * $picx / $mapx ).",".int( ( $latlo - $latru ) / 2 * $picy / $mapy );;
+
+ # if ( @poserr > 0 ) {
+
+ # $poserrxy = int( ( $lonlo - $poserr[ 0 ]{longitude} ) * $picx / $mapx ) . "," . int( ( $latlo - $poserr[ 0 ]{latitude} ) * $picy / $mapy );
+
+ # for ( my $i = 1; $i < @poserr; $i++ ){
+ # $poserrxy .= ",".int( ( $lonlo - $poserr[ $i ]{longitude} ) * $picx / $mapx) . "," . int( ( $latlo - $poserr[ $i ]{latitude} ) * $picy / $mapy );
+ # }
+
+ # }
+
# Log3 $name, 1, "AutomowerConnectUpdateDetail ( '$name', '$type', $detailFnFirst, $picx, $picy, $scalx, [ '$errdesc', '$errdate' ], [ $posxy ], [ $poserrxy ] )";
- my $detailFnFirst = $hash->{helper}{detailFnFirst};
+
+ # map {
+ # ::FW_directNotify("#FHEMWEB:$_", "AutomowerConnectUpdateDetail ( '$name', '$type', $detailFnFirst, $picx, $picy, $scalx, [ '$errdesc', '$errdate', '$errstate' ], [ $posxy ], [ $poserrxy ] )","");
+ # } devspec2array("TYPE=FHEMWEB");
+
+ # prepare hash for json map update
+ $hash->{helper}{mapupdate}{name} = $name;
+ $hash->{helper}{mapupdate}{type} = $type;
+ $hash->{helper}{mapupdate}{detailfnfirst} = $hash->{helper}{detailFnFirst};
+ $hash->{helper}{mapupdate}{lonlo} = $lonlo;
+ $hash->{helper}{mapupdate}{latlo} = $latlo;
+ $hash->{helper}{mapupdate}{mapx} = $mapx;
+ $hash->{helper}{mapupdate}{mapy} = $mapy;
+ $hash->{helper}{mapupdate}{picx} = $picx;
+ $hash->{helper}{mapupdate}{picy} = $picy;
+ $hash->{helper}{mapupdate}{scalx} = $scalx;
+ $hash->{helper}{mapupdate}{errdesc} = [ "$errdesc", "$errdate", "$errstate" ];
+ $hash->{helper}{mapupdate}{posxy} = \@posxy;
+ $hash->{helper}{mapupdate}{poserrxy} = \@poserrxy;
map {
- ::FW_directNotify("#FHEMWEB:$_", "AutomowerConnectUpdateDetail ( '$name', '$type', $detailFnFirst, $picx, $picy, $scalx, [ '$errdesc', '$errdate' ], [ $posxy ], [ $poserrxy ] )","");
+ ::FW_directNotify("#FHEMWEB:$_", "AutomowerConnectUpdateJson ( '$FW_ME/$type/$name/json' )","");
} devspec2array("TYPE=FHEMWEB");
$hash->{helper}{detailFnFirst} = 0;
- return undef;
+
+return undef;
}
##############################################################
@@ -617,17 +676,19 @@ sub APIAuth {
my $client_id = $hash->{helper}->{client_id};
my $client_secret = $hash->{helper}->{passObj}->getReadPassword( $name );
my $grant_type = $hash->{helper}->{grant_type};
+ my $timeout = AttrVal( $name, 'timeoutApiAuth', $hash->{helper}->{timeout_apiauth} );
my $header = "Content-Type: application/x-www-form-urlencoded\r\nAccept: application/json";
my $data = 'grant_type=' . $grant_type.'&client_id=' . $client_id . '&client_secret=' . $client_secret;
::HttpUtils_NonblockingGet( {
url => AUTHURL . '/oauth2/token',
- timeout => 5,
+ timeout => $timeout,
hash => $hash,
method => 'POST',
header => $header,
data => $data,
callback => \&APIAuthResponse,
+ t_begin => scalar gettimeofday()
} );
}
} else {
@@ -648,6 +709,7 @@ sub APIAuthResponse {
my $statuscode = $param->{code} // '';
my $iam = "$type $name APIAuthResponse:";
+ Log3 $name, 1, "$iam response time ". sprintf( "%.2f", ( gettimeofday() - $param->{t_begin} ) ) . ' s' if ( $param->{timeout} == 60 );
Log3 $name, 1, "\ndebug $iam \n\$statuscode [$statuscode]\n\$err [$err],\n \$data [$data] \n\$param->url $param->{url}" if ( AttrVal($name, 'debug', '') );
if( !$err && $statuscode == 200 && $data) {
@@ -726,17 +788,19 @@ sub getMower {
my $access_token = ReadingsVal($name,".access_token","");
my $provider = ReadingsVal($name,".provider","");
my $client_id = $hash->{helper}->{client_id};
+ my $timeout = AttrVal( $name, 'timeoutGetMower', $hash->{helper}->{timeout_getmower} );
my $header = "Accept: application/vnd.api+json\r\nX-Api-Key: " . $client_id . "\r\nAuthorization: Bearer " . $access_token . "\r\nAuthorization-Provider: " . $provider;
Log3 $name, 5, "$iam header [ $header ]";
::HttpUtils_NonblockingGet({
- url => APIURL . "/mowers",
- timeout => 5,
- hash => $hash,
- method => "GET",
- header => $header,
- callback => \&getMowerResponse,
+ url => APIURL . "/mowers",
+ timeout => $timeout,
+ hash => $hash,
+ method => "GET",
+ header => $header,
+ callback => \&getMowerResponse,
+ t_begin => scalar gettimeofday()
});
@@ -750,10 +814,11 @@ sub getMowerResponse {
my $hash = $param->{hash};
my $name = $hash->{NAME};
my $type = $hash->{TYPE};
- my $statuscode = $param->{code};
+ my $statuscode = $param->{code} // '';
my $iam = "$type $name getMowerResponse:";
my $mowerNumber = $hash->{helper}{mowerNumber};
+ Log3 $name, 1, "$iam response time ". sprintf( "%.2f", ( gettimeofday() - $param->{t_begin} ) ) . ' s' if ( $param->{timeout} == 60 );
Log3 $name, 1, "debug $iam \$statuscode [$statuscode]\n\$err [$err],\n \$data [$data] \n\$param->url $param->{url}" if ( AttrVal($name, 'debug', '') );
if( !$err && $statuscode == 200 && $data) {
@@ -789,7 +854,7 @@ sub getMowerResponse {
}
Log3 $name, 5, "$iam found $foundMower ";
- if ( defined ($hash->{helper}{mower}{id}) && $hash->{helper}{midnightCycle} ) { # update dataset
+ if ( defined ( $hash->{helper}{mower}{id} ) && $hash->{helper}{midnightCycle} ) { # update dataset
$hash->{helper}{mowerold}{attributes}{metadata}{statusTimestamp} = $hash->{helper}{mower}{attributes}{metadata}{statusTimestamp};
$hash->{helper}{mowerold}{attributes}{mower}{activity} = $hash->{helper}{mower}{attributes}{mower}{activity};
@@ -800,6 +865,7 @@ sub getMowerResponse {
$hash->{helper}{mowerold}{attributes}{metadata}{statusTimestamp} = $hash->{helper}{mowers}[$mowerNumber]{attributes}{metadata}{statusTimestamp};
$hash->{helper}{mowerold}{attributes}{mower}{activity} = $hash->{helper}{mowers}[$mowerNumber]{attributes}{mower}{activity};
$hash->{helper}{mowerold}{attributes}{statistics}{numberOfCollisions} = $hash->{helper}{mowers}[$mowerNumber]{attributes}{statistics}{numberOfCollisions};
+ $hash->{helper}{statistics}{numberOfCollisionsOld} = $hash->{helper}{mowers}[$mowerNumber]{attributes}{statistics}{numberOfCollisions};
if ( AttrVal( $name, 'mapImageCoordinatesToRegister', '' ) eq '' ) {
posMinMax( $hash, $hash->{helper}{mowers}[$mowerNumber]{attributes}{positions} );
@@ -814,7 +880,7 @@ sub getMowerResponse {
$hash->{helper}{storediff} = $hash->{helper}{mower}{attributes}{metadata}{statusTimestamp} - $hash->{helper}{mowerold}{attributes}{metadata}{statusTimestamp};
- calculateStatistics($hash) if ( $hash->{helper}{midnightCycle} );
+ calculateStatistics( $hash ) if ( $hash->{helper}{midnightCycle} );
# Update readings
readingsBeginUpdate($hash);
@@ -873,6 +939,7 @@ sub CMD {
my $name = $hash->{NAME};
my $type = $hash->{TYPE};
my $iam = "$type $name CMD:";
+ my $timeout = AttrVal( $name, 'timeoutCMD', $hash->{helper}->{timeout_cmd} );
$hash->{helper}{mower_commandSend} = $cmd[ 0 ] . ' ' . ( $cmd[ 1 ] ? $cmd[ 1 ] : '' );
if ( IsDisabled( $name ) ) {
@@ -920,12 +987,13 @@ my $header = "Accept: application/vnd.api+json\r\nX-Api-Key: ".$client_id."\r\nA
::HttpUtils_NonblockingGet({
url => APIURL . "/mowers/". $mower_id . "/".$post,
- timeout => 15,
+ timeout => $timeout,
hash => $hash,
method => "POST",
header => $header,
data => $json,
callback => \&CMDResponse,
+ t_begin => scalar gettimeofday()
});
}
@@ -939,6 +1007,7 @@ sub CMDResponse {
my $statuscode = $param->{code};
my $iam = "$type $name CMDResponse:";
+ Log3 $name, 1, "$iam response time ". sprintf( "%.2f", ( gettimeofday() - $param->{t_begin} ) ) . ' s' if ( $param->{timeout} == 60 );
Log3 $name, 1, "\ndebug $iam \n\$statuscode [$statuscode]\n\$err [$err],\n \$data [$data] \n\$param->url $param->{url}" if ( AttrVal($name, 'debug', '') );
if( !$err && $statuscode == 202 && $data ) {
@@ -1169,6 +1238,34 @@ sub Attr {
}
##########
+ } elsif( $attrName eq "loglevelDevIo" ) {
+
+ if( $cmd eq "set" ) {
+
+ return "$iam $attrName is invalid, select a number of [012345]" unless( $attrVal =~ /^[0-5]{1}$/ );
+ $hash->{devioLoglevel} = $attrVal;
+ Log3 $name, 4, "$iam $cmd $attrName $attrVal";
+
+ } elsif( $cmd eq "del" ) {
+
+ delete( $hash->{devioLoglevel} );
+ Log3 $name, 3, "$iam $cmd $attrName and set default.";
+
+ }
+ ##########
+ } elsif( $attrName =~ /^(timeoutGetMower|timeoutApiAuth|timeoutCMD)$/ ) {
+
+ if( $cmd eq "set" ) {
+
+ return "$iam $attrVal is invalid, allowed time as integer between 5 and 61" unless( $attrVal =~ /^[\d]{1,2}$/ && $attrVal > 5 && $attrVal < 61 );
+ Log3 $name, 4, "$iam $cmd $attrName $attrVal";
+
+ } elsif( $cmd eq "del" ) {
+
+ Log3 $name, 3, "$iam $cmd $attrName and set default value.";
+
+ }
+ ##########
} elsif ( $attrName eq 'numberOfWayPointsToDisplay' ) {
my $icurr = scalar @{$hash->{helper}{areapos}};
@@ -1179,7 +1276,7 @@ sub Attr {
for ( my $i = $icurr; $i > $attrVal; $i-- ) {
pop @{$hash->{helper}{areapos}};
}
- Log3 $name, 3, "$iam $cmd $attrName $attrVal";
+ Log3 $name, 4, "$iam $cmd $attrName $attrVal";
} elsif( $cmd eq "del" ) {
@@ -1360,6 +1457,8 @@ sub AlignArray {
@ar = reverse @ar if ( $cnt > 1 ); # positions seem to be in reversed order
+ # @ar = @ar if ( $cnt > 1 ); # positions seem to be not in reversed order
+
$tmp = dclone( \@ar );
if ( @{ $hash->{helper}{areapos} } ) {
@@ -1434,6 +1533,7 @@ sub isErrorThanPrepare {
my $errc = $hash->{helper}{mower}{attributes}{mower}{errorCode};
$hash->{helper}{lasterror}{errordesc} = $::FHEM::Devices::AMConnect::Common::errortable->{$errc};
$hash->{helper}{lasterror}{errordate} = FmtDateTimeGMT( $ect / 1000 );
+ $hash->{helper}{lasterror}{errorstate} = $hash->{helper}{mower}{attributes}{mower}{state};
$hash->{helper}{lasterror}{errorzone} = $hash->{helper}{currentZone} if ( defined( $hash->{helper}{currentZone} ) );
my $tmp = dclone( $hash->{helper}{lasterror} );
@@ -1458,6 +1558,7 @@ sub resetLastErrorIfCorrected {
$hash->{helper}{lasterror}{timestamp} = 0;
$hash->{helper}{lasterror}{errordesc} = '-';
$hash->{helper}{lasterror}{errordate} = '';
+ $hash->{helper}{lasterror}{errorstate} = '';
::FHEM::Devices::AMConnect::Common::FW_detailFn_Update ($hash);
}
@@ -1653,22 +1754,28 @@ sub GetMap() {
}
#########################
-# sub GetJson() {
- # my ($request) = @_;
+sub GetJson() {
+ my ($request) = @_;
- # if ( $request =~ /^\/(AutomowerConnect)\/(\w+)\/json/ ) {
+ if ( $request =~ /^\/(AutomowerConnect)\/(\w+)\/json/ ) {
- # my $type = $1;
- # my $name = $2;
- # my $hash = $::defs{$name};
- # my $jsonMime = "application/json";
- # my $jsonData = eval { encode_json ( $hash->{helper}{areapos} ) };
- # return ( $jsonMime, $jsonData );
+ my $type = $1;
+ my $name = $2;
+ my $hash = $::defs{$name};
+ my $jsonMime = "application/json";
+ my $jsonData = eval { encode_json ( $hash->{helper}{mapupdate} ) };
+ if ($@) {
- # }
- # return ( "text/plain; charset=utf-8", "No AutomowerConnect device for webhook $request" );
+ Log3 $name, 2, "$type $name encode_json error: $@";
+ return ( "text/plain; charset=utf-8", "No AutomowerConnect device for webhook $request" );
-# }
+ }
+ return ( $jsonMime, $jsonData );
+
+ }
+ return ( "text/plain; charset=utf-8", "No AutomowerConnect device for webhook $request" );
+
+}
#########################
sub readMap {
@@ -1782,7 +1889,8 @@ sub fillReadings {
readingsBulkUpdateIfChanged($hash, "planner_nextStart", $tstamp ? $timestamp : '-' );
$pref = 'statistics';
- readingsBulkUpdateIfChanged( $hash, $pref."_numberOfCollisions", '(' . $hash->{helper}{statistics}{lastDayCollisions} . '/' . $hash->{helper}{mower}{attributes}{$pref}{numberOfCollisions} . ')' );
+ my $noCol = $hash->{helper}{mower}{attributes}{$pref}{numberOfCollisions} - $hash->{helper}{mowerold}{attributes}{$pref}{numberOfCollisions};
+ readingsBulkUpdateIfChanged( $hash, $pref."_numberOfCollisions", '(' . $noCol . '/' . $hash->{helper}{statistics}{lastDayCollisions} . '/' . $hash->{helper}{mower}{attributes}{$pref}{numberOfCollisions} . ')' );
readingsBulkUpdateIfChanged( $hash, $pref."_newGeoDataSets", $hash->{helper}{newdatasets} );
$pref = 'settings';
readingsBulkUpdateIfChanged( $hash, $pref."_headlight", $hash->{helper}{mower}{attributes}{$pref}{headlight}{mode} );
@@ -1806,7 +1914,9 @@ sub calculateStatistics {
$hash->{helper}{statistics}{lastDayTrack} = $hash->{helper}{statistics}{currentDayTrack};
$hash->{helper}{statistics}{lastDayArea} = $hash->{helper}{statistics}{currentDayArea};
$hash->{helper}{statistics}{lastDayTime} = $hash->{helper}{statistics}{currentDayTime};
- $hash->{helper}{statistics}{lastDayCollisions} = $hash->{helper}{mower}{attributes}{statistics}{numberOfCollisions} - $hash->{helper}{mowerold}{attributes}{statistics}{numberOfCollisions};
+ $hash->{helper}{statistics}{lastDayCollisions} = $hash->{helper}{mower}{attributes}{statistics}{numberOfCollisions} - $hash->{helper}{statistics}{numberOfCollisionsOld};
+ $hash->{helper}{statistics}{numberOfCollisionsOld} = $hash->{helper}{mower}{attributes}{statistics}{numberOfCollisions};
+
$hash->{helper}{statistics}{currentWeekTrack} += $hash->{helper}{statistics}{currentDayTrack};
$hash->{helper}{statistics}{currentWeekArea} += $hash->{helper}{statistics}{currentDayArea};
$hash->{helper}{statistics}{currentWeekTime} += $hash->{helper}{statistics}{currentDayTime};
@@ -2056,7 +2166,7 @@ sub listErrorStack {
for ( my $i = 0; $i < @{ $hash->{helper}{errorstack} }; $i++ ) {
- $cnt++; $ret .= ' ' . $hash->{helper}{errorstack}[$i]{errordate} . ' | ' . $hash->{helper}{errorstack}[$i]{errordesc} . ' | ' . $hash->{helper}{errorstack}[$i]{errorzone} . ' | ' . $hash->{helper}{errorstack}[$i]{positions}[0]{longitude} . ' / ' . $hash->{helper}{errorstack}[$i]{positions}[0]{latitude} . ' |
';
+ $cnt++; $ret .= ' ' . $hash->{helper}{errorstack}[$i]{errordate} . ' | ' . $hash->{helper}{errorstack}[$i]{errorstate} . ' - ' . $hash->{helper}{errorstack}[$i]{errordesc} . ' | ' . $hash->{helper}{errorstack}[$i]{errorzone} . ' | ' . $hash->{helper}{errorstack}[$i]{positions}[0]{longitude} . ' / ' . $hash->{helper}{errorstack}[$i]{positions}[0]{latitude} . ' |
';
}
@@ -2299,17 +2409,17 @@ sub wsRead {
AlignArray( $hash );
- my $deltaTime = $hash->{helper}{positionsTime} - $hash->{helper}{statusTime};
+ # my $deltaTime = $hash->{helper}{positionsTime} - $hash->{helper}{statusTime};
# if encounter positions shortly after status-event count it as error positions
- if ( $hash->{helper}{mower}{attributes}{mower}{errorCode} && $deltaTime > 0 && $deltaTime < 0.29 && @{ $result->{attributes}{positions} } < 3) {
+ # if ( $hash->{helper}{mower}{attributes}{mower}{errorCode} && $deltaTime > 0 && $deltaTime < 0.29 && @{ $result->{attributes}{positions} } < 3) {
- $hash->{helper}{areapos}[ 0 ]{act} = 'N';
- $hash->{helper}{areapos}[ 1 ]{act} = 'N';
- $hash->{helper}{lasterror}{positions} = [dclone( $hash->{helper}{areapos}[ 0 ] ), dclone( $hash->{helper}{areapos}[ 1 ] ) ];
- $hash->{helper}{errorstack}[0]{positions} = [dclone( $hash->{helper}{areapos}[ 0 ] ), dclone( $hash->{helper}{areapos}[ 1 ] ) ];
+ # $hash->{helper}{areapos}[ 0 ]{act} = 'N';
+ # $hash->{helper}{areapos}[ 1 ]{act} = 'N';
+ # $hash->{helper}{lasterror}{positions} = [dclone( $hash->{helper}{areapos}[ 0 ] ), dclone( $hash->{helper}{areapos}[ 1 ] ) ];
+ # $hash->{helper}{errorstack}[0]{positions} = [dclone( $hash->{helper}{areapos}[ 0 ] ), dclone( $hash->{helper}{areapos}[ 1 ] ) ];
- }
+ # }
FW_detailFn_Update ($hash);
diff --git a/fhem/www/pgm2/automowerconnect.js b/fhem/www/pgm2/automowerconnect.js
index 609bfa32d..730d8f66b 100644
--- a/fhem/www/pgm2/automowerconnect.js
+++ b/fhem/www/pgm2/automowerconnect.js
@@ -6,7 +6,7 @@ function AutomowerConnectShowError( ctx, div, dev, picx, picy, errdesc, erray )
ctx.beginPath();
ctx.fillStyle = div.getAttribute( 'data-errorBackgroundColor' );
ctx.font = div.getAttribute( 'data-errorFont' );
- var m = ctx.measureText( errdesc[ 1 ] + ', ' + dev + ': ' + errdesc[ 0 ] ).width > picy - 6;
+ var m = ctx.measureText( errdesc[ 1 ] + ', ' + dev + ': ' + errdesc[ 2 ] + ' - ' + errdesc[ 0 ] ).width > picx - 6;
if ( m ) {
@@ -24,11 +24,11 @@ function AutomowerConnectShowError( ctx, div, dev, picx, picy, errdesc, erray )
if ( m ) {
ctx.fillText( errdesc[ 1 ] + ', ' + dev + ':', 3, 15 );
- ctx.fillText( errdesc[ 0 ], 3, 30 );
+ ctx.fillText( errdesc[ 2 ] + ' - ' + errdesc[ 0 ], 3, 30 );
} else {
- ctx.fillText( errdesc[ 1 ] + ', ' + dev + ': ' + errdesc[ 0 ], 3, 15 );
+ ctx.fillText( errdesc[ 1 ] + ', ' + dev + ': ' + errdesc[ 2 ] + ' - ' + errdesc[ 0 ], 3, 15 );
}
@@ -271,11 +271,18 @@ function AutomowerConnectTor ( x0, y0, x1, y1 ) {
//~ log ('ret: ' + ret);
return ret;
}
+
+function AutomowerConnectUpdateJson ( path ) {
+ $.getJSON( path, function( data, textStatus ) {
+ log( 'AutomowerConnectUpdateJson ( '+path+' ): status '+textStatus );
+ AutomowerConnectUpdateDetail ( data.name, data.type, data.detailfnfirst, data.picx, data.picy, data.scalx, data.errdesc, data.posxy, data.poserrxy );
+
+ });
+
+}
+
//AutomowerConnectUpdateDetail (, , , , ,, , , )
function AutomowerConnectUpdateDetail (dev, type, detailfnfirst, picx, picy, scalx, errdesc, pos, erray) {
- //~ $.getJSON('./fhem/AutomowerConnect/am430x/json', function(data) {
- //~ log(data[0].longitude+' '+data[0].latitude+' '+data[0].act);
-//~ });
const colorat = {
"U" : "otherActivityPath",
"N" : "errorPath",
@@ -286,7 +293,6 @@ function AutomowerConnectUpdateDetail (dev, type, detailfnfirst, picx, picy, sca
"L" : "leavingPath",
"G" : "goingHomePath"
};
-// log('pos.length '+pos.length+' lixy.length '+lixy.length+', scalx '+scalx );
// log('loop: Start '+ type+' '+dev );
if (FW_urlParams.detail == dev || 1) {
const canvas_0 = document.getElementById(type+'_'+dev+'_canvas_0');
@@ -295,7 +301,7 @@ function AutomowerConnectUpdateDetail (dev, type, detailfnfirst, picx, picy, sca
if ( canvas && canvas_0 ) {
-// log('loop: canvas && canvas_0 true '+ type+' '+dev );
+// log('loop: canvas && canvas_0 true '+ type+' '+dev + ' detailfnfirst '+detailfnfirst);
if ( detailfnfirst ) {
@@ -306,6 +312,7 @@ function AutomowerConnectUpdateDetail (dev, type, detailfnfirst, picx, picy, sca
// draw area limits
const lixy = div.getAttribute( 'data-areaLimitsPath' ).split(",");
if ( lixy.length > 0 ) AutomowerConnectLimits( ctx0, div, lixy, 'area' );
+// log('pos.length '+pos.length+' lixy.length '+lixy.length+', scalx '+scalx );
// draw property limits
const plixy = div.getAttribute( 'data-propertyLimitsPath' ).split( "," );