From 396b0765774717a4dcf70732a173f9be777577b9 Mon Sep 17 00:00:00 2001 From: Ellert <> Date: Thu, 29 Jun 2023 22:36:38 +0000 Subject: [PATCH] 74_AutomowerConnect: Common.pm Cref update, add Log after WS filter, add Collisions to statistics and zones if addPollingMinInterval is set, some code cleanup git-svn-id: https://svn.fhem.de/fhem/trunk@27715 2b470e98-0d58-463d-a4d8-8e2adae1ed80 --- fhem/CHANGED | 2 + fhem/FHEM/74_AutomowerConnect.pm | 4 +- fhem/lib/FHEM/Devices/AMConnect/Common.pm | 281 ++++++++++++---------- 3 files changed, 153 insertions(+), 134 deletions(-) diff --git a/fhem/CHANGED b/fhem/CHANGED index ad3f83403..ff560dbdc 100644 --- a/fhem/CHANGED +++ b/fhem/CHANGED @@ -1,5 +1,7 @@ # 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: Common.pm Cref update, add Log after WS filter + add Collisions to statistics and zones if addPollingMinInterval is set - feature: 74_AutomowerConnect: Common.pm, prevent DevIo from directly setting STATE, prepare automowerconnect.js for use with FTUI 2 - bugfix: 74_AutomowerConnect: Common.pm, automowerconnect.js, fix problem diff --git a/fhem/FHEM/74_AutomowerConnect.pm b/fhem/FHEM/74_AutomowerConnect.pm index 874ba1ff7..a13d99b98 100644 --- a/fhem/FHEM/74_AutomowerConnect.pm +++ b/fhem/FHEM/74_AutomowerConnect.pm @@ -229,7 +229,7 @@ __END__
  • StatisticsData
    get <name> StatisticsData
    - Lists statistics data with its hash path. The hash path can be used for generating userReadings. The trigger is device_state: connected.
  • + Lists statistics data with its hash path. The hash path can be used for generating userReadings. The trigger is e.g. device_state: connected or mower_wsEvent: <status-event|positions-event|settings-event>.
  • errorCodes
    get <name> errorCodes
    @@ -592,7 +592,7 @@ __END__
  • StatisticsData
    get <name> StatisticsData
    - Listet statistische Daten mit ihrem Hashpfad auf. Der Hashpfad kann zur Erzeugung von userReadings genutzt werden, getriggert wird durch device_state: connected
  • + Listet statistische Daten mit ihrem Hashpfad auf. Der Hashpfad kann zur Erzeugung von userReadings genutzt werden, getriggert wird z.B. durch device_state: connected oder mower_wsEvent: <status-event|positions-event|settings-event>
  • errorStack
    get <name> errorStack
    diff --git a/fhem/lib/FHEM/Devices/AMConnect/Common.pm b/fhem/lib/FHEM/Devices/AMConnect/Common.pm index 82b55b686..ccd2495ef 100644 --- a/fhem/lib/FHEM/Devices/AMConnect/Common.pm +++ b/fhem/lib/FHEM/Devices/AMConnect/Common.pm @@ -200,6 +200,7 @@ my $mapZonesTpl = '{ mapZonesTpl => $mapZonesTpl, posMinMax => "-180 90\n180 -90", newdatasets => 0, + newcollisions => 0, newzonedatasets => 0, positionsTime => 0, storesum => 0, @@ -276,6 +277,7 @@ my $mapZonesTpl = '{ currentDayTrack => 0, currentDayArea => 0, currentDayTime => 0, + currentDayCollisions => 0, lastDayTrack => 0, lastDayArea => 0, lastDaytime => 0, @@ -1758,6 +1760,7 @@ sub ZoneHandling { $hash->{helper}{mapZones}{$_}{currentDayAreaPct} = ( $sumDayArea ? sprintf( "%.0f", $hash->{helper}{mapZones}{$_}{zoneLength} / $sumDayArea * 100 ) : 0 ); } @zonekeys; + $hash->{helper}{mapZones}{$hash->{helper}{currentZone}}{currentDayCollisions} += $hash->{helper}{newcollisions}; $hash->{helper}{newzonedatasets} = $cnt; } @@ -1823,6 +1826,8 @@ sub AreaStatistics { my $lsum = calcPathLength( $hash, 0, $i ); my $asum = 0; my $atim = 0; + my $acol = $hash->{helper}{mower}{attributes}{statistics}{numberOfCollisions} - $hash->{helper}{mowerold}{attributes}{statistics}{numberOfCollisions}; + $hash->{helper}{newcollisions} = $acol - $hash->{helper}{statistics}{currentDayCollisions}; $asum = $lsum * AttrVal($name,'mowerCuttingWidth',0.24); $atim = $i*30; # seconds @@ -1832,6 +1837,7 @@ sub AreaStatistics { $hash->{helper}{statistics}{currentDayTrack} += $lsum; $hash->{helper}{statistics}{currentDayArea} += $asum; $hash->{helper}{statistics}{currentDayTime} += $atim; + $hash->{helper}{statistics}{currentDayCollisions} = $acol; return undef; } @@ -2019,7 +2025,7 @@ sub fillReadings { readingsBulkUpdateIfChanged($hash, "planner_nextStart", $tstamp ? $timestamp : '-' ); $pref = 'statistics'; - my $noCol = $hash->{helper}{mower}{attributes}{$pref}{numberOfCollisions} - $hash->{helper}{mowerold}{attributes}{$pref}{numberOfCollisions}; + my $noCol = $hash->{helper}{statistics}{currentDayCollisions}; readingsBulkUpdateIfChanged( $hash, $pref."_numberOfCollisions", '(' . $noCol . '/' . $hash->{helper}{statistics}{lastDayCollisions} . '/' . $hash->{helper}{mower}{attributes}{$pref}{numberOfCollisions} . ')' ); readingsBulkUpdateIfChanged( $hash, $pref."_newGeoDataSets", $hash->{helper}{newdatasets} ); $pref = 'settings'; @@ -2119,6 +2125,7 @@ sub listStatisticsData { my ( $hash ) = @_; if ( $::init_done && $hash->{helper}{statistics} ) { + my $additional_polling = $hash->{helper}{additional_polling}; my $name = $hash->{NAME}; my $cnt = 0; my $ret = ''; @@ -2126,93 +2133,104 @@ sub listStatisticsData { $ret .= 'Statistics Data'; $ret .= ' Hash Path Value Unit '; - $cnt++;$ret .= ' $hash->{helper}{mower}{attributes}{statistics}{numberOfChargingCycles}   ' . $hash->{helper}{mower}{attributes}{statistics}{numberOfChargingCycles} . ' '; - $cnt++;$ret .= ' $hash->{helper}{mower}{attributes}{statistics}{numberOfCollisions}   ' . $hash->{helper}{mower}{attributes}{statistics}{numberOfCollisions} . ' '; - $cnt++;$ret .= ' $hash->{helper}{mower}{attributes}{statistics}{totalChargingTime}   ' . sprintf( "%.0f", $hash->{helper}{mower}{attributes}{statistics}{totalChargingTime} / 3600 ) . ' h '; - $cnt++;$ret .= ' $hash->{helper}{mower}{attributes}{statistics}{totalCuttingTime}   ' . sprintf( "%.0f", $hash->{helper}{mower}{attributes}{statistics}{totalCuttingTime} / 3600 ) . ' h '; - $cnt++;$ret .= ' $hash->{helper}{mower}{attributes}{statistics}{totalRunningTime}   ' . sprintf( "%.0f", $hash->{helper}{mower}{attributes}{statistics}{totalRunningTime} / 3600 ) . '1 h '; - $cnt++;$ret .= ' $hash->{helper}{mower}{attributes}{statistics}{totalSearchingTime}   ' . sprintf( "%.0f", $hash->{helper}{mower}{attributes}{statistics}{totalSearchingTime} / 3600 ) . ' h '; + $ret .= ' $hash->{helper}{mower}{attributes}{statistics}{numberOfChargingCycles}   ' . $hash->{helper}{mower}{attributes}{statistics}{numberOfChargingCycles} . ' '; + $ret .= ' $hash->{helper}{mower}{attributes}{statistics}{numberOfCollisions}   ' . $hash->{helper}{mower}{attributes}{statistics}{numberOfCollisions} . ' '; + $ret .= ' $hash->{helper}{mower}{attributes}{statistics}{totalChargingTime}   ' . sprintf( "%.0f", $hash->{helper}{mower}{attributes}{statistics}{totalChargingTime} / 3600 ) . ' h '; + $ret .= ' $hash->{helper}{mower}{attributes}{statistics}{totalCuttingTime}   ' . sprintf( "%.0f", $hash->{helper}{mower}{attributes}{statistics}{totalCuttingTime} / 3600 ) . ' h '; + $ret .= ' $hash->{helper}{mower}{attributes}{statistics}{totalRunningTime}   ' . sprintf( "%.0f", $hash->{helper}{mower}{attributes}{statistics}{totalRunningTime} / 3600 ) . '1 h '; + $ret .= ' $hash->{helper}{mower}{attributes}{statistics}{totalSearchingTime}   ' . sprintf( "%.0f", $hash->{helper}{mower}{attributes}{statistics}{totalSearchingTime} / 3600 ) . ' h '; - $cnt++;$ret .= ' $hash->{helper}{statistics}{currentDayTrack}   ' . sprintf( "%.0f", $hash->{helper}{statistics}{currentDayTrack} ) . ' m '; - $cnt++;$ret .= ' $hash->{helper}{statistics}{currentDayArea}   ' . sprintf( "%.0f", $hash->{helper}{statistics}{currentDayArea} ) . ' qm '; - $cnt++;$ret .= ' $hash->{helper}{statistics}{currentDayTime}   ' . sprintf( "%.0f", $hash->{helper}{statistics}{currentDayTime} ) . ' s '; - $cnt++;$ret .= ' calculated speed   ' . sprintf( "%.2f", $hash->{helper}{statistics}{currentDayTrack} / $hash->{helper}{statistics}{currentDayTime} ) . ' m/s ' if ( $hash->{helper}{statistics}{currentDayTime} ); + $ret .= ' $hash->{helper}{statistics}{currentDayTrack}   ' . sprintf( "%.0f", $hash->{helper}{statistics}{currentDayTrack} ) . ' m '; + $ret .= ' $hash->{helper}{statistics}{currentDayArea}   ' . sprintf( "%.0f", $hash->{helper}{statistics}{currentDayArea} ) . ' qm '; + $ret .= ' $hash->{helper}{statistics}{currentDayTime}   ' . sprintf( "%.0f", $hash->{helper}{statistics}{currentDayTime} ) . ' s '; + $ret .= ' $hash->{helper}{statistics}{currentDayCollisions}   ' . sprintf( "%.0f", $hash->{helper}{statistics}{currentDayCollisions} ) . ' ' if ( $additional_polling ); + $ret .= ' calculated speed   ' . sprintf( "%.2f", $hash->{helper}{statistics}{currentDayTrack} / $hash->{helper}{statistics}{currentDayTime} ) . ' m/s ' if ( $hash->{helper}{statistics}{currentDayTime} ); - $cnt++;$ret .= ' $hash->{helper}{statistics}{lastDayTrack}   ' . sprintf( "%.0f", $hash->{helper}{statistics}{lastDayTrack} ) . ' m '; - $cnt++;$ret .= ' $hash->{helper}{statistics}{lastDayArea}   ' . sprintf( "%.0f", $hash->{helper}{statistics}{lastDayArea} ) . ' qm '; - $cnt++;$ret .= ' $hash->{helper}{statistics}{lastDayTime}   ' . sprintf( "%.0f", $hash->{helper}{statistics}{lastDayTime} ) . ' s '; - $cnt++;$ret .= ' $hash->{helper}{statistics}{lastDayCollisions}   ' . sprintf( "%.0f", $hash->{helper}{statistics}{lastDayCollisions} ) . ' '; - $cnt++;$ret .= ' last day calculated speed   ' . sprintf( "%.2f", $hash->{helper}{statistics}{lastDayTrack} / $hash->{helper}{statistics}{lastDayTime} ) . ' m/s ' if ( $hash->{helper}{statistics}{lastDayTime} ); + $ret .= ' $hash->{helper}{statistics}{lastDayTrack}   ' . sprintf( "%.0f", $hash->{helper}{statistics}{lastDayTrack} ) . ' m '; + $ret .= ' $hash->{helper}{statistics}{lastDayArea}   ' . sprintf( "%.0f", $hash->{helper}{statistics}{lastDayArea} ) . ' qm '; + $ret .= ' $hash->{helper}{statistics}{lastDayTime}   ' . sprintf( "%.0f", $hash->{helper}{statistics}{lastDayTime} ) . ' s '; + $ret .= ' $hash->{helper}{statistics}{lastDayCollisions}   ' . sprintf( "%.0f", $hash->{helper}{statistics}{lastDayCollisions} ) . ' '; + $ret .= ' last day calculated speed   ' . sprintf( "%.2f", $hash->{helper}{statistics}{lastDayTrack} / $hash->{helper}{statistics}{lastDayTime} ) . ' m/s ' if ( $hash->{helper}{statistics}{lastDayTime} ); - $cnt++;$ret .= ' $hash->{helper}{statistics}{currentWeekTrack}   ' . sprintf( "%.0f", $hash->{helper}{statistics}{currentWeekTrack} ) . ' m '; - $cnt++;$ret .= ' $hash->{helper}{statistics}{currentWeekArea}   ' . sprintf( "%.0f", $hash->{helper}{statistics}{currentWeekArea} ) . ' qm '; - $cnt++;$ret .= ' $hash->{helper}{statistics}{currentWeekTime}   ' . sprintf( "%.0f", $hash->{helper}{statistics}{currentWeekTime} ) . ' s '; - $cnt++;$ret .= ' $hash->{helper}{statistics}{lastWeekTrack}   ' . sprintf( "%.0f", $hash->{helper}{statistics}{lastWeekTrack} ) . ' m '; - $cnt++;$ret .= ' $hash->{helper}{statistics}{lastWeekArea}   ' . sprintf( "%.0f", $hash->{helper}{statistics}{lastWeekArea} ) . ' qm '; - $cnt++;$ret .= ' $hash->{helper}{statistics}{lastWeekTime}   ' . sprintf( "%.0f", $hash->{helper}{statistics}{lastWeekTime} ) . ' s '; + $ret .= ' $hash->{helper}{statistics}{currentWeekTrack}   ' . sprintf( "%.0f", $hash->{helper}{statistics}{currentWeekTrack} ) . ' m '; + $ret .= ' $hash->{helper}{statistics}{currentWeekArea}   ' . sprintf( "%.0f", $hash->{helper}{statistics}{currentWeekArea} ) . ' qm '; + $ret .= ' $hash->{helper}{statistics}{currentWeekTime}   ' . sprintf( "%.0f", $hash->{helper}{statistics}{currentWeekTime} ) . ' s '; + $ret .= ' $hash->{helper}{statistics}{lastWeekTrack}   ' . sprintf( "%.0f", $hash->{helper}{statistics}{lastWeekTrack} ) . ' m '; + $ret .= ' $hash->{helper}{statistics}{lastWeekArea}   ' . sprintf( "%.0f", $hash->{helper}{statistics}{lastWeekArea} ) . ' qm '; + $ret .= ' $hash->{helper}{statistics}{lastWeekTime}   ' . sprintf( "%.0f", $hash->{helper}{statistics}{lastWeekTime} ) . ' s '; if ( AttrVal($name, 'mapZones', 0) && defined( $hash->{helper}{mapZones} ) ) { - my @zonekeys = sort (keys %{$hash->{helper}{mapZones}}); + my @zonekeys = sort (keys %{$hash->{helper}{mapZones}}); - for ( @zonekeys ) { + if ( $additional_polling ) { - $cnt++; - $ret .= ' $hash->{helper}{mapZones}{' . $_ . '}{currentDayCntPct}   ' . ( $hash->{helper}{mapZones}{$_}{currentDayCntPct} ? $hash->{helper}{mapZones}{$_}{currentDayCntPct} : '' ) . ' % '; + for ( @zonekeys ) { + + $ret .= ' $hash->{helper}{mapZones}{' . $_ . '}{currentDayCollisions}   ' . ( $hash->{helper}{mapZones}{$_}{currentDayCollisions} ? $hash->{helper}{mapZones}{$_}{currentDayCollisions} : '' ) . ' '; + + } + + } + + for ( @zonekeys ) { + + + $ret .= ' $hash->{helper}{mapZones}{' . $_ . '}{currentDayCntPct}   ' . ( $hash->{helper}{mapZones}{$_}{currentDayCntPct} ? $hash->{helper}{mapZones}{$_}{currentDayCntPct} : '' ) . ' % '; + + } + + for ( @zonekeys ) { + + + $ret .= ' $hash->{helper}{mapZones}{' . $_ . '}{lastDayCntPct}   ' . ( $hash->{helper}{mapZones}{$_}{lastDayCntPct} ? $hash->{helper}{mapZones}{$_}{lastDayCntPct} : '' ) . ' % '; + + } + + for ( @zonekeys ) { + + + $ret .= ' $hash->{helper}{mapZones}{' . $_ . '}{currentWeekCntPct}   ' . ( $hash->{helper}{mapZones}{$_}{currentWeekCntPct} ? $hash->{helper}{mapZones}{$_}{currentWeekCntPct} : '' ) . ' % '; + + } + + for ( @zonekeys ) { + + + $ret .= ' $hash->{helper}{mapZones}{' . $_ . '}{lastWeekCntPct}   ' . ( $hash->{helper}{mapZones}{$_}{lastWeekCntPct} ? $hash->{helper}{mapZones}{$_}{lastWeekCntPct} : '' ). ' % '; + + } + + for ( @zonekeys ) { + + + $ret .= ' $hash->{helper}{mapZones}{' . $_ . '}{currentDayAreaPct}   ' . ( $hash->{helper}{mapZones}{$_}{currentDayAreaPct} ? $hash->{helper}{mapZones}{$_}{currentDayAreaPct} : '' ) . ' % '; + + } + + for ( @zonekeys ) { + + + $ret .= ' $hash->{helper}{mapZones}{' . $_ . '}{lastDayAreaPct}   ' . ( $hash->{helper}{mapZones}{$_}{lastDayAreaPct} ? $hash->{helper}{mapZones}{$_}{lastDayAreaPct} : '' ) . ' % '; + + } + + for ( @zonekeys ) { + + + $ret .= ' $hash->{helper}{mapZones}{' . $_ . '}{currentWeekAreaPct}   ' . ( $hash->{helper}{mapZones}{$_}{currentWeekAreaPct} ? $hash->{helper}{mapZones}{$_}{currentWeekAreaPct} : '' ) . ' % '; + + } + + for ( @zonekeys ) { + + + $ret .= ' $hash->{helper}{mapZones}{' . $_ . '}{lastWeekAreaPct}   ' . ( $hash->{helper}{mapZones}{$_}{lastWeekAreaPct} ? $hash->{helper}{mapZones}{$_}{lastWeekAreaPct} : '' ). ' % '; + + } } - for ( @zonekeys ) { - - $cnt++; - $ret .= ' $hash->{helper}{mapZones}{' . $_ . '}{lastDayCntPct}   ' . ( $hash->{helper}{mapZones}{$_}{lastDayCntPct} ? $hash->{helper}{mapZones}{$_}{lastDayCntPct} : '' ) . ' % '; - - } - - for ( @zonekeys ) { - - $cnt++; - $ret .= ' $hash->{helper}{mapZones}{' . $_ . '}{currentWeekCntPct}   ' . ( $hash->{helper}{mapZones}{$_}{currentWeekCntPct} ? $hash->{helper}{mapZones}{$_}{currentWeekCntPct} : '' ) . ' % '; - - } - - for ( @zonekeys ) { - - $cnt++; - $ret .= ' $hash->{helper}{mapZones}{' . $_ . '}{lastWeekCntPct}   ' . ( $hash->{helper}{mapZones}{$_}{lastWeekCntPct} ? $hash->{helper}{mapZones}{$_}{lastWeekCntPct} : '' ). ' % '; - - } - - for ( @zonekeys ) { - - $cnt++; - $ret .= ' $hash->{helper}{mapZones}{' . $_ . '}{currentDayAreaPct}   ' . ( $hash->{helper}{mapZones}{$_}{currentDayAreaPct} ? $hash->{helper}{mapZones}{$_}{currentDayAreaPct} : '' ) . ' % '; - - } - - for ( @zonekeys ) { - - $cnt++; - $ret .= ' $hash->{helper}{mapZones}{' . $_ . '}{lastDayAreaPct}   ' . ( $hash->{helper}{mapZones}{$_}{lastDayAreaPct} ? $hash->{helper}{mapZones}{$_}{lastDayAreaPct} : '' ) . ' % '; - - } - - for ( @zonekeys ) { - - $cnt++; - $ret .= ' $hash->{helper}{mapZones}{' . $_ . '}{currentWeekAreaPct}   ' . ( $hash->{helper}{mapZones}{$_}{currentWeekAreaPct} ? $hash->{helper}{mapZones}{$_}{currentWeekAreaPct} : '' ) . ' % '; - - } - - for ( @zonekeys ) { - - $cnt++; - $ret .= ' $hash->{helper}{mapZones}{' . $_ . '}{lastWeekAreaPct}   ' . ( $hash->{helper}{mapZones}{$_}{lastWeekAreaPct} ? $hash->{helper}{mapZones}{$_}{lastWeekAreaPct} : '' ). ' % '; - - } - - } - $ret .= ''; $ret .= '

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

    1 totalRunningTime = totalCuttingTime + totalSearchingTime'; @@ -2298,7 +2316,7 @@ sub listErrorStack { for ( my $i = 0; $i < @{ $hash->{helper}{errorstack} }; $i++ ) { - $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} . ' '; + $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} . ' '; } @@ -2354,7 +2372,7 @@ sub listInternalData { $ret .= ' Used For Activities  Stack Name  Current Size  Max Size '; $ret .= 'PARKED_IN_CS, CHARGING  cspos  ' . $csnr . ' ' . $csnrmax . ' '; $ret .= 'ALL  areapos  ' . $arnr . ' ' . $arnrmax . ' '; - $ret .= 'NOT_APPLICABLE with error time stamp  lasterror/positions  ' . $ernr . ' - '; + $ret .= 'NOT_APPLICABLE with error time stamp  lasterror/positions  ' . $ernr . ' - '; $ret .= ''; if ( $hash->{TYPE} eq 'AutomowerConnect' ) { @@ -2362,18 +2380,18 @@ sub listInternalData { $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 .= ''; + $ret .= ''; + $ret .= ''; + $ret .= ''; + $ret .= ''; + $ret .= ''; + $ret .= ''; + $ret .= ''; + $ret .= ''; + $ret .= ''; + $ret .= ''; + $ret .= ''; + $ret .= ''; $ret .= '
    Rest API Data
    Link to APIsHusqvarna Developer
    Authentification API URL' . AUTHURL . '
    Automower Connect API URL' . APIURL . '
    Websocket IO Device name' . WSDEVICENAME . '
    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') . '
    Link to APIsHusqvarna Developer
    Authentification API URL' . AUTHURL . '
    Automower Connect API URL' . APIURL . '
    Websocket IO Device name' . WSDEVICENAME . '
    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') . '
    '; @@ -2393,7 +2411,7 @@ sub listInternalData { sub listErrorCodes { if ($::init_done) { - my $rowCount = 1; + my $rowCount = 0; my %ec = (); my $ec = \%ec; for ( keys %{$errortable} ) { @@ -2407,13 +2425,12 @@ sub listErrorCodes { $ret .= 'Mower Error Table'; for (sort keys %{$ec}) { $ret .= '{$_}; $ret .= ''; - $rowCount++; } $ret .= ''; @@ -2492,11 +2509,11 @@ sub wsRead { my $additional_polling = $hash->{helper}{additional_polling} * 1000; my $use_position_polling = $hash->{helper}{use_position_polling}; my $buf = DevIo_SimpleRead( $hash ); - return "" if ( !defined($buf) ); + return "" if ( !defined( $buf ) ); if ( $buf ) { - my $result = eval { decode_json($buf) }; + my $result = eval { decode_json( $buf ) }; if ( $@ ) { @@ -2504,21 +2521,21 @@ sub wsRead { } else { - if ( defined( $result->{type} ) ) { + if ( !defined( $result->{type} ) ) { - $hash->{helper}{wsResult}{$result->{type}} = $result; - $hash->{helper}{wsResult}{type} = $result->{type}; - - } else { - - $hash->{helper}{wsResult}{other} = $result; + $hash->{helper}{wsResult}{other} = dclone( $result ); } - if ( defined( $result->{type} && $result->{id} eq $hash->{helper}{mower_id}) ) { + if ( defined( $result->{type} && $result->{id} eq $hash->{helper}{mower_id} ) ) { + + $hash->{helper}{wsResult}{$result->{type}} = dclone( $result ); + $hash->{helper}{wsResult}{type} = $result->{type}; if ( $result->{type} eq "status-event" ) { + Log3 $name, 4, "$iam select websocket data for \$result->{id} $result->{id}, \$hash->{helper}{mower_id} $hash->{helper}{mower_id}, battery $result->{attributes}{battery}{batteryPercent}"; + $hash->{helper}{statusTime} = gettimeofday(); $hash->{helper}{mowerold}{attributes}{metadata}{statusTimestamp} = $hash->{helper}{mower}{attributes}{metadata}{statusTimestamp}; $hash->{helper}{mowerold}{attributes}{mower}{activity} = $hash->{helper}{mower}{attributes}{mower}{activity};