2
0
mirror of https://github.com/fhem/fhem-mirror.git synced 2025-02-25 09:55:38 +00:00

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
This commit is contained in:
Ellert 2023-07-14 11:34:00 +00:00
parent 6e0afc46fc
commit 3fc28e47db
4 changed files with 177 additions and 95 deletions
fhem
CHANGED
FHEM
lib/FHEM/Devices/AMConnect
www/pgm2

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

@ -378,7 +378,7 @@ __END__
<li><a id='AutomowerConnect-attr-addPollingMinInterval'>addPollingMinInterval</a><br>
<code>attr &lt;name&gt; addPollingMinInterval &lt;interval in seconds&gt;</code><br>
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).</li>
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).</li>
<li><a id='AutomowerConnect-attr-addPositionPolling'>addPositionPolling</a><br>
<code>attr &lt;name&gt; addPositionPolling &lt;[1|<b>0</b>]&gt;</code><br>
@ -741,7 +741,7 @@ __END__
<li><a id='AutomowerConnect-attr-addPollingMinInterval'>addPollingMinInterval</a><br>
<code>attr &lt;name&gt; addPollingMinInterval &lt;interval in seconds&gt;</code><br>
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.</li>
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.</li>
<li><a id='AutomowerConnect-attr-addPositionPolling'>addPositionPolling</a><br>
<code>attr &lt;name&gt; addPositionPolling &lt;[1|<b>0</b>]&gt;</code><br>

