From dd1c08c7fafba71524ebf6109258238637295934 Mon Sep 17 00:00:00 2001 From: Ellert Date: Fri, 14 Jul 2023 11:34:00 +0000 Subject: [PATCH] 74_AutomowerConnect: Common.pm try to reconnect if ws is open but not ready, improve leaving path colorization if position polling is on, some changes in automowerconnect.js, Cref update. git-svn-id: https://svn.fhem.de/fhem/trunk@27764 2b470e98-0d58-463d-a4d8-8e2adae1ed80 --- fhem/CHANGED | 3 + fhem/FHEM/74_AutomowerConnect.pm | 4 +- fhem/lib/FHEM/Devices/AMConnect/Common.pm | 220 +++++++++++++--------- fhem/www/pgm2/automowerconnect.js | 45 +++++ 4 files changed, 177 insertions(+), 95 deletions(-) diff --git a/fhem/CHANGED b/fhem/CHANGED index 03730291f..c086f9774 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. + - change: 74_AutomowerConnect: Common.pm try to reconnect if ws is open but + not ready, improve leaving path colorization if position polling is + on, some changes in automowerconnect.js, Cref update. - new: 98_KNX_scan: implement FHEM-cmd: KNX_scan - change: 00_KNXIO.pm: moved KNX_scan perl function to 10_KNX.pm - change: 10_KNX.pm: moved KNX_scan perl function to 10_KNX.pm diff --git a/fhem/FHEM/74_AutomowerConnect.pm b/fhem/FHEM/74_AutomowerConnect.pm index a13d99b98..0f1033d7a 100644 --- a/fhem/FHEM/74_AutomowerConnect.pm +++ b/fhem/FHEM/74_AutomowerConnect.pm @@ -378,7 +378,7 @@ __END__
  • addPollingMinInterval
    attr <name> addPollingMinInterval <interval in seconds>
    - Set minimum intervall for additional polling, default 0 (no polling). Gets periodically statistics data from mower. Make sure to be within API limits (10000 calls per month).
  • + Set minimum intervall for additional polling triggered by status-event, default 0 (no polling). Gets periodically statistics data from mower. Make sure to be within API limits (10000 calls per month).
  • addPositionPolling
    attr <name> addPositionPolling <[1|0]>
    @@ -741,7 +741,7 @@ __END__
  • addPollingMinInterval
    attr <name> addPollingMinInterval <interval in seconds>
    - Setzt das Mindestintervall für zusätzliches Polling der API, default 0 (kein Polling). Liest periodisch statistische Daten vom Mäher. Es muss sichergestellt werden, das die API Begrenzung (10000 Anfragen pro Monat) eingehalten wird.
  • + Setzt das Mindestintervall für zusätzliches Polling der API nach einem status-event, default 0 (kein Polling). Liest periodisch zusätzlich statistische Daten vom Mäher. Es muss sichergestellt werden, das die API Begrenzung (10000 Anfragen pro Monat) eingehalten wird.
  • addPositionPolling
    attr <name> addPositionPolling <[1|0]>
    diff --git a/fhem/lib/FHEM/Devices/AMConnect/Common.pm b/fhem/lib/FHEM/Devices/AMConnect/Common.pm index 9e7686f0e..7c0c4d06d 100644 --- a/fhem/lib/FHEM/Devices/AMConnect/Common.pm +++ b/fhem/lib/FHEM/Devices/AMConnect/Common.pm @@ -156,7 +156,9 @@ mowingPathLineColor="#ff0000" mowingPathLineDash="6,2" mowingPathLineWidth="1" mowingPathDotWidth="2" -mowingPathUseDots=""'; +mowingPathUseDots="" +mowingPathShowCollisions="" +'; my $mapZonesTpl = '{ "01_oben" : { @@ -174,11 +176,13 @@ my $mapZonesTpl = '{ passObj => FHEM::Core::Authentication::Passwords->new($type), interval => 840, interval_ws => 7110, - interval_ping => 60, + interval_ping => 570, use_position_polling => 0, additional_polling => 0, + reverse_positions_order => 1, retry_interval_apiauth => 840, retry_interval_getmower => 840, + retry_interval_wsreopen => 2, timeout_apiauth => 5, timeout_getmower => 5, timeout_cmd => 10, @@ -742,7 +746,6 @@ sub APIAuthResponse { } - ############################################################## # # GET MOWERS @@ -818,7 +821,7 @@ sub getMowerResponseWs { my $iam = "$type $name getMowerResponseWs:"; 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', '') ); + Log3 $name, 4, "$iam response polling after status-event \$statuscode >$statuscode<, \$err >$err<, \$param->url $param->{url} \n \$data >$data<"; if( !$err && $statuscode == 200 && $data) { @@ -848,13 +851,14 @@ sub getMowerResponseWs { for ( $cnt = 0; $cnt < $poslen; $cnt++ ) { if ( $hash->{helper}{searchpos}[ 0 ]{longitude} == $result->{data}{attributes}{positions}[ $cnt ]{longitude} - && $hash->{helper}{searchpos}[ 0 ]{latitude} == $result->{data}{attributes}{positions}[ $cnt ]{latitude} ) { + && $hash->{helper}{searchpos}[ 0 ]{latitude} == $result->{data}{attributes}{positions}[ $cnt ]{latitude} || $cnt == $poslen -1) { # if nothing found take all if ( $cnt > 0 ) { my @ar; push @ar, @{ $result->{data}{attributes}{positions} }[ 0 .. $cnt-1 ]; $hash->{helper}{mower}{attributes}{positions} = dclone( \@ar ); + AlignArray( $hash ); FW_detailFn_Update ($hash); @@ -915,7 +919,7 @@ sub 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', '') ); + Log3 $name, 4, "$iam response \$statuscode >$statuscode<, \$err >$err<, \$param->url $param->{url} \n\$data >$data<"; if( !$err && $statuscode == 200 && $data) { @@ -1007,7 +1011,7 @@ sub getMowerResponse { } else { readingsSingleUpdate( $hash, 'device_state', "error statuscode $statuscode", 1 ); - Log3 $name, 1, "$iam \$statuscode [$statuscode]\n\$err [$err],\n \$data [$data] \n\$param->url $param->{url}"; + Log3 $name, 1, "$iam \$statuscode >$statuscode<, \$err >$err<, \$param->url $param->{url} \n\$data >$data<"; } @@ -1563,6 +1567,8 @@ sub AlignArray { my ($hash) = @_; my $name = $hash->{NAME}; my $use_position_polling = $hash->{helper}{use_position_polling}; + my $reverse_positions_order = $hash->{helper}{reverse_positions_order}; + my $additional_polling = $hash->{helper}{additional_polling}; my $act = $hash->{helper}{mower}{attributes}{mower}{activity}; my $actold = $hash->{helper}{mowerold}{attributes}{mower}{activity}; my $cnt = @{ $hash->{helper}{mower}{attributes}{positions} }; @@ -1573,8 +1579,9 @@ sub AlignArray { my @ar = @{ $hash->{helper}{mower}{attributes}{positions} }; my $deltaTime = $hash->{helper}{positionsTime} - $hash->{helper}{statusTime}; - # if encounter positions shortly after status event old activity is assigned to positions - if ( $cnt > 1 && $deltaTime > 0 && $deltaTime < 0.29 && !$use_position_polling) { + # if encounter positions shortly after status event old activity is assigned to positions + if ( $cnt > 1 && $deltaTime > 0 && $deltaTime < 0.29 && !$use_position_polling || + $use_position_polling && ( $actold =~ /LEAVING/ && $act eq 'MOWING' || $act =~ /GOING_HOME/ && $actold eq 'MOWING' ) ) { map { $_->{act} = $hash->{helper}{$actold}{short} } @ar; @@ -1586,8 +1593,11 @@ sub AlignArray { if ( !$use_position_polling ) { - @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 + if ( $reverse_positions_order ) { + + @ar = reverse @ar if ( $cnt > 1 ); # positions seem to be in reversed order + + } } @@ -1618,6 +1628,12 @@ sub AlignArray { } + if ( $hash->{helper}{newcollisions} && $additional_polling && $act =~ /^(MOWING)$/ ) { + + TagWayPointsAsCollision ( $hash, $cnt ); + + } + if ( AttrVal($name, 'mapZones', 0) && $act =~ /^(MOWING)$/ ) { $tmp = dclone( \@ar ); @@ -1758,6 +1774,8 @@ sub ZoneHandling { map { $hash->{helper}{mapZones}{$_}{currentDayCntPct} = ( $sumDayCnt ? sprintf( "%.0f", $hash->{helper}{mapZones}{$_}{zoneCnt} / $sumDayCnt * 100 ) : 0 ); $hash->{helper}{mapZones}{$_}{currentDayAreaPct} = ( $sumDayArea ? sprintf( "%.0f", $hash->{helper}{mapZones}{$_}{zoneLength} / $sumDayArea * 100 ) : 0 ); + $hash->{helper}{mapZones}{$_}{currentDayTrack} = $hash->{helper}{mapZones}{$_}{zoneLength}; + $hash->{helper}{mapZones}{$_}{currentDayTime} = $hash->{helper}{mapZones}{$_}{zoneCnt} * 30; } @zonekeys; $hash->{helper}{mapZones}{$hash->{helper}{currentZone}}{currentDayCollisions} += $hash->{helper}{newcollisions}; @@ -1818,6 +1836,19 @@ sub calcPathLength { return $lsum; } +######################### +sub TagWayPointsAsCollision { + my ( $hash, $i ) = @_; + my $name = $hash->{NAME}; + for ( my $k = 1; $k < ($i-1); $k++) { + + $hash->{helper}{areapos}[$k]{act} = 'K'; + + } + $hash->{helper}{areapos}[0]{act} = 'KE'; + $hash->{helper}{areapos}[$i-1]{act} = 'KS' if ($i>1); +} + ######################### sub AreaStatistics { my ( $hash, $i ) = @_; @@ -2056,9 +2087,11 @@ sub calculateStatistics { $hash->{helper}{statistics}{currentWeekTrack} += $hash->{helper}{statistics}{currentDayTrack}; $hash->{helper}{statistics}{currentWeekArea} += $hash->{helper}{statistics}{currentDayArea}; $hash->{helper}{statistics}{currentWeekTime} += $hash->{helper}{statistics}{currentDayTime}; + $hash->{helper}{statistics}{currentWeekCollisions} += $hash->{helper}{statistics}{lastDayCollisions}; $hash->{helper}{statistics}{currentDayTrack} = 0; $hash->{helper}{statistics}{currentDayArea} = 0; $hash->{helper}{statistics}{currentDayTime} = 0; + $hash->{helper}{statistics}{currentDayCollisions} = 0; if ( AttrVal($name, 'mapZones', 0) && defined( $hash->{helper}{mapZones} ) ) { @@ -2069,9 +2102,15 @@ sub calculateStatistics { $hash->{helper}{mapZones}{$_}{currentWeekCnt} += $hash->{helper}{mapZones}{$_}{zoneCnt}; $sumCurrentWeekCnt += $hash->{helper}{mapZones}{$_}{currentWeekCnt}; $hash->{helper}{mapZones}{$_}{currentWeekArea} += $hash->{helper}{mapZones}{$_}{zoneLength}; - $sumCurrentWeekArea += $hash->{helper}{mapZones}{$_}{currentWeekArea}; + $sumCurrentWeekArea += ( $hash->{helper}{mapZones}{$_}{currentWeekArea} ? $hash->{helper}{mapZones}{$_}{currentWeekArea} : 0 ); + $hash->{helper}{mapZones}{$_}{lastDayTrack} = $hash->{helper}{mapZones}{$_}{currentDayTrack}; + $hash->{helper}{mapZones}{$_}{currentWeekTrack} += ( $hash->{helper}{mapZones}{$_}{currentDayTrack} ? $hash->{helper}{mapZones}{$_}{currentDayTrack} : 0 ); + $hash->{helper}{mapZones}{$_}{lastDayTime} = ( $hash->{helper}{mapZones}{$_}{currentDayTime} ? $hash->{helper}{mapZones}{$_}{currentDayTime} : 0 ); + $hash->{helper}{mapZones}{$_}{currentWeekTime} += ( $hash->{helper}{mapZones}{$_}{currentDayTime} ? $hash->{helper}{mapZones}{$_}{currentDayTime} : 0 ); $hash->{helper}{mapZones}{$_}{zoneCnt} = 0; $hash->{helper}{mapZones}{$_}{zoneLength} = 0; + $hash->{helper}{mapZones}{$_}{currentDayTrack} = 0; + $hash->{helper}{mapZones}{$_}{currentDayTime} = 0; } @zonekeys; map { @@ -2081,6 +2120,11 @@ sub calculateStatistics { $hash->{helper}{mapZones}{$_}{currentWeekAreaPct} = ( $sumCurrentWeekArea ? sprintf( "%.0f", $hash->{helper}{mapZones}{$_}{currentWeekArea} / $sumCurrentWeekArea * 100 ) : '' ); $hash->{helper}{mapZones}{$_}{currentDayCntPct} = ''; $hash->{helper}{mapZones}{$_}{currentDayAreaPct} = ''; + if ( $hash->{helper}{additional_polling} ) { + $hash->{helper}{mapZones}{$_}{lastDayCollisions} = ( $hash->{helper}{mapZones}{$_}{currentDayCollisions} ? $hash->{helper}{mapZones}{$_}{currentDayCollisions} : 0 ); + $hash->{helper}{mapZones}{$_}{currentWeekCollisions} += ( $hash->{helper}{mapZones}{$_}{currentDayCollisions} ? $hash->{helper}{mapZones}{$_}{currentDayCollisions} : 0 ); + $hash->{helper}{mapZones}{$_}{currentDayCollisions} = 0; + } } @zonekeys; } @@ -2090,9 +2134,11 @@ sub calculateStatistics { $hash->{helper}{statistics}{lastWeekTrack} = $hash->{helper}{statistics}{currentWeekTrack}; $hash->{helper}{statistics}{lastWeekArea} = $hash->{helper}{statistics}{currentWeekArea}; $hash->{helper}{statistics}{lastWeekTime} = $hash->{helper}{statistics}{currentWeekTime}; + $hash->{helper}{statistics}{lastWeekCollisions} = $hash->{helper}{statistics}{currentWeekCollisions}; $hash->{helper}{statistics}{currentWeekTrack} = 0; $hash->{helper}{statistics}{currentWeekArea} = 0; $hash->{helper}{statistics}{currentWeekTime} = 0; + $hash->{helper}{statistics}{currentWeekCollisions} = 0; if ( AttrVal($name, 'mapZones', 0) && defined( $hash->{helper}{mapZones} ) ) { @@ -2100,8 +2146,16 @@ sub calculateStatistics { map { $hash->{helper}{mapZones}{$_}{lastWeekCntPct} = $hash->{helper}{mapZones}{$_}{currentWeekCntPct}; $hash->{helper}{mapZones}{$_}{lastWeekAreaPct} = $hash->{helper}{mapZones}{$_}{currentWeekAreaPct}; + $hash->{helper}{mapZones}{$_}{lastWeekTrack} = $hash->{helper}{mapZones}{$_}{currentWeekTrack}; + $hash->{helper}{mapZones}{$_}{lastWeekTime} = $hash->{helper}{mapZones}{$_}{currentWeekTime}; $hash->{helper}{mapZones}{$_}{currentWeekCntPct} = ''; $hash->{helper}{mapZones}{$_}{currentWeekAreaPct} = ''; + $hash->{helper}{mapZones}{$_}{currentWeekTrack} = 0; + $hash->{helper}{mapZones}{$_}{currentWeekTime} = 0; + if ( $hash->{helper}{additional_polling} ) { + $hash->{helper}{mapZones}{$_}{lastWeekCollisions} = $hash->{helper}{mapZones}{$_}{currentWeekCollisions}; + $hash->{helper}{mapZones}{$_}{currentWeekCollisions} = 0; + } } @zonekeys; } @@ -2125,6 +2179,16 @@ sub listStatisticsData { my ( $hash ) = @_; if ( $::init_done && $hash->{helper}{statistics} ) { + my %unit =( + Track => 'm', + Area => 'qm', + Time => 's', + Collisions => ' ', + CntPct => '%', + AreaPct => '%' + ); + my @props = qw(Track Area Time Collisions); + my @items = qw(currentDay lastDay currentWeek lastWeek); my $additional_polling = $hash->{helper}{additional_polling}; my $name = $hash->{NAME}; my $cnt = 0; @@ -2140,95 +2204,48 @@ sub listStatisticsData { $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 '; - $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} ); + my $prop = ''; + for my $item ( @items ) { - $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} ); + for $prop ( @props ) { + + $ret .= ' $hash->{helper}{statistics}{'. $item . $prop . '}   ' . sprintf( "%.0f", ( $hash->{helper}{statistics}{$item.$prop} ? $hash->{helper}{statistics}{$item.$prop} : 0 ) ) . ' ' . $unit{$prop} . ' ' if ( $item.$prop ne 'currentDayCollision' or $additional_polling ); + + } + + $ret .= ' '. $item . ' calculated speed   ' . sprintf( "%.2f", $hash->{helper}{statistics}{$item.'Track'} / $hash->{helper}{statistics}{$item.'Time'} ) . ' m/s ' if ( $hash->{helper}{statistics}{$item.'Time'} ); + + } - $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 @props = qw(Track CntPct AreaPct); + unshift @props, 'Collisions' if ( $additional_polling ); - if ( $additional_polling ) { + for my $prop ( @props ) { - for ( @zonekeys ) { + for my $item ( @items ) { - $ret .= ' $hash->{helper}{mapZones}{' . $_ . '}{currentDayCollisions}   ' . ( $hash->{helper}{mapZones}{$_}{currentDayCollisions} ? $hash->{helper}{mapZones}{$_}{currentDayCollisions} : '' ) . ' '; + for ( @zonekeys ) { + + if ($prop eq 'Track') { + + $ret .= ' '. $item . ' calculated speed for '. $_ . '   ' . sprintf( "%.2f", $hash->{helper}{mapZones}{$_}{$item.'Track'} / $hash->{helper}{mapZones}{$_}{$item.'Time'} ) . ' m/s ' if ( $hash->{helper}{mapZones}{$_}{$item.'Time'} ); + + } else { + + $ret .= ' $hash->{helper}{mapZones}{' . $_ . '}{'. $item . $prop . '}   ' . ( $hash->{helper}{mapZones}{$_}{$item.$prop} ? $hash->{helper}{mapZones}{$_}{$item.$prop} : '' ) . ' ' . $unit{$prop} . ' '; + + } + + } } } - 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} : '' ). ' % '; - - } - } $ret .= ''; @@ -2495,11 +2512,21 @@ sub wsReopen { RemoveInternalTimer( $hash, \&wsReopen ); RemoveInternalTimer( $hash, \&wsKeepAlive ); DevIo_CloseDev( $hash ) if ( DevIo_IsOpen( $hash ) ); - $hash->{DeviceName} = WSDEVICENAME; - DevIo_OpenDev( $hash, 0, \&wsInit, \&wsCb ); + # $hash->{DeviceName} = WSDEVICENAME; + # DevIo_OpenDev( $hash, 0, \&wsInit, \&wsCb ); + InternalTimer( gettimeofday() + $hash->{helper}{retry_interval_wsreopen}, \&wsAsyncDevIo_OpenDev, $hash, 0 ); } +######################### +sub wsAsyncDevIo_OpenDev { + my ( $hash ) = @_; + RemoveInternalTimer( $hash, \&wsAsyncDevIo_OpenDev ); + $hash->{DeviceName} = WSDEVICENAME; + $hash->{helper}{retry_interval_wsreopen} = 2; + DevIo_OpenDev( $hash, 0, \&wsInit, \&wsCb ); +} + ######################### sub wsRead { my ($hash) = @_; @@ -2510,6 +2537,7 @@ sub wsRead { my $use_position_polling = $hash->{helper}{use_position_polling}; my $buf = DevIo_SimpleRead( $hash ); return "" if ( !defined( $buf ) ); + Log3 $name, 4, "$iam received websocket data: >$buf<"; if ( $buf ) { @@ -2525,6 +2553,14 @@ sub wsRead { $hash->{helper}{wsResult}{other} = dclone( $result ); + if ( $result =~ /^{"ready":false/ ) { + + readingsSingleUpdate( $hash, 'mower_wsEvent', 'not ready', 1); + $hash->{helper}{retry_interval_wsreopen} = 420; + wsReopen($hash); + + } + } if ( defined( $result->{type} ) && $result->{id} eq $hash->{helper}{mower_id} ) { @@ -2551,13 +2587,12 @@ sub wsRead { isErrorThanPrepare( $hash ); resetLastErrorIfCorrected( $hash ); - } elsif ( ( $additional_polling < $hash->{helper}{storesum} || $additional_polling && $act eq 'LEAVING' ) && !$hash->{helper}{midnightCycle} ) { + } elsif ( ( $additional_polling < $hash->{helper}{storesum} || $additional_polling && $act =~ /^(LEAVING|GOING_HOME)/ ) && !$hash->{helper}{midnightCycle} ) { $hash->{helper}{storesum} = 0; # RemoveInternalTimer( $hash, \&getMowerWs ); # InternalTimer(gettimeofday() + 2, \&getMowerWs, $hash, 0 ); getMowerWs( $hash ); - Log3 $name, 4, "$iam received websocket data, and polling is on: >$buf<"; $hash->{First_Read} = 0; return; @@ -2577,7 +2612,6 @@ sub wsRead { } elsif ( $use_position_polling && $additional_polling ) { - Log3 $name, 4, "$iam received websocket data, but position polling is on: >$buf<"; $hash->{First_Read} = 0; return; @@ -2607,7 +2641,6 @@ sub wsRead { } - Log3 $name, 4, "$iam received websocket data: >$buf<"; $hash->{First_Read} = 0; return; @@ -2616,6 +2649,7 @@ sub wsRead { ######################### sub wsReady { my ($hash ) = @_; + RemoveInternalTimer( $hash, \&wsAsyncDevIo_OpenDev); RemoveInternalTimer( $hash, \&wsReopen); RemoveInternalTimer( $hash, \&wsKeepAlive); return DevIo_OpenDev( $hash, 1, \&wsInit, \&wsCb ); diff --git a/fhem/www/pgm2/automowerconnect.js b/fhem/www/pgm2/automowerconnect.js index 859aeab77..2b8a13e5a 100644 --- a/fhem/www/pgm2/automowerconnect.js +++ b/fhem/www/pgm2/automowerconnect.js @@ -115,6 +115,44 @@ function AutomowerConnectScale( ctx, picx, picy, scalx ) { ctx.stroke(); } +function AutomowerConnectTag( ctx, pos, colorat ) { + + for ( i = 0; i < pos.length ; i+=3 ){ + + if ( pos[ i + 2 ] == 'K' ){ + ctx.beginPath(); + ctx.setLineDash( [] ); + ctx.lineWidth=1.5; + ctx.strokeStyle = 'white'; + ctx.fillStyle= 'black'; + ctx.arc( parseInt( pos[ i ] ), parseInt( pos[ i + 1 ] ), 2, 0, 2 * Math.PI, false ); + ctx.fill(); + ctx.stroke(); + } + if ( pos[ i + 2 ] == 'KE' ){ + ctx.beginPath(); + ctx.setLineDash( [] ); + ctx.lineWidth=3; + ctx.strokeStyle = 'white'; + ctx.fillStyle= 'black'; + ctx.arc( parseInt( pos[ i ] ), parseInt( pos[ i + 1 ] ), 4, 0, 2 * Math.PI, false ); + ctx.fill(); + ctx.stroke(); + } + if ( pos[ i + 2 ] == 'KS' ){ + ctx.beginPath(); + ctx.setLineDash( [] ); + ctx.lineWidth=3; + ctx.strokeStyle = 'red'; + ctx.fillStyle= 'black'; + ctx.arc( parseInt( pos[ i ] ), parseInt( pos[ i + 1 ] ), 4, 0, 2 * Math.PI, false ); + ctx.fill(); + ctx.stroke(); + } + + } + +} function AutomowerConnectIcon( ctx, csx, csy, csrel, type ) { if (parseInt(csx) > 0 && parseInt(csy) > 0) { // draw icon @@ -302,6 +340,9 @@ function AutomowerConnectUpdateDetail (dev, type, detailfnfirst, picx, picy, sca "P" : "chargingStationPath", "C" : "chargingStationPath", "M" : "mowingPath", + "K" : "mowingPath", + "KE" : "mowingPath", + "KS" : "mowingPath", "L" : "leavingPath", "G" : "goingHomePath" }; @@ -355,6 +396,10 @@ function AutomowerConnectUpdateDetail (dev, type, detailfnfirst, picx, picy, sca } + // draw collision tag + if ( div.getAttribute( 'data-mowingPathShowCollisions' ) ) + AutomowerConnectTag( ctx, pos, colorat ); + // draw start if ( div.getAttribute( 'data-mowingPathDisplayStart' ) ) { ctx.beginPath();