From 7de92ec746bfb8c5fe03db0cc54cc876f0685894 Mon Sep 17 00:00:00 2001 From: Ellert Date: Wed, 1 Feb 2023 21:52:13 +0000 Subject: [PATCH] AutomowerConnect(Device): rework detail view getter for StatisticsData and MowerData git-svn-id: https://svn.fhem.de/fhem/trunk@27166 2b470e98-0d58-463d-a4d8-8e2adae1ed80 --- fhem/CHANGED | 3 + fhem/FHEM/74_AutomowerConnect.pm | 306 +++++++++++++++++-------- fhem/FHEM/75_AutomowerConnectDevice.pm | 281 ++++++++++++++++------- 3 files changed, 405 insertions(+), 185 deletions(-) diff --git a/fhem/CHANGED b/fhem/CHANGED index a4fff875e..74f695f53 100644 --- a/fhem/CHANGED +++ b/fhem/CHANGED @@ -1,5 +1,8 @@ # 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. + - feature: 74_AutomowerConnect: rework detail view + getter for MowerData and StatisticsData + - feature: 75_AutomowerConnectDevice: same as AutomowerConnect - feature: 93_DbLog: new Get menu for a selection of getters, send Log data back to parent process, Forum:#131790 - change: 93_DbRep: ignore non-numeric values in diffValue and output diff --git a/fhem/FHEM/74_AutomowerConnect.pm b/fhem/FHEM/74_AutomowerConnect.pm index b51ea0085..8aad4176b 100644 --- a/fhem/FHEM/74_AutomowerConnect.pm +++ b/fhem/FHEM/74_AutomowerConnect.pm @@ -202,6 +202,17 @@ sub Define{ arrayName => '', maxLength => 0, callFn => '' + }, + statistics => { + currentSpeed => 0, + currentDayTrack => 0, + currentDayArea => 0, + lastDayTrack => 0, + lastDayArea => 0, + currentWeekTrack => 0, + currentWeekArea => 0, + lastWeekTrack => 0, + lastWeekArea => 0 } } ); @@ -337,7 +348,6 @@ sub APIAuthResponse { readingsBulkUpdateIfChanged($hash,'.expires',$hash->{helper}{auth}{expires},0 ); readingsBulkUpdateIfChanged($hash,'.scope',$hash->{helper}{auth}{scope},0 ); readingsBulkUpdateIfChanged($hash,'.token_type',$hash->{helper}{auth}{token_type},0 ); - readingsBulkUpdateIfChanged($hash,'.provider',$hash->{helper}{auth}{provider} ); my $expire_date = FmtDateTime($hash->{helper}{auth}{expires}); readingsBulkUpdateIfChanged($hash,'api_token_expires',$expire_date ); @@ -480,7 +490,6 @@ sub getMowerResponse { readingsBulkUpdateIfChanged($hash, "batteryPercent", $hash->{helper}{mower}{attributes}{battery}{batteryPercent} ); readingsBulkUpdateIfChanged($hash, 'api_MowerFound', $foundMower ); my $pref = 'mower'; - readingsBulkUpdateIfChanged($hash, $pref.'_id', $hash->{helper}{mower}{id} ); readingsBulkUpdateIfChanged($hash, $pref.'_mode', $hash->{helper}{mower}{attributes}{$pref}{mode} ); readingsBulkUpdateIfChanged($hash, $pref.'_activity', $hash->{helper}{mower}{attributes}{$pref}{activity} ); readingsBulkUpdateIfChanged($hash, $pref.'_state', $hash->{helper}{mower}{attributes}{$pref}{state} ); @@ -497,8 +506,6 @@ sub getMowerResponse { my $model = $hash->{helper}{mower}{attributes}{$pref}{model}; $model =~ s/AUTOMOWER./AM/; $hash->{MODEL} = $model if ( $model && $hash->{MODEL} ne $model ); - # readingsBulkUpdateIfChanged($hash, $pref."_model", $model ); - readingsBulkUpdateIfChanged($hash, $pref."_serialNumber", $hash->{helper}{mower}{attributes}{$pref}{serialNumber} ); $pref = 'planner'; readingsBulkUpdateIfChanged($hash, "planner_restrictedReason", $hash->{helper}{mower}{attributes}{$pref}{restrictedReason} ); readingsBulkUpdateIfChanged($hash, "planner_overrideAction", $hash->{helper}{mower}{attributes}{$pref}{override}{action} ); @@ -507,12 +514,7 @@ sub getMowerResponse { $timestamp = FmtDateTime($tstamp/1000); readingsBulkUpdateIfChanged($hash, "planner_nextStart", $tstamp ? $timestamp : '-' ); $pref = 'statistics'; - readingsBulkUpdateIfChanged($hash, $pref."_numberOfChargingCycles", $hash->{helper}->{mower}{attributes}{$pref}{numberOfChargingCycles} ); - readingsBulkUpdateIfChanged($hash, $pref."_totalCuttingTime", $hash->{helper}->{mower}{attributes}{$pref}{totalCuttingTime} ); - readingsBulkUpdateIfChanged($hash, $pref."_totalChargingTime", $hash->{helper}->{mower}{attributes}{$pref}{totalChargingTime} ); - readingsBulkUpdateIfChanged($hash, $pref."_totalSearchingTime", $hash->{helper}->{mower}{attributes}{$pref}{totalSearchingTime} ); readingsBulkUpdateIfChanged($hash, $pref."_numberOfCollisions", $hash->{helper}->{mower}{attributes}{$pref}{numberOfCollisions} ); - readingsBulkUpdateIfChanged($hash, $pref."_totalRunningTime", $hash->{helper}->{mower}{attributes}{$pref}{totalRunningTime} ); $pref = 'settings'; readingsBulkUpdateIfChanged($hash, $pref."_headlight", $hash->{helper}->{mower}{attributes}{$pref}{headlight}{mode} ); readingsBulkUpdateIfChanged($hash, $pref."_cuttingHeight", $hash->{helper}->{mower}{attributes}{$pref}{cuttingHeight} ); @@ -521,9 +523,7 @@ sub getMowerResponse { readingsBulkUpdateIfChanged($hash, $pref."_Timestamp", FmtDateTime( $hash->{helper}{mower}{attributes}{metadata}{statusTimestamp}/1000 )); readingsBulkUpdateIfChanged($hash, $pref."_TimestampDiff", $storediff/1000 ); readingsBulkUpdateIfChanged($hash, $pref."_TimestampOld", FmtDateTime( $hash->{helper}{mowerold}{attributes}{metadata}{statusTimestamp}/1000 )); - $pref = 'positions'; - readingsBulkUpdateIfChanged($hash, $pref."_lastLonLat", $hash->{helper}{mower}{attributes}{$pref}[0]{longitude} . ' ' . $hash->{helper}{mower}{attributes}{$pref}[0]{latitude} ); - readingsBulkUpdateIfChanged($hash, 'state', 'connected' ); + readingsEndUpdate($hash, 1); my @time = localtime(); my $secs = ( $time[2] * 3600 ) + ( $time[1] * 60 ) + $time[0]; @@ -531,23 +531,23 @@ sub getMowerResponse { # do at midnight if ( $secs <= $interval ) { - readingsBulkUpdateIfChanged( $hash, 'statistics_lastDayTrack', ReadingsNum( $name, 'statistics_currentDayTrack', 0 )); - readingsBulkUpdateIfChanged( $hash, 'statistics_lastDayArea', ReadingsNum( $name, 'statistics_currentDayArea', 0 )); - readingsBulkUpdateIfChanged( $hash, 'statistics_currentWeekTrack', ReadingsNum( $name, 'statistics_currentWeekTrack', 0 ) + ReadingsNum( $name, 'statistics_currentDayTrack', 0 )); - readingsBulkUpdateIfChanged( $hash, 'statistics_currentWeekArea', ReadingsNum( $name, 'statistics_currentWeekArea', 0 ) + ReadingsNum( $name, 'statistics_currentDayArea', 0 )); - readingsBulkUpdateIfChanged( $hash, 'statistics_currentDayTrack', 0, 0); - readingsBulkUpdateIfChanged( $hash, 'statistics_currentDayArea', 0, 0); - # do on mondays + $hash->{helper}{statistics}{lastDayTrack} = $hash->{helper}{statistics}{currentDayTrack}; + $hash->{helper}{statistics}{lastDayArea} = $hash->{helper}{statistics}{currentDayArea}; + $hash->{helper}{statistics}{currentWeekTrack} += $hash->{helper}{statistics}{currentDayTrack}; + $hash->{helper}{statistics}{currentWeekArea} += $hash->{helper}{statistics}{currentDayArea}; + $hash->{helper}{statistics}{currentDayTrack} = 0; + $hash->{helper}{statistics}{currentDayArea} = 0; + # do on mondays if ( $time[6] == 1 && $secs <= $interval ) { - readingsBulkUpdateIfChanged( $hash, 'statistics_lastWeekTrack', ReadingsNum( $name, 'statistics_currentWeekTrack', 0 )); - readingsBulkUpdateIfChanged( $hash, 'statistics_lastWeekArea', ReadingsNum( $name, 'statistics_currentWeekArea', 0 )); - readingsBulkUpdateIfChanged( $hash, 'statistics_currentWeekTrack', 0, 0); - readingsBulkUpdateIfChanged( $hash, 'statistics_currentWeekArea', 0, 0); + $hash->{helper}{statistics}{lastWeekTrack} = $hash->{helper}{statistics}{currentWeekTrack}; + $hash->{helper}{statistics}{lastWeekArea} = $hash->{helper}{statistics}{currentWeekArea}; + $hash->{helper}{statistics}{currentWeekTrack} = 0; + $hash->{helper}{statistics}{currentWeekArea} = 0; } } - readingsEndUpdate($hash, 1); + readingsSingleUpdate($hash, 'state', 'connected', 1 ); RemoveInternalTimer( $hash, \&APIAuth ); InternalTimer( gettimeofday() + $interval, \&APIAuth, $hash, 0 ); @@ -699,19 +699,29 @@ sub Get { my $ret = '' . FW_detailFn( undef, $name, undef, undef) . ''; return $ret; - } elsif ( $setName eq 'listErrorCodes' ) { + } elsif ( $setName eq 'errorCodes' ) { my $ret = listErrorCodes($hash); return $ret; - } elsif ( $setName eq 'listInternalData' ) { + } elsif ( $setName eq 'InternalData' ) { my $ret = listInternalData($hash); return $ret; + } elsif ( $setName eq 'MowerData' ) { + + my $ret = listMowerData($hash); + return $ret; + + } elsif ( $setName eq 'StatisticsData' ) { + + my $ret = listStatisticsData($hash); + return $ret; + } else { - - return "Unknown argument $setName, choose one of listErrorCodes:noArg listInternalData:noArg "; + + return "Unknown argument $setName, choose one of StatisticsData:noArg MowerData:noArg InternalData:noArg errorCodes:noArg "; } } @@ -743,7 +753,7 @@ sub Set { } elsif ( ReadingsVal( $name, 'state', 'defined' ) !~ /defined|initialized|authentification|authenticated|update/ && $setName eq 'mowerScheduleToAttribute' ) { - my $calendarjson = JSON::XS->new->pretty(1)->encode ($hash->{helper}{mower}{attributes}{calendar}{tasks}); + my $calendarjson = eval { JSON::XS->new->pretty(1)->encode ($hash->{helper}{mower}{attributes}{calendar}{tasks}) }; if ( $@ ) { return "$iam $@"; } @@ -1212,7 +1222,8 @@ sub ChargingStationPosition { sub AreaStatistics { my ($hash) = @_; my $name = $hash->{NAME}; - my $i = $hash->{helper}{MOWING}{cnt}; + my $activity = 'MOWING'; + my $i = $hash->{helper}{$activity}{cnt}; my $k = 0; my @xyarr = @{$hash->{helper}{areapos}};# areapos my $n = @xyarr; @@ -1223,19 +1234,22 @@ sub AreaStatistics { my $vm = 0; for ( $k = 0; $k <= $i-1; $k++) { - $lsum += ((($xyarr[ $k ]{longitude} - $xyarr[ $k+1 ]{longitude}) * $sclon)**2 + (($xyarr[ $k ]{latitude} - $xyarr[ $k+1 ]{latitude}) * $sclat)**2)**0.5; + + $lsum += ((($xyarr[ $k ]{longitude} - $xyarr[ $k+1 ]{longitude}) * $sclon)**2 + (($xyarr[ $k ]{latitude} - $xyarr[ $k+1 ]{latitude}) * $sclat)**2)**0.5; + } + $asum = $lsum * AttrVal($name,'mowerCuttingWidth',0.24); my $td = $xyarr[ 0 ]{storedTimestamp} - $xyarr[ $k ]{storedTimestamp}; - $vm = sprintf( '%.6f', $lsum / $td ) * 1000 if ($td); - $lsum += int( ReadingsNum( $name, 'statistics_currentDayTrack', 0 ) ); - $asum += int( ReadingsNum( $name, 'statistics_currentDayArea', 0 ) ); - readingsBeginUpdate($hash); - readingsBulkUpdateIfChanged($hash,'statistics_currentDayTrack', int($lsum)); # m - readingsBulkUpdateIfChanged($hash,'statistics_currentDayArea', int($asum)); # qm - readingsBulkUpdateIfChanged($hash,'statistics_lastIntervalMowerSpeed', $vm); # m/s - readingsBulkUpdateIfChanged($hash,'statistics_lastIntervalNumberOfWayPoints', $i-1); # m/s - readingsEndUpdate($hash,1); + $vm = sprintf( '%.6f', $lsum / $td ) * 1000 if ($td); # m/s + $hash->{helper}{$activity}{speed} = $vm; + $hash->{helper}{$activity}{track} = $lsum; + $hash->{helper}{$activity}{area} = $asum; + $hash->{helper}{statistics}{currentSpeed} = $vm; + $hash->{helper}{statistics}{currentDayTrack} += $lsum; + $hash->{helper}{statistics}{currentDayArea} += $asum; + $hash->{helper}{statistics}{currentSpeed} = $vm; + return undef; } @@ -1335,6 +1349,89 @@ sub posMinMax { return undef; } +######################### +sub listStatisticsData { + my ( $hash ) = @_; + my $name = $hash->{NAME}; + my $cnt = 0; + my $ret = ''; + $ret .= ''; + $ret .= ''; + + $ret .= ''; + $cnt++;$ret .= ''; + $cnt++;$ret .= ''; + $cnt++;$ret .= ''; + $cnt++;$ret .= ''; + $cnt++;$ret .= ''; + $cnt++;$ret .= ''; + + $cnt++;$ret .= ''; + $cnt++;$ret .= ''; + $cnt++;$ret .= ''; + $cnt++;$ret .= ''; + $cnt++;$ret .= ''; + $cnt++;$ret .= ''; + $cnt++;$ret .= ''; + $cnt++;$ret .= ''; + $cnt++;$ret .= ''; + + $ret .= '
Statistics Data
Hash Path Value Unit
$hash->{helper}{mower}{attributes}{statistics}{numberOfChargingCycles}   ' . $hash->{helper}{mower}{attributes}{statistics}{numberOfChargingCycles} . '
$hash->{helper}{mower}{attributes}{statistics}{numberOfCollisions}   ' . $hash->{helper}{mower}{attributes}{statistics}{numberOfCollisions} . '
$hash->{helper}{mower}{attributes}{statistics}{totalChargingTime}   ' . $hash->{helper}{mower}{attributes}{statistics}{totalChargingTime} . ' s
$hash->{helper}{mower}{attributes}{statistics}{totalCuttingTime}   ' . $hash->{helper}{mower}{attributes}{statistics}{totalCuttingTime} . ' s
$hash->{helper}{mower}{attributes}{statistics}{totalRunningTime}   ' . $hash->{helper}{mower}{attributes}{statistics}{totalRunningTime} . '1 s
$hash->{helper}{mower}{attributes}{statistics}{totalSearchingTime}   ' . $hash->{helper}{mower}{attributes}{statistics}{totalSearchingTime} . ' s
$hash->{helper}{statistics}{currentSpeed}   ' . $hash->{helper}{statistics}{currentSpeed} . ' m/s
$hash->{helper}{statistics}{currentDayTrack}   ' . $hash->{helper}{statistics}{currentDayTrack} . ' m
$hash->{helper}{statistics}{currentDayArea}   ' . $hash->{helper}{statistics}{currentDayArea} . ' qm
$hash->{helper}{statistics}{lastDayTrack}   ' . $hash->{helper}{statistics}{lastDayTrack} . ' m
$hash->{helper}{statistics}{lastDayArea}   ' . $hash->{helper}{statistics}{lastDayArea} . ' qm
$hash->{helper}{statistics}{currentWeekTrack}   ' . $hash->{helper}{statistics}{currentWeekTrack} . ' m
$hash->{helper}{statistics}{currentWeekArea}   ' . $hash->{helper}{statistics}{currentWeekArea} . ' qm
$hash->{helper}{statistics}{lastWeekTrack}   ' . $hash->{helper}{statistics}{lastWeekTrack} . ' m
$hash->{helper}{statistics}{lastWeekArea}   ' . $hash->{helper}{statistics}{lastWeekArea} . ' qm
'; + $ret .= '

