2
0
mirror of https://github.com/fhem/fhem-mirror.git synced 2025-02-25 03:44:52 +00:00

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
This commit is contained in:
Ellert 2023-06-16 22:04:30 +00:00
parent f3eddb5d81
commit 5551c67305
4 changed files with 240 additions and 63 deletions

View File

@ -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

View File

@ -378,11 +378,39 @@ __END__
</code></li>
<li><a href="disable">disable</a></li>
<li><a href="disabledForIntervals">disabledForIntervals</a></li>
<br><br>
</ul>
<br>
<a id="AutomowerConnectUserAttr"></a>
<b>userattr</b><br>
<ul>
The following user attributes are taken into account.<br>
<li><a id='AutomowerConnect-attr-loglevelDevIo'>loglevelDevIo</a><br>
<code>attr &lt;name&gt; loglevelDevIo &lt;[012345]&gt;</code><br>
Set internal deviologlevel, <a target="_blank" href="https://wiki.fhem.de/wiki/DevIo#Wichtige_Internals_zur_Konfiguration"> DevIo: Wichtige_Internals_zur_Konfiguration</a> </li>
<li><a id='AutomowerConnect-attr-timeoutGetMower'>timeoutGetMower</a><br>
<code>attr &lt;name&gt; timeoutGetMower &lt;[6 to 60]&gt;</code><br>
Set timeout for API call, default 5 s. </li>
<li><a id='AutomowerConnect-attr-timeoutApiAuth'>ApiAuth</a><br>
<code>attr &lt;name&gt; timeoutApiAuth &lt;[6 to 60]&gt;</code><br>
Set timeout for API call, default 5 s. </li>
<li><a id='AutomowerConnect-attr-timeoutCMD'>timeoutCMD</a><br>
<code>attr &lt;name&gt; timeoutCMD &lt;[6 to 60]&gt;</code><br>
Set timeout for API call, default 5 s. </li><br>
The response time is meassured and logged if a timeout ist set to 60 s.
<br><br>
</ul>
<a id="AutomowerConnectReadings"></a>
<b>Readings</b>
<ul>
@ -407,7 +435,7 @@ __END__
<li>settings_cuttingHeight - actual cutting height from API</li>
<li>settings_headlight - actual headlight mode from API</li>
<li>statistics_newGeoDataSets - number of new data sets between the last two different time stamps</li>
<li>statistics_numberOfCollisions - Number of collisions (last day/all days)</li>
<li>statistics_numberOfCollisions - Number of collisions (current day/last day/all days)</li>
<li>status_connected - state of connetion between mower and Husqvarna Cloud.</li>
<li>status_statusTimestamp - local time of last status update</li>
<li>status_statusTimestampDiff - time difference in seconds between the last and second last status update</li>
@ -708,11 +736,39 @@ __END__
</code></li>
<li><a href="disable">disable</a></li>
<li><a href="disabledForIntervals">disabledForIntervals</a></li>
<br>
</ul>
<br>
<a id="AutomowerConnectUserAttr"></a>
<b>userattr</b><br>
<ul>
Die folgenden Benutzerattribute werden unterstützt.<br>
<li><a id='AutomowerConnect-attr-loglevelDevIo'>loglevelDevIo</a><br>
<code>attr &lt;name&gt; loglevelDevIo &lt;[012345]&gt;</code><br>
Setzt das Internal deviologlevel, <a target="_blank" href="https://wiki.fhem.de/wiki/DevIo#Wichtige_Internals_zur_Konfiguration"> DevIo: Wichtige_Internals_zur_Konfiguration</a> </li>
<li><a id='AutomowerConnect-attr-timeoutGetMower'>timeoutGetMower</a><br>
<code>attr &lt;name&gt; timeoutGetMower &lt;[6 to 60]&gt;</code><br>
Setzt den Timeout für das Lesen der Mäherdaten, default 5 s. </li>
<li><a id='AutomowerConnect-attr-timeoutApiAuth'>timeoutApiAuth</a><br>
<code>attr &lt;name&gt; timeoutApiAuth &lt;[6 to 60]&gt;</code><br>
Setzt den Timeout für die Authentifikation, default 5 s. </li>
<li><a id='AutomowerConnect-attr-timeoutCMD'>timeoutCMD</a><br>
<code>attr &lt;name&gt; timeoutCMD &lt;[6 to 60]&gt;</code><br>
Setzt den Timeout für Befehl senden, default 15 s. </li><br>
Wird ein Timeout auf 60 s gesetzt, wird die Antwortzeit gemessen und geloggt.
<br><br>
</ul>
<a id="AutomowerConnectReadings"></a>
<b>Readings</b>
<ul>
@ -737,7 +793,7 @@ __END__
<li>settings_cuttingHeight - aktuelle Schnitthöhe aus der API</li>
<li>settings_headlight - aktueller Scheinwerfermode aus der API</li>
<li>statistics_newGeoDataSets - Anzahl der neuen Datensätze zwischen den letzten zwei unterschiedlichen Zeitstempeln</li>
<li>statistics_numberOfCollisions - Anzahl der Kollisionen (letzter Tag/alle Tage)</li>
<li>statistics_numberOfCollisions - Anzahl der Kollisionen (laufender Tag/letzter Tag/alle Tage)</li>
<li>status_connected - Status der Verbindung zwischen dem Automower und der Husqvarna Cloud.</li>
<li>status_statusTimestamp - Lokalzeit des letzten Statusupdates in der API</li>
<li>status_statusTimestampDiff - Zeitdifferenz zwischen dem letzten und vorletzten Statusupdate.</li>

View File

@ -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 .= '<tr class="column '.( $cnt % 2 ? 'odd' : 'even' ).'"><td> ' . $hash->{helper}{errorstack}[$i]{errordate} . ' </td><td> ' . $hash->{helper}{errorstack}[$i]{errordesc} . ' </td><td> ' . $hash->{helper}{errorstack}[$i]{errorzone} . ' </td><td> ' . $hash->{helper}{errorstack}[$i]{positions}[0]{longitude} . ' / ' . $hash->{helper}{errorstack}[$i]{positions}[0]{latitude} . ' </td></tr>';
$cnt++; $ret .= '<tr class="column '.( $cnt % 2 ? 'odd' : 'even' ).'"><td> ' . $hash->{helper}{errorstack}[$i]{errordate} . ' </td><td> ' . $hash->{helper}{errorstack}[$i]{errorstate} . ' - ' . $hash->{helper}{errorstack}[$i]{errordesc} . ' </td><td> ' . $hash->{helper}{errorstack}[$i]{errorzone} . ' </td><td> ' . $hash->{helper}{errorstack}[$i]{positions}[0]{longitude} . ' / ' . $hash->{helper}{errorstack}[$i]{positions}[0]{latitude} . ' </td></tr>';
}
@ -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);

View File

@ -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 (<devicename>, <type>, <detailfnfirst>, <imagesize x>, <imagesize y>,<scale x>, <error description>, <path array>, <error array>)
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( "," );