@ -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 .= '<tr class="column '.( $cnt++ % 2 ? 'odd' : 'even' ).'"><td> $hash->{helper}{mower}{attributes}{statistics}{<b>totalRunningTime</b>} &emsp;</td><td> ' . sprintf( "%.0f", $hash->{helper}{mower}{attributes}{statistics}{totalRunningTime} / 3600 ) . '<sup>1</sup> </td><td> h </td></tr>';
$ret .= '<tr class="column '.( $cnt++ % 2 ? 'odd' : 'even' ).'"><td> $hash->{helper}{mower}{attributes}{statistics}{<b>totalSearchingTime</b>} &emsp;</td><td> ' . sprintf( "%.0f", $hash->{helper}{mower}{attributes}{statistics}{totalSearchingTime} / 3600 ) . ' </td><td> h </td></tr>';
$ret .= '<tr class="column '.( $cnt++ % 2 ? 'odd' : 'even' ).'"><td> $hash->{helper}{statistics}{<b>currentDayTrack</b>} &emsp;</td><td> ' . sprintf( "%.0f", $hash->{helper}{statistics}{currentDayTrack} ) . ' </td><td> m </td></tr>';
$ret .= '<tr class="column '.( $cnt++ % 2 ? 'odd' : 'even' ).'"><td> $hash->{helper}{statistics}{<b>currentDayArea</b>} &emsp;</td><td> ' . sprintf( "%.0f", $hash->{helper}{statistics}{currentDayArea} ) . ' </td><td> qm </td></tr>';
$ret .= '<tr class="column '.( $cnt++ % 2 ? 'odd' : 'even' ).'"><td> $hash->{helper}{statistics}{<b>currentDayTime</b>} &emsp;</td><td> ' . sprintf( "%.0f", $hash->{helper}{statistics}{currentDayTime} ) . ' </td><td> s </td></tr>';
$ret .= '<tr class="column '.( $cnt++ % 2 ? 'odd' : 'even' ).'"><td> $hash->{helper}{statistics}{<b>currentDayCollisions</b>} &emsp;</td><td> ' . sprintf( "%.0f", $hash->{helper}{statistics}{currentDayCollisions} ) . ' </td><td> </td></tr>' if ( $additional_polling );
$ret .= '<tr class="column '.( $cnt++ % 2 ? 'odd' : 'even' ).'"><td> <b>calculated speed</b> &emsp;</td><td> ' . sprintf( "%.2f", $hash->{helper}{statistics}{currentDayTrack} / $hash->{helper}{statistics}{currentDayTime} ) . ' </td><td> m/s </td></tr>' if ( $hash->{helper}{statistics}{currentDayTime} );
my $prop = '';
for my $item ( @items ) {
$ret .= '<tr class="column '.( $cnt++ % 2 ? 'odd' : 'even' ).'"><td> $hash->{helper}{statistics}{<b>lastDayTrack</b>} &emsp;</td><td> ' . sprintf( "%.0f", $hash->{helper}{statistics}{lastDayTrack} ) . ' </td><td> m </td></tr>';
$ret .= '<tr class="column '.( $cnt++ % 2 ? 'odd' : 'even' ).'"><td> $hash->{helper}{statistics}{<b>lastDayArea</b>} &emsp;</td><td> ' . sprintf( "%.0f", $hash->{helper}{statistics}{lastDayArea} ) . ' </td><td> qm </td></tr>';
$ret .= '<tr class="column '.( $cnt++ % 2 ? 'odd' : 'even' ).'"><td> $hash->{helper}{statistics}{<b>lastDayTime</b>} &emsp;</td><td> ' . sprintf( "%.0f", $hash->{helper}{statistics}{lastDayTime} ) . ' </td><td> s </td></tr>';
$ret .= '<tr class="column '.( $cnt++ % 2 ? 'odd' : 'even' ).'"><td> $hash->{helper}{statistics}{<b>lastDayCollisions</b>} &emsp;</td><td> ' . sprintf( "%.0f", $hash->{helper}{statistics}{lastDayCollisions} ) . ' </td><td> </td></tr>';
$ret .= '<tr class="column '.( $cnt++ % 2 ? 'odd' : 'even' ).'"><td> <b>last day calculated speed</b> &emsp;</td><td> ' . sprintf( "%.2f", $hash->{helper}{statistics}{lastDayTrack} / $hash->{helper}{statistics}{lastDayTime} ) . ' </td><td> m/s </td></tr>' if ( $hash->{helper}{statistics}{lastDayTime} );
for $prop ( @props ) {
$ret .= '<tr class="column '.( $cnt++ % 2 ? 'odd' : 'even' ).'"><td> $hash->{helper}{statistics}{<b>'. $item . $prop . '</b>} &emsp;</td><td> ' . sprintf( "%.0f", ( $hash->{helper}{statistics}{$item.$prop} ? $hash->{helper}{statistics}{$item.$prop} : 0 ) ) . ' </td><td> ' . $unit{$prop} . ' </td></tr>' if ( $item.$prop ne 'currentDayCollision' or $additional_polling );
}
$ret .= '<tr class="column '.( $cnt++ % 2 ? 'odd' : 'even' ).'"><td> <b>'. $item . ' calculated speed</b> &emsp;</td><td> ' . sprintf( "%.2f", $hash->{helper}{statistics}{$item.'Track'} / $hash->{helper}{statistics}{$item.'Time'} ) . ' </td><td> m/s </td></tr>' if ( $hash->{helper}{statistics}{$item.'Time'} );
}
$ret .= '<tr class="column '.( $cnt++ % 2 ? 'odd' : 'even' ).'"><td> $hash->{helper}{statistics}{<b>currentWeekTrack</b>} &emsp;</td><td> ' . sprintf( "%.0f", $hash->{helper}{statistics}{currentWeekTrack} ) . ' </td><td> m </td></tr>';
$ret .= '<tr class="column '.( $cnt++ % 2 ? 'odd' : 'even' ).'"><td> $hash->{helper}{statistics}{<b>currentWeekArea</b>} &emsp;</td><td> ' . sprintf( "%.0f", $hash->{helper}{statistics}{currentWeekArea} ) . ' </td><td> qm </td></tr>';
$ret .= '<tr class="column '.( $cnt++ % 2 ? 'odd' : 'even' ).'"><td> $hash->{helper}{statistics}{<b>currentWeekTime</b>} &emsp;</td><td> ' . sprintf( "%.0f", $hash->{helper}{statistics}{currentWeekTime} ) . ' </td><td> s </td></tr>';
$ret .= '<tr class="column '.( $cnt++ % 2 ? 'odd' : 'even' ).'"><td> $hash->{helper}{statistics}{<b>lastWeekTrack</b>} &emsp;</td><td> ' . sprintf( "%.0f", $hash->{helper}{statistics}{lastWeekTrack} ) . ' </td><td> m </td></tr>';
$ret .= '<tr class="column '.( $cnt++ % 2 ? 'odd' : 'even' ).'"><td> $hash->{helper}{statistics}{<b>lastWeekArea</b>} &emsp;</td><td> ' . sprintf( "%.0f", $hash->{helper}{statistics}{lastWeekArea} ) . ' </td><td> qm </td></tr>';
$ret .= '<tr class="column '.( $cnt++ % 2 ? 'odd' : 'even' ).'"><td> $hash->{helper}{statistics}{<b>lastWeekTime</b>} &emsp;</td><td> ' . sprintf( "%.0f", $hash->{helper}{statistics}{lastWeekTime} ) . ' </td><td> s </td></tr>';
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 .= '<tr class="column '.( $cnt++ % 2 ? 'odd' : 'even' ) . '"><td> $hash->{helper}{mapZones}{' . $_ . '}{<b>currentDayCollisions</b>} &emsp;</td><td> ' . ( $hash->{helper}{mapZones}{$_}{currentDayCollisions} ? $hash->{helper}{mapZones}{$_}{currentDayCollisions} : '' ) . ' </td><td> </td></tr>';
for ( @zonekeys ) {
if ($prop eq 'Track') {
$ret .= '<tr class="column '.( $cnt++ % 2 ? 'odd' : 'even' ).'"><td> <b> '. $item . ' calculated speed for '. $_ . '</b> &emsp;</td><td> ' . sprintf( "%.2f", $hash->{helper}{mapZones}{$_}{$item.'Track'} / $hash->{helper}{mapZones}{$_}{$item.'Time'} ) . ' </td><td> m/s </td></tr>' if ( $hash->{helper}{mapZones}{$_}{$item.'Time'} );
} else {
$ret .= '<tr class="column '.( $cnt++ % 2 ? 'odd' : 'even' ) . '"><td> $hash->{helper}{mapZones}{' . $_ . '}{<b>'. $item . $prop . '</b>} &emsp;</td><td> ' . ( $hash->{helper}{mapZones}{$_}{$item.$prop} ? $hash->{helper}{mapZones}{$_}{$item.$prop} : '' ) . ' </td><td> ' . $unit{$prop} . ' </td></tr>';
}
}
}
}
for ( @zonekeys ) {
$ret .= '<tr class="column '.( $cnt++ % 2 ? 'odd' : 'even' ) . '"><td> $hash->{helper}{mapZones}{' . $_ . '}{<b>currentDayCntPct</b>} &emsp;</td><td> ' . ( $hash->{helper}{mapZones}{$_}{currentDayCntPct} ? $hash->{helper}{mapZones}{$_}{currentDayCntPct} : '' ) . ' </td><td> % </td></tr>';
}
for ( @zonekeys ) {
$ret .= '<tr class="column '.( $cnt++ % 2 ? 'odd' : 'even' ) . '"><td> $hash->{helper}{mapZones}{' . $_ . '}{<b>lastDayCntPct</b>} &emsp;</td><td> ' . ( $hash->{helper}{mapZones}{$_}{lastDayCntPct} ? $hash->{helper}{mapZones}{$_}{lastDayCntPct} : '' ) . ' </td><td> % </td></tr>';
}
for ( @zonekeys ) {
$ret .= '<tr class="column '.( $cnt++ % 2 ? 'odd' : 'even' ) . '"><td> $hash->{helper}{mapZones}{' . $_ . '}{<b>currentWeekCntPct</b>} &emsp;</td><td> ' . ( $hash->{helper}{mapZones}{$_}{currentWeekCntPct} ? $hash->{helper}{mapZones}{$_}{currentWeekCntPct} : '' ) . ' </td><td> % </td></tr>';
}
for ( @zonekeys ) {
$ret .= '<tr class="column '.( $cnt++ % 2 ? 'odd' : 'even' ) . '"><td> $hash->{helper}{mapZones}{' . $_ . '}{<b>lastWeekCntPct</b>} &emsp;</td><td> ' . ( $hash->{helper}{mapZones}{$_}{lastWeekCntPct} ? $hash->{helper}{mapZones}{$_}{lastWeekCntPct} : '' ). ' </td><td> % </td></tr>';
}
for ( @zonekeys ) {
$ret .= '<tr class="column '.( $cnt++ % 2 ? 'odd' : 'even' ) . '"><td> $hash->{helper}{mapZones}{' . $_ . '}{<b>currentDayAreaPct</b>} &emsp;</td><td> ' . ( $hash->{helper}{mapZones}{$_}{currentDayAreaPct} ? $hash->{helper}{mapZones}{$_}{currentDayAreaPct} : '' ) . ' </td><td> % </td></tr>';
}
for ( @zonekeys ) {
$ret .= '<tr class="column '.( $cnt++ % 2 ? 'odd' : 'even' ) . '"><td> $hash->{helper}{mapZones}{' . $_ . '}{<b>lastDayAreaPct</b>} &emsp;</td><td> ' . ( $hash->{helper}{mapZones}{$_}{lastDayAreaPct} ? $hash->{helper}{mapZones}{$_}{lastDayAreaPct} : '' ) . ' </td><td> % </td></tr>';
}
for ( @zonekeys ) {
$ret .= '<tr class="column '.( $cnt++ % 2 ? 'odd' : 'even' ) . '"><td> $hash->{helper}{mapZones}{' . $_ . '}{<b>currentWeekAreaPct</b>} &emsp;</td><td> ' . ( $hash->{helper}{mapZones}{$_}{currentWeekAreaPct} ? $hash->{helper}{mapZones}{$_}{currentWeekAreaPct} : '' ) . ' </td><td> % </td></tr>';
}
for ( @zonekeys ) {
$ret .= '<tr class="column '.( $cnt++ % 2 ? 'odd' : 'even' ) . '"><td> $hash->{helper}{mapZones}{' . $_ . '}{<b>lastWeekAreaPct</b>} &emsp;</td><td> ' . ( $hash->{helper}{mapZones}{$_}{lastWeekAreaPct} ? $hash->{helper}{mapZones}{$_}{lastWeekAreaPct} : '' ). ' </td><td> % </td></tr>';
}
}
$ret .= '</tbody></table>';
@ -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 );

@ -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();