1 totalRunningTime = totalCuttingTime + totalSearchingTime'; + $ret .= ''; + + return $ret; +} + +######################### +sub listMowerData { + my ( $hash ) = @_; + my $name = $hash->{NAME}; + my $cnt = 0; + my $ret = ''; + $ret .= ''; + $ret .= ''; + + $ret .= ''; + $cnt++;$ret .= ''; + $cnt++;$ret .= ''; + $cnt++;$ret .= ''; + $cnt++;$ret .= ''; + $cnt++;$ret .= ''; + $cnt++;$ret .= ''; + $cnt++;$ret .= ''; + $cnt++;$ret .= ''; + $cnt++;$ret .= ''; + $cnt++;$ret .= ''; + $cnt++;$ret .= ''; + + my $calendarjson = eval { JSON::XS->new->pretty(1)->encode ($hash->{helper}{mower}{attributes}{calendar}{tasks}) }; + + $cnt++;$ret .= ''; + $cnt++;$ret .= ''; + $cnt++;$ret .= ''; + $cnt++;$ret .= ''; + $cnt++;$ret .= ''; + $cnt++;$ret .= ''; + $cnt++;$ret .= ''; + $cnt++;$ret .= ''; + $cnt++;$ret .= ''; + $cnt++;$ret .= ''; +# $cnt++;$ret .= ''; + $cnt++;$ret .= ''; + $cnt++;$ret .= ''; + $cnt++;$ret .= ''; + $cnt++;$ret .= ''; + $cnt++;$ret .= ''; + $cnt++;$ret .= ''; + + $ret .= '
Mower Data
Hash Path Value Unit
$hash->{helper}{mower}{type}   ' . $hash->{helper}{mower}{type} . '
$hash->{helper}{mower}{id}   ' . $hash->{helper}{mower}{id} . '
$hash->{helper}{mower}{attributes}{system}{name}   ' . $hash->{helper}{mower}{attributes}{system}{name} . '
$hash->{helper}{mower}{attributes}{system}{model}   ' . $hash->{helper}{mower}{attributes}{system}{model} . '
$hash->{helper}{mower}{attributes}{system}{serialNumber}   ' . $hash->{helper}{mower}{attributes}{system}{serialNumber} . '
$hash->{helper}{mower}{attributes}{battery}{batteryPercent}   ' . $hash->{helper}{mower}{attributes}{battery}{batteryPercent} . ' %
$hash->{helper}{mower}{attributes}{mower}{mode}   ' . $hash->{helper}{mower}{attributes}{mower}{mode} . '
$hash->{helper}{mower}{attributes}{mower}{activity}   ' . $hash->{helper}{mower}{attributes}{mower}{activity} . '
$hash->{helper}{mower}{attributes}{mower}{state}   ' . $hash->{helper}{mower}{attributes}{mower}{state} . '
$hash->{helper}{mower}{attributes}{mower}{errorCode}   ' . $hash->{helper}{mower}{attributes}{mower}{errorCode} . '
$hash->{helper}{mower}{attributes}{mower}{errorCodeTimestamp}   ' . $hash->{helper}{mower}{attributes}{mower}{errorCodeTimestamp} . ' ms
$hash->{helper}{mower}{attributes}{calendar}{tasks}   ' . ($@ ? $@ : $calendarjson) . '
$hash->{helper}{mower}{attributes}{planner}{nextStartTimestamp}   ' . $hash->{helper}{mower}{attributes}{planner}{nextStartTimestamp} . '
$hash->{helper}{mower}{attributes}{planner}{override}{action}   ' . $hash->{helper}{mower}{attributes}{planner}{override}{action} . '
$hash->{helper}{mower}{attributes}{planner}{restrictedReason}   ' . $hash->{helper}{mower}{attributes}{planner}{restrictedReason} . '
$hash->{helper}{mower}{attributes}{metadata}{connected}   ' . $hash->{helper}{mower}{attributes}{metadata}{connected} . '
$hash->{helper}{mower}{attributes}{metadata}{statusTimestamp}   ' . $hash->{helper}{mower}{attributes}{metadata}{statusTimestamp} . ' ms
$hash->{helper}{mower}{attributes}{positions}[0]{longitude}   ' . $hash->{helper}{mower}{attributes}{positions}[0]{longitude} . ' decimal degree
$hash->{helper}{mower}{attributes}{positions}[0]{latitude}   ' . $hash->{helper}{mower}{attributes}{positions}[0]{latitude} . ' decimal degree
$hash->{helper}{mower}{attributes}{settings}{cuttingHeight}   ' . $hash->{helper}{mower}{attributes}{settings}{cuttingHeight} . '
$hash->{helper}{mower}{attributes}{settings}{headlight}{mode}   ' . $hash->{helper}{mower}{attributes}{settings}{headlight}{mode} . '
$hash->{helper}{mower}{attributes}{statistics}{cuttingBladeUsageTime}   ' . $hash->{helper}{mower}{attributes}{statistics}{cuttingBladeUsageTime} . '
$hash->{helper}{mower}{attributes}{statistics}{numberOfChargingCycles}   ' . $hash->{helper}{mower}{attributes}{statistics}{numberOfChargingCycles} . '
$hash->{helper}{mower}{attributes}{statistics}{numberOfCollisions}   ' . $hash->{helper}{mower}{attributes}{statistics}{numberOfCollisions} . '
$hash->{helper}{mower}{attributes}{statistics}{totalChargingTime}   ' . $hash->{helper}{mower}{attributes}{statistics}{totalChargingTime} . ' s
$hash->{helper}{mower}{attributes}{statistics}{totalCuttingTime}   ' . $hash->{helper}{mower}{attributes}{statistics}{totalCuttingTime} . ' s
$hash->{helper}{mower}{attributes}{statistics}{totalRunningTime}   ' . $hash->{helper}{mower}{attributes}{statistics}{totalRunningTime} . '1 s
$hash->{helper}{mower}{attributes}{statistics}{totalSearchingTime}   ' . $hash->{helper}{mower}{attributes}{statistics}{totalSearchingTime} . ' s
'; + $ret .= '

1 totalRunningTime = totalCuttingTime + totalSearchingTime'; + $ret .= ''; + + return $ret; +} + ######################### sub listInternalData { my ( $hash ) = @_; @@ -1376,9 +1473,17 @@ sub listInternalData { if ( $hash->{TYPE} eq 'AutomowerConnect' ) { $ret .= '

'; - $ret .= ''; + $ret .= ''; - $ret .= ''; + $ret .= ''; + $ret .= ''; + $ret .= ''; + $ret .= ''; + $ret .= ''; + $ret .= ''; + $ret .= ''; + $ret .= ''; + $ret .= ''; $ret .= '
Access Token ( expires: ' . ReadingsVal( $name, 'api_token_expires', 'none') . ' ) Authentification Data
' . ReadingsVal( $name, '.access_token', 'none') . '
Authentification URL' . AUTHURL . '
Client-Id' . $hash->{helper}{client_id} . '
Grant-Type' . $hash->{helper}{grant_type} . '
User-Id' . ReadingsVal($name, '.user_id', '-') . '
Provider' . ReadingsVal($name, '.provider', '-') . '
Scope' . ReadingsVal($name, '.scope', '-') . '
Token Type' . ReadingsVal($name, '.token_type', '-') . '
Token Expires ' . FmtDateTime( ReadingsVal($name, '.expires', '0') ) . '
Access Token' . ReadingsVal($name, '.access_token', '0') . '
'; @@ -1487,43 +1592,56 @@ sub listErrorCodes {

@@ -1640,47 +1768,27 @@ sub listErrorCodes { @@ -1747,43 +1855,56 @@ sub listErrorCodes {
@@ -1902,46 +2033,27 @@ sub listErrorCodes { diff --git a/fhem/FHEM/75_AutomowerConnectDevice.pm b/fhem/FHEM/75_AutomowerConnectDevice.pm index 73990d5ae..5bbd0745f 100644 --- a/fhem/FHEM/75_AutomowerConnectDevice.pm +++ b/fhem/FHEM/75_AutomowerConnectDevice.pm @@ -193,6 +193,17 @@ sub Define{ arrayName => '', maxLength => 0, callFn => '' + }, + statistics => { + currentSpeed => 0, + currentDayTrack => 0, + currentDayArea => 0, + lastDayTrack => 0, + lastDayArea => 0, + currentWeekTrack => 0, + currentWeekArea => 0, + lastWeekTrack => 0, + lastWeekArea => 0 } } ); @@ -332,12 +343,7 @@ sub Notify { $timestamp = FmtDateTime($tstamp/1000); readingsBulkUpdateIfChanged($hash, "planner_nextStart", $tstamp ? $timestamp : '-' ); $pref = 'statistics'; - readingsBulkUpdateIfChanged($hash, $pref."_numberOfChargingCycles", $hash->{helper}->{mower}{attributes}{$pref}{numberOfChargingCycles} ); - readingsBulkUpdateIfChanged($hash, $pref."_totalCuttingTime", $hash->{helper}->{mower}{attributes}{$pref}{totalCuttingTime} ); - readingsBulkUpdateIfChanged($hash, $pref."_totalChargingTime", $hash->{helper}->{mower}{attributes}{$pref}{totalChargingTime} ); - readingsBulkUpdateIfChanged($hash, $pref."_totalSearchingTime", $hash->{helper}->{mower}{attributes}{$pref}{totalSearchingTime} ); readingsBulkUpdateIfChanged($hash, $pref."_numberOfCollisions", $hash->{helper}->{mower}{attributes}{$pref}{numberOfCollisions} ); - readingsBulkUpdateIfChanged($hash, $pref."_totalRunningTime", $hash->{helper}->{mower}{attributes}{$pref}{totalRunningTime} ); $pref = 'settings'; readingsBulkUpdateIfChanged($hash, $pref."_headlight", $hash->{helper}->{mower}{attributes}{$pref}{headlight}{mode} ); readingsBulkUpdateIfChanged($hash, $pref."_cuttingHeight", $hash->{helper}->{mower}{attributes}{$pref}{cuttingHeight} ); @@ -348,7 +354,8 @@ sub Notify { readingsBulkUpdateIfChanged($hash, $pref."_TimestampOld", FmtDateTime( $hash->{helper}{mowerold}{attributes}{metadata}{statusTimestamp}/1000 )); $pref = 'positions'; readingsBulkUpdateIfChanged($hash, $pref."_lastLonLat", $hash->{helper}{mower}{attributes}{$pref}[0]{longitude} . ' ' . $hash->{helper}{mower}{attributes}{$pref}[0]{latitude} ); - readingsBulkUpdateIfChanged($hash, 'state', 'connected' ); + readingsBulkUpdate($hash, 'state', 'connected',1); + readingsEndUpdate($hash, 1); my @time = localtime(); my $secs = ( $time[2] * 3600 ) + ( $time[1] * 60 ) + $time[0]; @@ -356,23 +363,24 @@ sub Notify { # do at midnight if ( $secs <= $interval ) { - readingsBulkUpdateIfChanged( $hash, 'statistics_lastDayTrack', ReadingsNum( $name, 'statistics_currentDayTrack', 0 )); - readingsBulkUpdateIfChanged( $hash, 'statistics_lastDayArea', ReadingsNum( $name, 'statistics_currentDayArea', 0 )); - readingsBulkUpdateIfChanged( $hash, 'statistics_currentWeekTrack', ReadingsNum( $name, 'statistics_currentWeekTrack', 0 ) + ReadingsNum( $name, 'statistics_currentDayTrack', 0 )); - readingsBulkUpdateIfChanged( $hash, 'statistics_currentWeekArea', ReadingsNum( $name, 'statistics_currentWeekArea', 0 ) + ReadingsNum( $name, 'statistics_currentDayArea', 0 )); - readingsBulkUpdateIfChanged( $hash, 'statistics_currentDayTrack', 0, 0); - readingsBulkUpdateIfChanged( $hash, 'statistics_currentDayArea', 0, 0); - # do on mondays - if ( $time[6] == 1 && $secs <= $interval ) { + $hash->{helper}{statistics}{lastDayTrack} = $hash->{helper}{statistics}{currentDayTrack}; + $hash->{helper}{statistics}{lastDayArea} = $hash->{helper}{statistics}{currentDayArea}; + $hash->{helper}{statistics}{currentWeekTrack} += $hash->{helper}{statistics}{currentDayTrack}; + $hash->{helper}{statistics}{currentWeekArea} += $hash->{helper}{statistics}{currentDayArea}; + $hash->{helper}{statistics}{currentDayTrack} = 0; + $hash->{helper}{statistics}{currentDayArea} = 0; + # do on mondays + if ( $time[6] == 1 && $secs <= $interval ) { - readingsBulkUpdateIfChanged( $hash, 'statistics_lastWeekTrack', ReadingsNum( $name, 'statistics_currentWeekTrack', 0 )); - readingsBulkUpdateIfChanged( $hash, 'statistics_lastWeekArea', ReadingsNum( $name, 'statistics_currentWeekArea', 0 )); - readingsBulkUpdateIfChanged( $hash, 'statistics_currentWeekTrack', 0, 0); - readingsBulkUpdateIfChanged( $hash, 'statistics_currentWeekArea', 0, 0); + $hash->{helper}{statistics}{lastWeekTrack} = $hash->{helper}{statistics}{currentWeekTrack}; + $hash->{helper}{statistics}{lastWeekArea} = $hash->{helper}{statistics}{currentWeekArea}; + $hash->{helper}{statistics}{currentWeekTrack} = 0; + $hash->{helper}{statistics}{currentWeekArea} = 0; } } - readingsEndUpdate($hash, 1); + readingsSingleUpdate($hash, 'state', 'connected',1); + } return undef; @@ -520,18 +528,29 @@ sub Get { my $ret = '' . FW_detailFn( undef, $name, undef, undef) . ''; return $ret; - } elsif ( $setName eq 'listErrorCodes' ) { + } elsif ( $setName eq 'errorCodes' ) { my $ret = listErrorCodes($hash); return $ret; - } elsif ( $setName eq 'listInternalData' ) { + } elsif ( $setName eq 'InternalData' ) { my $ret = listInternalData($hash); return $ret; + + } elsif ( $setName eq 'MowerData' ) { + + my $ret = listMowerData($hash); + return $ret; + + } elsif ( $setName eq 'StatisticsData' ) { + + my $ret = listStatisticsData($hash); + return $ret; + } else { - - return "Unknown argument $setName, choose one of listInternalData:noArg listErrorCodes:noArg "; + + return "Unknown argument $setName, choose one of StatisticsData:noArg MowerData:noArg InternalData:noArg errorCodes:noArg "; } } @@ -962,8 +981,8 @@ sub ChargingStationPosition { my $ym = 0; map { $ym += $_->{latitude} } @{$hash->{helper}{cspos}}; $ym = $ym/$n; - $hash->{helper}{chargingStation}{longitude} = int($xm * 10000000 + 0.5) / 10000000; - $hash->{helper}{chargingStation}{latitude} = int($ym * 10000000 + 0.5) / 10000000; + $hash->{helper}{chargingStation}{longitude} = sprintf("%.8f",$xm); + $hash->{helper}{chargingStation}{latitude} = sprintf("%.8f",$ym); return undef; } @@ -971,6 +990,7 @@ sub ChargingStationPosition { sub AreaStatistics { my ($hash) = @_; my $name = $hash->{NAME}; + my $activity = 'MOWING'; my $i = $hash->{helper}{MOWING}{cnt}; my $k = 0; my @xyarr = @{$hash->{helper}{areapos}};# areapos @@ -984,19 +1004,18 @@ sub AreaStatistics { my $vm = 0; for ( $k = 0; $k <= $i-1; $k++) { - $lsum += ((($xyarr[ $k ]{longitude} - $xyarr[ $k+1 ]{longitude}) * $sclon)**2 + (($xyarr[ $k ]{latitude} - $xyarr[ $k+1 ]{latitude}) * $sclat)**2)**0.5; + $lsum += ((($xyarr[ $k ]{longitude} - $xyarr[ $k+1 ]{longitude}) * $sclon)**2 + (($xyarr[ $k ]{latitude} - $xyarr[ $k+1 ]{latitude}) * $sclat)**2)**0.5; # m } - $asum = $lsum * AttrVal($name,'mowerCuttingWidth',0.24); + $asum = $lsum * AttrVal($name,'mowerCuttingWidth',0.24); # qm my $td = $xyarr[ 0 ]{storedTimestamp} - $xyarr[ $k ]{storedTimestamp}; - $vm = int($lsum / $td * 1000000 + 0.5)/1000 if ($td); - $lsum += int( ReadingsNum( $name, 'statistics_currentDayTrack', 0 ) ); - $asum += int( ReadingsNum( $name, 'statistics_currentDayArea', 0 ) ); - readingsBeginUpdate($hash); - readingsBulkUpdateIfChanged($hash,'statistics_currentDayTrack', int($lsum)); # m - readingsBulkUpdateIfChanged($hash,'statistics_currentDayArea', int($asum)); # qm - readingsBulkUpdateIfChanged($hash,'statistics_lastIntervalMowerSpeed', $vm); # m/s - readingsBulkUpdateIfChanged($hash,'statistics_lastIntervalNumberOfWayPoints', $i-1); # m/s - readingsEndUpdate($hash,1); + $vm = sprintf( '%.6f', $lsum / $td ) * 1000 if ($td); # m/s + $hash->{helper}{$activity}{speed} = $vm; + $hash->{helper}{$activity}{track} = $lsum; + $hash->{helper}{$activity}{area} = $asum; + $hash->{helper}{statistics}{currentSpeed} = $vm; + $hash->{helper}{statistics}{currentDayTrack} += $lsum; + $hash->{helper}{statistics}{currentDayArea} += $asum; + $hash->{helper}{statistics}{currentSpeed} = $vm; return undef; } @@ -1096,6 +1115,89 @@ sub posMinMax { return undef; } +######################### +sub listStatisticsData { + my ( $hash ) = @_; + my $name = $hash->{NAME}; + my $cnt = 0; + my $ret = ''; + $ret .= ''; + $ret .= ''; + + $ret .= ''; + $cnt++;$ret .= ''; + $cnt++;$ret .= ''; + $cnt++;$ret .= ''; + $cnt++;$ret .= ''; + $cnt++;$ret .= ''; + $cnt++;$ret .= ''; + + $cnt++;$ret .= ''; + $cnt++;$ret .= ''; + $cnt++;$ret .= ''; + $cnt++;$ret .= ''; + $cnt++;$ret .= ''; + $cnt++;$ret .= ''; + $cnt++;$ret .= ''; + $cnt++;$ret .= ''; + $cnt++;$ret .= ''; + + $ret .= '
Statistics Data
Hash Path Value Unit
$hash->{helper}{mower}{attributes}{statistics}{numberOfChargingCycles}   ' . $hash->{helper}{mower}{attributes}{statistics}{numberOfChargingCycles} . '
$hash->{helper}{mower}{attributes}{statistics}{numberOfCollisions}   ' . $hash->{helper}{mower}{attributes}{statistics}{numberOfCollisions} . '
$hash->{helper}{mower}{attributes}{statistics}{totalChargingTime}   ' . $hash->{helper}{mower}{attributes}{statistics}{totalChargingTime} . ' s
$hash->{helper}{mower}{attributes}{statistics}{totalCuttingTime}   ' . $hash->{helper}{mower}{attributes}{statistics}{totalCuttingTime} . ' s
$hash->{helper}{mower}{attributes}{statistics}{totalRunningTime}   ' . $hash->{helper}{mower}{attributes}{statistics}{totalRunningTime} . '1 s
$hash->{helper}{mower}{attributes}{statistics}{totalSearchingTime}   ' . $hash->{helper}{mower}{attributes}{statistics}{totalSearchingTime} . ' s
$hash->{helper}{statistics}{currentSpeed}   ' . $hash->{helper}{statistics}{currentSpeed} . ' m/s
$hash->{helper}{statistics}{currentDayTrack}   ' . $hash->{helper}{statistics}{currentDayTrack} . ' m
$hash->{helper}{statistics}{currentDayArea}   ' . $hash->{helper}{statistics}{currentDayArea} . ' qm
$hash->{helper}{statistics}{lastDayTrack}   ' . $hash->{helper}{statistics}{lastDayTrack} . ' m
$hash->{helper}{statistics}{lastDayArea}   ' . $hash->{helper}{statistics}{lastDayArea} . ' qm
$hash->{helper}{statistics}{currentWeekTrack}   ' . $hash->{helper}{statistics}{currentWeekTrack} . ' m
$hash->{helper}{statistics}{currentWeekArea}   ' . $hash->{helper}{statistics}{currentWeekArea} . ' qm
$hash->{helper}{statistics}{lastWeekTrack}   ' . $hash->{helper}{statistics}{lastWeekTrack} . ' m
$hash->{helper}{statistics}{lastWeekArea}   ' . $hash->{helper}{statistics}{lastWeekArea} . ' qm
'; + $ret .= '

1 totalRunningTime = totalCuttingTime + totalSearchingTime'; + $ret .= ''; + + return $ret; +} + +######################### +sub listMowerData { + my ( $hash ) = @_; + my $name = $hash->{NAME}; + my $cnt = 0; + my $ret = ''; + $ret .= ''; + $ret .= ''; + + $ret .= ''; + $cnt++;$ret .= ''; + $cnt++;$ret .= ''; + $cnt++;$ret .= ''; + $cnt++;$ret .= ''; + $cnt++;$ret .= ''; + $cnt++;$ret .= ''; + $cnt++;$ret .= ''; + $cnt++;$ret .= ''; + $cnt++;$ret .= ''; + $cnt++;$ret .= ''; + $cnt++;$ret .= ''; + + my $calendarjson = eval { JSON::XS->new->pretty(1)->encode ($hash->{helper}{mower}{attributes}{calendar}{tasks}) }; + + $cnt++;$ret .= ''; + $cnt++;$ret .= ''; + $cnt++;$ret .= ''; + $cnt++;$ret .= ''; + $cnt++;$ret .= ''; + $cnt++;$ret .= ''; + $cnt++;$ret .= ''; + $cnt++;$ret .= ''; + $cnt++;$ret .= ''; + $cnt++;$ret .= ''; +# $cnt++;$ret .= ''; + $cnt++;$ret .= ''; + $cnt++;$ret .= ''; + $cnt++;$ret .= ''; + $cnt++;$ret .= ''; + $cnt++;$ret .= ''; + $cnt++;$ret .= ''; + + $ret .= '
Mower Data
Hash Path Value Unit
$hash->{helper}{mower}{type}   ' . $hash->{helper}{mower}{type} . '
$hash->{helper}{mower}{id}   ' . $hash->{helper}{mower}{id} . '
$hash->{helper}{mower}{attributes}{system}{name}   ' . $hash->{helper}{mower}{attributes}{system}{name} . '
$hash->{helper}{mower}{attributes}{system}{model}   ' . $hash->{helper}{mower}{attributes}{system}{model} . '
$hash->{helper}{mower}{attributes}{system}{serialNumber}   ' . $hash->{helper}{mower}{attributes}{system}{serialNumber} . '
$hash->{helper}{mower}{attributes}{battery}{batteryPercent}   ' . $hash->{helper}{mower}{attributes}{battery}{batteryPercent} . ' %
$hash->{helper}{mower}{attributes}{mower}{mode}   ' . $hash->{helper}{mower}{attributes}{mower}{mode} . '
$hash->{helper}{mower}{attributes}{mower}{activity}   ' . $hash->{helper}{mower}{attributes}{mower}{activity} . '
$hash->{helper}{mower}{attributes}{mower}{state}   ' . $hash->{helper}{mower}{attributes}{mower}{state} . '
$hash->{helper}{mower}{attributes}{mower}{errorCode}   ' . $hash->{helper}{mower}{attributes}{mower}{errorCode} . '
$hash->{helper}{mower}{attributes}{mower}{errorCodeTimestamp}   ' . $hash->{helper}{mower}{attributes}{mower}{errorCodeTimestamp} . ' ms
$hash->{helper}{mower}{attributes}{calendar}{tasks}   ' . ($@ ? $@ : $calendarjson) . '
$hash->{helper}{mower}{attributes}{planner}{nextStartTimestamp}   ' . $hash->{helper}{mower}{attributes}{planner}{nextStartTimestamp} . '
$hash->{helper}{mower}{attributes}{planner}{override}{action}   ' . $hash->{helper}{mower}{attributes}{planner}{override}{action} . '
$hash->{helper}{mower}{attributes}{planner}{restrictedReason}   ' . $hash->{helper}{mower}{attributes}{planner}{restrictedReason} . '
$hash->{helper}{mower}{attributes}{metadata}{connected}   ' . $hash->{helper}{mower}{attributes}{metadata}{connected} . '
$hash->{helper}{mower}{attributes}{metadata}{statusTimestamp}   ' . $hash->{helper}{mower}{attributes}{metadata}{statusTimestamp} . ' ms
$hash->{helper}{mower}{attributes}{positions}[0]{longitude}   ' . $hash->{helper}{mower}{attributes}{positions}[0]{longitude} . ' decimal degree
$hash->{helper}{mower}{attributes}{positions}[0]{latitude}   ' . $hash->{helper}{mower}{attributes}{positions}[0]{latitude} . ' decimal degree
$hash->{helper}{mower}{attributes}{settings}{cuttingHeight}   ' . $hash->{helper}{mower}{attributes}{settings}{cuttingHeight} . '
$hash->{helper}{mower}{attributes}{settings}{headlight}{mode}   ' . $hash->{helper}{mower}{attributes}{settings}{headlight}{mode} . '
$hash->{helper}{mower}{attributes}{statistics}{cuttingBladeUsageTime}   ' . $hash->{helper}{mower}{attributes}{statistics}{cuttingBladeUsageTime} . '
$hash->{helper}{mower}{attributes}{statistics}{numberOfChargingCycles}   ' . $hash->{helper}{mower}{attributes}{statistics}{numberOfChargingCycles} . '
$hash->{helper}{mower}{attributes}{statistics}{numberOfCollisions}   ' . $hash->{helper}{mower}{attributes}{statistics}{numberOfCollisions} . '
$hash->{helper}{mower}{attributes}{statistics}{totalChargingTime}   ' . $hash->{helper}{mower}{attributes}{statistics}{totalChargingTime} . ' s
$hash->{helper}{mower}{attributes}{statistics}{totalCuttingTime}   ' . $hash->{helper}{mower}{attributes}{statistics}{totalCuttingTime} . ' s
$hash->{helper}{mower}{attributes}{statistics}{totalRunningTime}   ' . $hash->{helper}{mower}{attributes}{statistics}{totalRunningTime} . '1 s
$hash->{helper}{mower}{attributes}{statistics}{totalSearchingTime}   ' . $hash->{helper}{mower}{attributes}{statistics}{totalSearchingTime} . ' s
'; + $ret .= '

1 totalRunningTime = totalCuttingTime + totalSearchingTime'; + $ret .= ''; + + return $ret; +} + ######################### sub listInternalData { my ( $hash ) = @_; @@ -1233,34 +1335,43 @@ sub listErrorCodes {

@@ -1463,34 +1566,44 @@ sub listErrorCodes {
@@ -1614,39 +1737,21 @@ sub listErrorCodes {
  • mower_errorCode - last error code
  • mower_errorCodeTimestamp - last error code time stamp
  • mower_errorDescription - error description
  • -
  • mower_id - ID des Automowers
  • mower_mode - aktueller Arbeitsmodus "MAIN_AREA" | "SECONDARY_AREA" | "HOME" | "DEMO" | "UNKNOWN"
  • mower_state - aktueller Status "UNKNOWN" | "NOT_APPLICABLE" | "PAUSED" | "IN_OPERATION" | "WAIT_UPDATING" | "WAIT_POWER_UP" | "RESTRICTED" | "OFF" | "STOPPED" | "ERROR" | "FATAL_ERROR" |"ERROR_AT_POWER_UP"
  • planner_nextStart - nächste Startzeit
  • planner_restrictedReason - Grund für Parken NONE, WEEK_SCHEDULE, PARK_OVERRIDE, SENSOR, DAILY_LIMIT, FOTA, FROST
  • planner_overrideAction - Grund für vorrangige Aktion NOT_ACTIVE, FORCE_PARK, FORCE_MOW
  • -
  • positions_lastLonLat - letzte bekannte Position (Längengrad Breitengrad)
  • state - Status der Verbindung des FHEM-Gerätes zur Husqvarna Cloud API (defined, connected, error).
  • 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_numberOfChargingCycles - Anzahl der Ladezyklen
  • statistics_numberOfCollisions - Anzahl der Kollisionen
  • -
  • statistics_totalChargingTime - Gesamtladezeit in Stunden
  • -
  • statistics_totalCuttingTime - Gesamtschneidezeit in Stunden
  • -
  • statistics_totalRunningTime - Gesamtlaufzeit in Stunden
  • -
  • statistics_totalSearchingTime - Gesamtsuchzeit in Stunden
  • -
  • statistics_currentDayTrack - berechnete gefahrene Strecke in Meter bei_Activity MOWING seit Mitternacht
  • -
  • statistics_currentDayArea - berechnete übermähte Fläche in Quadratmeter bei der Activity MOWING seit Mitternacht
  • -
  • statistics_lastIntervalNumberOfWayPoints - Anzahl der Wegpunkte im letzten Interval
  • -
  • statistics_currentMowerSpeed - berechnet Geschwindigkeit in Meter pro Sekunde bei der_Activity MOWING im letzten Interval
  • -
  • statistics_lastDayTrack - berechnete gefahrene Strecke in Meter bei_Activity MOWING des letzten Tages
  • -
  • statistics_lastDayArea - berechnete übermähte Fläche in Quadratmeter bei der Activity MOWING des letzten Tages
  • -
  • statistics_currentWeekTrack - berechnete gefahrene Strecke in Meter bei_Activity MOWING
  • -
  • statistics_currentWeekArea - berechnete übermähte Fläche in Quadratmeter bei der Activity MOWING der laufenden Woche
  • -
  • statistics_lastWeekTrack - berechnete gefahrene Strecke in Meter bei_Activity MOWING der letzten Woche
  • -
  • statistics_lastWeekArea - berechnete übermähte Fläche in Quadratmeter bei der Activity MOWING der letzten Woche
  • status_connected - Status der Verbindung zwischen dem Automower und der Husqvarna Cloud, (1 => true, 0 => false)
  • status_statusTimestamp - Lokalzeit der letzten Änderung der Daten in der API
  • status_statusTimestampDiff - Zeitdifferenz zwichen den beiden letzten Änderungen im Inhalt der Daten aus der API
  • status_statusTimestampOld - Lokalzeit der vorletzten Änderung der Daten in der API
  • system_name - Name des Automowers
  • -
  • system_serialNumber - Seriennummer des Automowers