mirror of
https://github.com/fhem/fhem-mirror.git
synced 2025-01-31 06:39:11 +00:00
AutomowerConnectFamily: set cutting height for user defined zones automatically, code cleaning, some fixes
git-svn-id: https://svn.fhem.de/fhem/trunk@27559 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
parent
8a2a846535
commit
6ef2b49a9f
@ -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: AutomowerConnectFamily: set cutting height for user defined zones
|
||||
automatically, code cleaning, some fixes
|
||||
- feature: 72_FRITZBOX: neue Readings:
|
||||
dect<n>_NoRingTime - Ruhezeiten des DECT Telefons <n>
|
||||
dect<n>_NoRingWithNightSetting - Bei aktiver Klingelsperre
|
||||
|
@ -44,6 +44,7 @@ BEGIN {
|
||||
qw(
|
||||
AttrVal
|
||||
CommandAttr
|
||||
CommandDeleteReading
|
||||
FmtDateTime
|
||||
getKeyValue
|
||||
InternalTimer
|
||||
@ -358,156 +359,18 @@ sub getMowerResponse {
|
||||
# Update readings
|
||||
readingsBeginUpdate($hash);
|
||||
|
||||
readingsBulkUpdateIfChanged($hash, "batteryPercent", $hash->{helper}{mower}{attributes}{battery}{batteryPercent} );
|
||||
readingsBulkUpdateIfChanged($hash, 'api_MowerFound', $foundMower );
|
||||
my $pref = 'mower';
|
||||
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} );
|
||||
readingsBulkUpdateIfChanged($hash, $pref.'_commandStatus', 'cleared' );
|
||||
readingsBulkUpdateIfChanged($hash, 'api_MowerFound', $foundMower ); # host only
|
||||
::FHEM::Devices::AMConnect::Common::fillReadings( $hash );
|
||||
|
||||
if ( AttrVal($name, 'mapZones', 0) && $hash->{helper}{currentZone} && $hash->{helper}{mapZones}{$hash->{helper}{currentZone}}{curZoneCnt} ) {
|
||||
my $curZon = $hash->{helper}{currentZone};
|
||||
my $curZonCnt = $hash->{helper}{mapZones}{$curZon}{curZoneCnt};
|
||||
readingsBulkUpdateIfChanged($hash, $pref.'_currentZone', $curZon . '(' . $curZonCnt . '/' . $hash->{helper}{newzonedatasets} . ')' );
|
||||
}
|
||||
|
||||
my $tstamp = $hash->{helper}{mower}{attributes}{$pref}{errorCodeTimestamp};
|
||||
my $timestamp = ::FHEM::Devices::AMConnect::Common::FmtDateTimeGMT($tstamp/1000);
|
||||
readingsBulkUpdateIfChanged($hash, $pref."_errorCodeTimestamp", $tstamp ? $timestamp : '-' );
|
||||
|
||||
my $errc = $hash->{helper}{mower}{attributes}{$pref}{errorCode};
|
||||
readingsBulkUpdateIfChanged($hash, $pref.'_errorCode', $tstamp ? $errc : '-');
|
||||
|
||||
my $errd = $::FHEM::Devices::AMConnect::Common::errortable->{$errc};
|
||||
readingsBulkUpdateIfChanged($hash, $pref.'_errorDescription', $tstamp ? $errd : '-');
|
||||
|
||||
$pref = 'system';
|
||||
readingsBulkUpdateIfChanged($hash, $pref."_name", $hash->{helper}{mower}{attributes}{$pref}{name} );
|
||||
my $model = $hash->{helper}{mower}{attributes}{$pref}{model};
|
||||
$model =~ s/AUTOMOWER./AM/;
|
||||
# $hash->{MODEL} = '' if (!defined $hash->{MODEL});
|
||||
$hash->{MODEL} = $model if ( $model && $hash->{MODEL} ne $model );
|
||||
$pref = 'planner';
|
||||
readingsBulkUpdateIfChanged($hash, "planner_restrictedReason", $hash->{helper}{mower}{attributes}{$pref}{restrictedReason} );
|
||||
readingsBulkUpdateIfChanged($hash, "planner_overrideAction", $hash->{helper}{mower}{attributes}{$pref}{override}{action} );
|
||||
|
||||
$tstamp = $hash->{helper}{mower}{attributes}{$pref}{nextStartTimestamp};
|
||||
$timestamp = ::FHEM::Devices::AMConnect::Common::FmtDateTimeGMT($tstamp/1000);
|
||||
readingsBulkUpdateIfChanged($hash, "planner_nextStart", $tstamp ? $timestamp : '-' );
|
||||
|
||||
$pref = 'statistics';
|
||||
readingsBulkUpdateIfChanged($hash, $pref."_numberOfCollisions", $hash->{helper}->{mower}{attributes}{$pref}{numberOfCollisions} );
|
||||
readingsBulkUpdateIfChanged($hash, $pref."_newGeoDataSets", $hash->{helper}{newdatasets} );
|
||||
$pref = 'settings';
|
||||
readingsBulkUpdateIfChanged($hash, $pref."_headlight", $hash->{helper}->{mower}{attributes}{$pref}{headlight}{mode} );
|
||||
readingsBulkUpdateIfChanged($hash, $pref."_cuttingHeight", $hash->{helper}->{mower}{attributes}{$pref}{cuttingHeight} );
|
||||
$pref = 'status';
|
||||
my $connected = $hash->{helper}{mower}{attributes}{metadata}{connected};
|
||||
readingsBulkUpdateIfChanged($hash, $pref."_connected", ( $connected ? "CONNECTED($connected)" : "OFFLINE($connected)") );
|
||||
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 ));
|
||||
readingsEndUpdate($hash, 1);
|
||||
|
||||
my @time = localtime();
|
||||
my $secs = ( $time[2] * 3600 ) + ( $time[1] * 60 ) + $time[0];
|
||||
my $interval = $hash->{helper}->{interval};
|
||||
# do at midnight
|
||||
if ( $secs <= $interval ) {
|
||||
::FHEM::Devices::AMConnect::Common::calculateStatistics( $hash );
|
||||
|
||||
$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;
|
||||
|
||||
if ( AttrVal($name, 'mapZones', 0) && defined( $hash->{helper}{mapZones} ) ) {
|
||||
|
||||
my @zonekeys = sort (keys %{$hash->{helper}{mapZones}});
|
||||
my $sumLastDayCnt=0;
|
||||
my $sumCurrentWeekCnt=0;
|
||||
my $sumLastDayArea=0;
|
||||
my $sumCurrentWeekArea=0;
|
||||
map {
|
||||
$hash->{helper}{mapZones}{$_}{lastDayCnt} = $hash->{helper}{mapZones}{$_}{zoneCnt};
|
||||
$sumLastDayCnt += $hash->{helper}{mapZones}{$_}{lastDayCnt};
|
||||
$hash->{helper}{mapZones}{$_}{currentWeekCnt} += $hash->{helper}{mapZones}{$_}{lastDayCnt};
|
||||
$sumCurrentWeekCnt += $hash->{helper}{mapZones}{$_}{currentWeekCnt};
|
||||
$hash->{helper}{mapZones}{$_}{zoneCnt} = 0;
|
||||
|
||||
$hash->{helper}{mapZones}{$_}{lastDayArea} = $hash->{helper}{mapZones}{$_}{zoneLength};
|
||||
$sumLastDayArea += $hash->{helper}{mapZones}{$_}{lastDayArea};
|
||||
$hash->{helper}{mapZones}{$_}{currentWeekArea} += $hash->{helper}{mapZones}{$_}{lastDayArea};
|
||||
$sumCurrentWeekArea += $hash->{helper}{mapZones}{$_}{currentWeekArea};
|
||||
$hash->{helper}{mapZones}{$_}{zoneLength} = 0;
|
||||
} @zonekeys;
|
||||
|
||||
map {
|
||||
$hash->{helper}{mapZones}{$_}{lastDayCntPct} = sprintf( "%.0f", $hash->{helper}{mapZones}{$_}{lastDayCnt} / $sumLastDayCnt * 100 );
|
||||
} @zonekeys if( $sumLastDayCnt );
|
||||
|
||||
map {
|
||||
$hash->{helper}{mapZones}{$_}{currentWeekCntPct} = sprintf( "%.0f", $hash->{helper}{mapZones}{$_}{currentWeekCnt} / $sumCurrentWeekCnt * 100 );
|
||||
} @zonekeys if( $sumCurrentWeekCnt );
|
||||
|
||||
map {
|
||||
$hash->{helper}{mapZones}{$_}{lastDayAreaPct} = sprintf( "%.0f", $hash->{helper}{mapZones}{$_}{lastDayArea} / $sumLastDayArea * 100 );
|
||||
} @zonekeys if( $sumLastDayArea );
|
||||
|
||||
map {
|
||||
$hash->{helper}{mapZones}{$_}{currentWeekAreaPct} = sprintf( "%.0f", $hash->{helper}{mapZones}{$_}{currentWeekArea} / $sumCurrentWeekArea * 100 );
|
||||
} @zonekeys if( $sumCurrentWeekArea );
|
||||
|
||||
}
|
||||
# do on days
|
||||
if ( $time[6] == 1 ) {
|
||||
|
||||
$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;
|
||||
|
||||
if ( AttrVal($name, 'mapZones', 0) && defined( $hash->{helper}{mapZones} ) ) {
|
||||
|
||||
my @zonekeys = sort (keys %{$hash->{helper}{mapZones}});
|
||||
my $sumLastWeekCnt=0;
|
||||
my $sumLastWeekArea=0;
|
||||
map {
|
||||
$hash->{helper}{mapZones}{$_}{lastWeekCnt} = $hash->{helper}{mapZones}{$_}{currentWeekCnt};
|
||||
$sumLastWeekCnt += $hash->{helper}{mapZones}{$_}{lastWeekCnt};
|
||||
$hash->{helper}{mapZones}{$_}{currentWeekCnt} = 0;
|
||||
$hash->{helper}{mapZones}{$_}{lastWeekArea} = $hash->{helper}{mapZones}{$_}{currentWeekArea};
|
||||
$sumLastWeekArea += $hash->{helper}{mapZones}{$_}{lastWeekArea};
|
||||
$hash->{helper}{mapZones}{$_}{currentWeekArea} = 0;
|
||||
} @zonekeys;
|
||||
|
||||
map {
|
||||
$hash->{helper}{mapZones}{$_}{lastWeekCntPct} = sprintf( "%.0f", $hash->{helper}{mapZones}{$_}{lastWeekCnt} / $sumLastWeekCnt * 100 );
|
||||
} @zonekeys if( $sumLastWeekCnt );
|
||||
|
||||
map {
|
||||
$hash->{helper}{mapZones}{$_}{lastWeekAreaPct} = sprintf( "%.0f", $hash->{helper}{mapZones}{$_}{lastWeekArea} / $sumLastWeekArea * 100 );
|
||||
} @zonekeys if( $sumLastWeekArea );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#clear position arrays
|
||||
if ( AttrVal( $name, 'weekdaysToResetWayPoints', 1 ) =~ $time[6] ) {
|
||||
|
||||
$hash->{helper}{areapos} = [];
|
||||
$hash->{helper}{otherpos} = [];
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
readingsSingleUpdate($hash, 'state', 'connected', 1 );
|
||||
|
||||
|
||||
RemoveInternalTimer( $hash, \&APIAuth );
|
||||
InternalTimer( gettimeofday() + $interval, \&APIAuth, $hash, 0 );
|
||||
|
||||
return undef;
|
||||
|
||||
}
|
||||
@ -713,14 +576,14 @@ sub Attr {
|
||||
|
||||
if( $cmd eq "set" ) {
|
||||
|
||||
return "$iam $cmd $attrName $attrVal Interval must be greater than 0, recommended 600" unless($attrVal > 0);
|
||||
return "$iam $cmd $attrName $attrVal Interval must be greater than 0, recommended 420" unless($attrVal > 0);
|
||||
$hash->{helper}->{interval} = $attrVal;
|
||||
Log3 $name, 3, "$iam $cmd $attrName $attrVal";
|
||||
|
||||
} elsif( $cmd eq "del" ) {
|
||||
|
||||
$hash->{helper}->{interval} = 600;
|
||||
Log3 $name, 3, "$iam $cmd $attrName and set default 600";
|
||||
$hash->{helper}->{interval} = 420;
|
||||
Log3 $name, 3, "$iam $cmd $attrName and set default 420";
|
||||
|
||||
}
|
||||
##########
|
||||
@ -901,6 +764,8 @@ __END__
|
||||
<li>An arbitrary map can be used as background for the mower path.</li>
|
||||
<li>The map has to be a raster image in webp, png or jpg format.</li>
|
||||
<li>It's possible to control everything the API offers, e.g. schedule, headlight, cutting height and actions like start, pause, park etc. </li>
|
||||
<li>Zones are definable. </li>
|
||||
<li>Cutting height can be set for each zone differently. </li>
|
||||
<li>All API data is stored in the device hash, the last and the second last one. Use <code>{Dumper $defs{<name>}}</code> in the commandline to find the data and build userReadings out of it.</li><br>
|
||||
</ul>
|
||||
<u><b>Limits for the Automower Connect API</b></u>
|
||||
@ -1036,7 +901,7 @@ __END__
|
||||
<ul>
|
||||
<li><a id='AutomowerConnect-attr-interval'>interval</a><br>
|
||||
<code>attr <name> interval <time in seconds></code><br>
|
||||
Time in seconds that is used to get new data from Husqvarna Cloud. Default: 600</li>
|
||||
Time in seconds that is used to get new data from Husqvarna Cloud. Default: 420</li>
|
||||
<li><a id='AutomowerConnect-attr-mapImagePath'>mapImagePath</a><br>
|
||||
<code>attr <name> mapImagePath <path to image></code><br>
|
||||
Path of a raster image file for an area the mower path has to be drawn to.<br>
|
||||
@ -1132,30 +997,38 @@ __END__
|
||||
<code>
|
||||
'{<br>
|
||||
    "<name_1>" : {<br>
|
||||
      "condition" : "<condition to separate name_1 from other zones>"<br>
|
||||
      "condition" : "<condition to separate name_1 from other zones>",<br>
|
||||
      "cuttingHeight" : "<cutting height for the first zone>"<br>
|
||||
  },<br>
|
||||
    "<name_2>" : {<br>
|
||||
      "condition" : "<condition to separate name_2 from other zones, except name_1>"<br>
|
||||
      "condition" : "<condition to separate name_2 from other zones, except name_1>",<br>
|
||||
      "cuttingHeight" : "<cutting height for the second zone>"<br>
|
||||
  },<br>
|
||||
    "<name_3>" : {<br>
|
||||
      "condition" : "<condition to separate name_3 from other zones, except name_1 and name_2>"<br>
|
||||
      "condition" : "<condition to separate name_3 from other zones, except name_1 and name_2>",<br>
|
||||
      "cuttingHeight" : "<cutting height for the third zone>"<br>
|
||||
  },<br>
|
||||
    "<name_n-1>" : {<br>
|
||||
      "condition" : "<condition to separate name_n-1 from other zones ,except the zones already seperated>"<br>
|
||||
      "condition" : "<condition to separate name_n-1 from other zones ,except the zones already seperated>",<br>
|
||||
      "cuttingHeight" : "<cutting height for the nth-1 zone>"<br>
|
||||
  },<br>
|
||||
    "<name n>" : {<br>
|
||||
      "condition" : "Use 'undef' because the last zone remains."<br>
|
||||
      "condition" : "Use 'undef' because the last zone remains.",<br>
|
||||
      "cuttingHeight" : "<cutting height for the nth zone>"<br>
|
||||
  }<br>
|
||||
}'<br>
|
||||
</code><br>
|
||||
Example with two Zones and virtual lines defined by latitude 52.6484600648553, 52.64839739580418 (horizontal) and longitude 9.54799477359984 (vertikal). all way points above 52.6484600648553 or all way points above 52.64839739580418 and all way points to the right of 9.54799477359984 belong to zone 01_oben. All other way points belong to zone 02_unten.<br>
|
||||
There are different cutting heightts each zone
|
||||
<code>
|
||||
'{<br>
|
||||
    "01_oben" : {<br>
|
||||
      "condition" : "$latitude > 52.6484600648553 || $longitude > 9.54799477359984 && $latitude > 52.64839739580418"<br>
|
||||
      "condition" : "$latitude > 52.6484600648553 || $longitude > 9.54799477359984 && $latitude > 52.64839739580418",<br>
|
||||
      "cuttingHeight" : "7"<br>
|
||||
  },<br>
|
||||
    "02_unten" : {<br>
|
||||
      "condition" : "undef"<br>
|
||||
      "condition" : "undef",<br>
|
||||
      "cuttingHeight" : "3"<br>
|
||||
  }<br>
|
||||
}'<br>
|
||||
</code></li>
|
||||
@ -1233,6 +1106,8 @@ __END__
|
||||
<li>Der Pfad kann mit einer beliebigen Karte hinterlegt werden.</li>
|
||||
<li>Die Karte muss als Rasterbild im webp, png oder jpg Format vorliegen.</li>
|
||||
<li>Es ist möglich alles was die API anbietet zu steuern, z.B. Mähplan,Scheinwerfer, Schnitthöhe und Aktionen wie, Start, Pause, Parken usw. </li>
|
||||
<li>Zonen können selbst definiert werden. </li>
|
||||
<li>Die Schnitthöhe kann je selbstdefinierter Zone eingestellt werden. </li>
|
||||
<li>Die letzten und vorletzten Daten aus der API sind im Gerätehash gespeichert, Mit <code>{Dumper $defs{<device name>}}</code> in der Befehlezeile können die Daten angezeigt werden und daraus userReadings erstellt werden.</li><br>
|
||||
</ul>
|
||||
<u><b>Limit Automower Connect API</b></u>
|
||||
@ -1366,7 +1241,7 @@ __END__
|
||||
<ul>
|
||||
<li><a id='AutomowerConnect-attr-interval'>interval</a><br>
|
||||
<code>attr <name> interval <time in seconds></code><br>
|
||||
Zeit in Sekunden nach denen neue Daten aus der Husqvarna Cloud abgerufen werden. Standard: 600</li>
|
||||
Zeit in Sekunden nach denen neue Daten aus der Husqvarna Cloud abgerufen werden. Standard: 420</li>
|
||||
|
||||
<li><a id='AutomowerConnect-attr-mapImagePath'>mapImagePath</a><br>
|
||||
<code>attr <name> mapImagePath <path to image></code><br>
|
||||
@ -1465,30 +1340,39 @@ __END__
|
||||
<code>
|
||||
'{<br>
|
||||
    "<name_1>" : {<br>
|
||||
      "condition" : "<condition to separate name_1 from other zones>"<br>
|
||||
      "condition" : "<condition to separate name_1 from other zones>",<br>
|
||||
      "cuttingHeight" : "<cutting height for the first zone>"<br>
|
||||
  },<br>
|
||||
    "<name_2>" : {<br>
|
||||
      "condition" : "<condition to separate name_2 from other zones, except name_1>"<br>
|
||||
      "condition" : "<condition to separate name_2 from other zones, except name_1>",<br>
|
||||
      "cuttingHeight" : "<cutting height for the second zone>"<br>
|
||||
  },<br>
|
||||
    "<name_3>" : {<br>
|
||||
      "condition" : "<condition to separate name_3 from other zones, except name_1 and name_2>"<br>
|
||||
      "condition" : "<condition to separate name_3 from other zones, except name_1 and name_2>",<br>
|
||||
      "cuttingHeight" : "<cutting height for the third zone>"<br>
|
||||
  },<br>
|
||||
    "<name_n-1>" : {<br>
|
||||
      "condition" : "<condition to separate name_n-1 from other zones ,except the zones already seperated>"<br>
|
||||
      "condition" : "<condition to separate name_n-1 from other zones ,except the zones already seperated>",<br>
|
||||
      "cuttingHeight" : "<cutting height for the nth-1 zone>"<br>
|
||||
  },<br>
|
||||
    "<name n>" : {<br>
|
||||
      "condition" : "Use 'undef' because the last zone remains."<br>
|
||||
      "condition" : "Use 'undef' because the last zone remains.",<br>
|
||||
      "cuttingHeight" : "<cutting height for the nth zone>"<br>
|
||||
  }<br>
|
||||
}'<br>
|
||||
</code><br>
|
||||
Beispiel mit zwei Zonen und gedachten Linien bestimmt durch die Punkte Latitude 52.6484600648553, 52.64839739580418 (horizontal) und 9.54799477359984 (vertikal). Alle Wegpunkte deren Latitude über einer horizontalen Linie mit der Latitude 52.6484600648553 liegen oder alle Wegpunkte deren Latitude über einer horizontalen Linie mit der Latitude 52.64839739580418 liegen und deren Longitude rechts von einer vertikale Linie mit der Longitude 9.54799477359984 liegen, gehören zur Zone 01_oben. Alle anderen Wegpunkte gehören zur Zone 02_unten.
|
||||
Beispiel mit zwei Zonen und gedachten Linien bestimmt durch die Punkte Latitude 52.6484600648553, 52.64839739580418 (horizontal) und 9.54799477359984 (vertikal). Alle Wegpunkte deren Latitude über einer horizontalen Linie mit der Latitude 52.6484600648553 liegen oder alle Wegpunkte deren Latitude über einer horizontalen Linie mit der Latitude 52.64839739580418 liegen und deren Longitude rechts von einer vertikale Linie mit der Longitude 9.54799477359984 liegen, gehören zur Zone 01_oben. Alle anderen Wegpunkte gehören zur Zone 02_unten.<br>
|
||||
In den Zonen sind unterschiedliche Schnitthöhen eingestellt.<br>
|
||||
|
||||
<code>
|
||||
'{<br>
|
||||
    "01_oben" : {<br>
|
||||
      "condition" : "$latitude > 52.6484600648553 || $longitude > 9.54799477359984 && $latitude > 52.64839739580418"<br>
|
||||
      "condition" : "$latitude > 52.6484600648553 || $longitude > 9.54799477359984 && $latitude > 52.64839739580418",<br>
|
||||
      "cuttingHeight" : "7"<br>
|
||||
  },<br>
|
||||
    "02_unten" : {<br>
|
||||
      "condition" : "undef"<br>
|
||||
      "condition" : "undef",<br>
|
||||
      "cuttingHeight" : "3"<br>
|
||||
  }<br>
|
||||
}'<br>
|
||||
</code></li>
|
||||
|
@ -40,6 +40,7 @@ BEGIN {
|
||||
qw(
|
||||
AttrVal
|
||||
CommandAttr
|
||||
CommandDeleteReading
|
||||
fhemTzOffset
|
||||
FmtDateTime
|
||||
getKeyValue
|
||||
@ -190,153 +191,12 @@ sub Notify {
|
||||
|
||||
readingsBeginUpdate($hash);
|
||||
|
||||
readingsBulkUpdateIfChanged($hash, "batteryPercent", $hash->{helper}{mower}{attributes}{battery}{batteryPercent} );
|
||||
my $pref = 'mower';
|
||||
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} );
|
||||
readingsBulkUpdateIfChanged($hash, $pref.'_commandStatus', 'cleared' );
|
||||
::FHEM::Devices::AMConnect::Common::fillReadings( $hash );
|
||||
|
||||
if ( AttrVal($name, 'mapZones', 0) && $hash->{helper}{currentZone} && $hash->{helper}{mapZones}{$hash->{helper}{currentZone}}{curZoneCnt} ) {
|
||||
my $curZon = $hash->{helper}{currentZone};
|
||||
my $curZonCnt = $hash->{helper}{mapZones}{$curZon}{curZoneCnt};
|
||||
readingsBulkUpdateIfChanged($hash, $pref.'_currentZone', $curZon . '(' . $curZonCnt . '/' . $hash->{helper}{newzonedatasets} . ')' );
|
||||
}
|
||||
|
||||
my $tstamp = $hash->{helper}{mower}{attributes}{$pref}{errorCodeTimestamp};
|
||||
my $timestamp = ::FHEM::Devices::AMConnect::Common::FmtDateTimeGMT($tstamp/1000);
|
||||
readingsBulkUpdateIfChanged($hash, $pref."_errorCodeTimestamp", $tstamp ? $timestamp : '-' );
|
||||
|
||||
my $errc = $hash->{helper}{mower}{attributes}{$pref}{errorCode};
|
||||
readingsBulkUpdateIfChanged($hash, $pref.'_errorCode', $tstamp ? $errc : '-');
|
||||
|
||||
my $errd = $::FHEM::Devices::AMConnect::Common::errortable->{$errc};
|
||||
readingsBulkUpdateIfChanged($hash, $pref.'_errorDescription', $tstamp ? $errd : '-');
|
||||
|
||||
$pref = 'system';
|
||||
readingsBulkUpdateIfChanged($hash, $pref."_name", $hash->{helper}{mower}{attributes}{$pref}{name} );
|
||||
my $model = $hash->{helper}{mower}{attributes}{$pref}{model};
|
||||
$model =~ s/AUTOMOWER./AM/;
|
||||
# $hash->{MODEL} = '' if (!defined $hash->{MODEL});
|
||||
$hash->{MODEL} = $model if ( $model and $hash->{MODEL} ne $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} );
|
||||
|
||||
$tstamp = $hash->{helper}{mower}{attributes}{$pref}{nextStartTimestamp};
|
||||
$timestamp = ::FHEM::Devices::AMConnect::Common::FmtDateTimeGMT($tstamp/1000);
|
||||
readingsBulkUpdateIfChanged($hash, "planner_nextStart", $tstamp ? $timestamp : '-' );
|
||||
$pref = 'statistics';
|
||||
readingsBulkUpdateIfChanged($hash, $pref."_numberOfCollisions", $hash->{helper}->{mower}{attributes}{$pref}{numberOfCollisions} );
|
||||
readingsBulkUpdateIfChanged($hash, $pref."_newGeoDataSets", $hash->{helper}{newdatasets} );
|
||||
$pref = 'settings';
|
||||
readingsBulkUpdateIfChanged($hash, $pref."_headlight", $hash->{helper}->{mower}{attributes}{$pref}{headlight}{mode} );
|
||||
readingsBulkUpdateIfChanged($hash, $pref."_cuttingHeight", $hash->{helper}->{mower}{attributes}{$pref}{cuttingHeight} );
|
||||
$pref = 'status';
|
||||
my $connected = $hash->{helper}{mower}{attributes}{metadata}{connected};
|
||||
readingsBulkUpdateIfChanged($hash, $pref."_connected", ( $connected ? "CONNECTED($connected)" : "OFFLINE($connected)") );
|
||||
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';
|
||||
readingsBulkUpdate($hash, 'state', 'connected',1);
|
||||
readingsEndUpdate($hash, 1);
|
||||
|
||||
my @time = localtime();
|
||||
my $secs = ( $time[2] * 3600 ) + ( $time[1] * 60 ) + $time[0];
|
||||
my $interval = $hosthash->{helper}->{interval};
|
||||
# do at midnight
|
||||
if ( $secs <= $interval ) {
|
||||
::FHEM::Devices::AMConnect::Common::calculateStatistics( $hash );
|
||||
|
||||
$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;
|
||||
|
||||
if ( AttrVal($name, 'mapZones', 0) && defined( $hash->{helper}{mapZones} ) ) {
|
||||
|
||||
my @zonekeys = sort (keys %{$hash->{helper}{mapZones}});
|
||||
my $sumLastDayCnt=0;
|
||||
my $sumCurrentWeekCnt=0;
|
||||
my $sumLastDayArea=0;
|
||||
my $sumCurrentWeekArea=0;
|
||||
map {
|
||||
$hash->{helper}{mapZones}{$_}{lastDayCnt} = $hash->{helper}{mapZones}{$_}{zoneCnt};
|
||||
$sumLastDayCnt += $hash->{helper}{mapZones}{$_}{lastDayCnt};
|
||||
$hash->{helper}{mapZones}{$_}{currentWeekCnt} += $hash->{helper}{mapZones}{$_}{lastDayCnt};
|
||||
$sumCurrentWeekCnt += $hash->{helper}{mapZones}{$_}{currentWeekCnt};
|
||||
$hash->{helper}{mapZones}{$_}{zoneCnt} = 0;
|
||||
|
||||
$hash->{helper}{mapZones}{$_}{lastDayArea} = $hash->{helper}{mapZones}{$_}{zoneLength};
|
||||
$sumLastDayArea += $hash->{helper}{mapZones}{$_}{lastDayArea};
|
||||
$hash->{helper}{mapZones}{$_}{currentWeekArea} += $hash->{helper}{mapZones}{$_}{lastDayArea};
|
||||
$sumCurrentWeekArea += $hash->{helper}{mapZones}{$_}{currentWeekArea};
|
||||
$hash->{helper}{mapZones}{$_}{zoneLength} = 0;
|
||||
} @zonekeys;
|
||||
|
||||
map {
|
||||
$hash->{helper}{mapZones}{$_}{lastDayCntPct} = sprintf( "%.0f", $hash->{helper}{mapZones}{$_}{lastDayCnt} / $sumLastDayCnt * 100 );
|
||||
} @zonekeys if( $sumLastDayCnt );
|
||||
|
||||
map {
|
||||
$hash->{helper}{mapZones}{$_}{currentWeekCntPct} = sprintf( "%.0f", $hash->{helper}{mapZones}{$_}{currentWeekCnt} / $sumCurrentWeekCnt * 100 );
|
||||
} @zonekeys if( $sumCurrentWeekCnt );
|
||||
|
||||
map {
|
||||
$hash->{helper}{mapZones}{$_}{lastDayAreaPct} = sprintf( "%.0f", $hash->{helper}{mapZones}{$_}{lastDayArea} / $sumLastDayArea * 100 );
|
||||
} @zonekeys if( $sumLastDayArea );
|
||||
|
||||
map {
|
||||
$hash->{helper}{mapZones}{$_}{currentWeekAreaPct} = sprintf( "%.0f", $hash->{helper}{mapZones}{$_}{currentWeekArea} / $sumCurrentWeekArea * 100 );
|
||||
} @zonekeys if( $sumCurrentWeekArea );
|
||||
|
||||
}
|
||||
# do on days
|
||||
if ( $time[6] == 1 ) {
|
||||
|
||||
$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;
|
||||
|
||||
if ( AttrVal($name, 'mapZones', 0) && defined( $hash->{helper}{mapZones} ) ) {
|
||||
|
||||
my @zonekeys = sort (keys %{$hash->{helper}{mapZones}});
|
||||
my $sumLastWeekCnt=0;
|
||||
my $sumLastWeekArea=0;
|
||||
map {
|
||||
$hash->{helper}{mapZones}{$_}{lastWeekCnt} = $hash->{helper}{mapZones}{$_}{currentWeekCnt};
|
||||
$sumLastWeekCnt += $hash->{helper}{mapZones}{$_}{lastWeekCnt};
|
||||
$hash->{helper}{mapZones}{$_}{currentWeekCnt} = 0;
|
||||
$hash->{helper}{mapZones}{$_}{lastWeekArea} = $hash->{helper}{mapZones}{$_}{currentWeekArea};
|
||||
$sumLastWeekArea += $hash->{helper}{mapZones}{$_}{lastWeekArea};
|
||||
$hash->{helper}{mapZones}{$_}{currentWeekArea} = 0;
|
||||
} @zonekeys;
|
||||
|
||||
map {
|
||||
$hash->{helper}{mapZones}{$_}{lastWeekCntPct} = sprintf( "%.0f", $hash->{helper}{mapZones}{$_}{lastWeekCnt} / $sumLastWeekCnt * 100 );
|
||||
} @zonekeys if( $sumLastWeekCnt );
|
||||
|
||||
map {
|
||||
$hash->{helper}{mapZones}{$_}{lastWeekAreaPct} = sprintf( "%.0f", $hash->{helper}{mapZones}{$_}{lastWeekArea} / $sumLastWeekArea * 100 );
|
||||
} @zonekeys if( $sumLastWeekArea );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#clear position arrays
|
||||
if ( AttrVal( $name, 'weekdaysToResetWayPoints', 1 ) =~ $time[6] ) {
|
||||
|
||||
$hash->{helper}{areapos} = [];
|
||||
$hash->{helper}{otherpos} = [];
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
readingsSingleUpdate($hash, 'state', 'connected',1);
|
||||
|
||||
}
|
||||
@ -688,6 +548,8 @@ __END__
|
||||
<li>An arbitrary map can be used as background for the mower path.</li>
|
||||
<li>The map has to be a raster image in webp, png or jpg format.</li>
|
||||
<li>It's possible to control everything the API offers, e.g. schedule, headlight, cutting height and actions like start, pause, park etc. </li>
|
||||
<li>Zones are definable. </li>
|
||||
<li>Cutting height can be set for each zone differently. </li>
|
||||
<li>All API data is stored in the device hash, the last and the second last one. Use <code>{Dumper $defs{<name>}}</code> in the commandline to find the data and build userReadings out of it.</li><br>
|
||||
</ul>
|
||||
<u><b>Requirements</b></u>
|
||||
@ -887,30 +749,38 @@ __END__
|
||||
<code>
|
||||
'{<br>
|
||||
    "<name_1>" : {<br>
|
||||
      "condition" : "<condition to separate name_1 from other zones>"<br>
|
||||
      "condition" : "<condition to separate name_1 from other zones>",<br>
|
||||
      "cuttingHeight" : "<cutting height for the first zone>"<br>
|
||||
  },<br>
|
||||
    "<name_2>" : {<br>
|
||||
      "condition" : "<condition to separate name_2 from other zones, except name_1>"<br>
|
||||
      "condition" : "<condition to separate name_2 from other zones, except name_1>",<br>
|
||||
      "cuttingHeight" : "<cutting height for the second zone>"<br>
|
||||
  },<br>
|
||||
    "<name_3>" : {<br>
|
||||
      "condition" : "<condition to separate name_3 from other zones, except name_1 and name_2>"<br>
|
||||
      "condition" : "<condition to separate name_3 from other zones, except name_1 and name_2>",<br>
|
||||
      "cuttingHeight" : "<cutting height for the third zone>"<br>
|
||||
  },<br>
|
||||
    "<name_n-1>" : {<br>
|
||||
      "condition" : "<condition to separate name_n-1 from other zones ,except the zones already seperated>"<br>
|
||||
      "condition" : "<condition to separate name_n-1 from other zones ,except the zones already seperated>",<br>
|
||||
      "cuttingHeight" : "<cutting height for the nth-1 zone>"<br>
|
||||
  },<br>
|
||||
    "<name n>" : {<br>
|
||||
      "condition" : "Use undef because the last zone remains."<br>
|
||||
      "condition" : "Use 'undef' because the last zone remains.",<br>
|
||||
      "cuttingHeight" : "<cutting height for the nth zone>"<br>
|
||||
  }<br>
|
||||
}'<br>
|
||||
</code><br>
|
||||
Example with two Zones and virtual lines defined by latitude 52.6484600648553, 52.64839739580418 (horizontal) and longitude 9.54799477359984 (vertikal). all way points above 52.6484600648553 or all way points above 52.64839739580418 and all way points to the right of 9.54799477359984 belong to zone 01_oben. All other way points belong to zone 02_unten.<br>
|
||||
There are different cutting heightts each zone.<br>
|
||||
<code>
|
||||
'{<br>
|
||||
    "01_oben" : {<br>
|
||||
      "condition" : "$latitude > 52.6484600648553 || $longitude > 9.54799477359984 && $latitude > 52.64839739580418"<br>
|
||||
      "condition" : "$latitude > 52.6484600648553 || $longitude > 9.54799477359984 && $latitude > 52.64839739580418",<br>
|
||||
      "cuttingHeight" : "7"<br>
|
||||
  },<br>
|
||||
    "02_unten" : {<br>
|
||||
      "condition" : "undef"<br>
|
||||
      "condition" : "undef",<br>
|
||||
      "cuttingHeight" : "3"<br>
|
||||
  }<br>
|
||||
}'<br>
|
||||
</code></li>
|
||||
@ -983,12 +853,14 @@ __END__
|
||||
<ul>
|
||||
<li>Dieses Modul nutzt eine Istanz des AutomowerConnect Moduls als Host, um einen weiteren Husqvarna Automower, dessen Daten dort gehostet werden und der mit einem Connect Modul (SIM) ausgerüstet ist, zu steuern.</li>
|
||||
<li>Die Instanzen dieses Moduls bilden die FHEM-Geräte weiterer Mähroboter.</li>
|
||||
<li>Dieses Modul wird also erst benötigt, wenn mehrere Mähroboter unter einem Application Key registriert sind.</li>
|
||||
<li>Dieses Modul wird also erst benötigt, wenn mehrere Mähroboter unter einem Application Key registriert sind.</li>
|
||||
<li>Der Pfad des Mähroboters wird in der Detailansicht des FHEMWEB Frontends angezeigt.</li>
|
||||
<li>Die Zahl der anzuzeigenden Wegpunkte des Pfades kann frei gewählt werden.</li>
|
||||
<li>Der Pfad kann mit einer beliebigen Karte hinterlegt werden.</li>
|
||||
<li>Die Karte muss als Rasterbild im webp, png oder jpg Format vorliegen.</li>
|
||||
<li>Es ist möglich alles was Die API anbietet zu steuern, z.B. Mähplan,Scheinwerfer, Schnitthöhe und Aktionen wie, Start, Pause, Parken usw. </li>
|
||||
<li>Zonen können selbst definiert werden. </li>
|
||||
<li>Die Schnitthöhe kann je selbstdefinierter Zone eingestellt werden. </li>
|
||||
<li>Die letzten und vorletzten Daten aus dem Host sind im Gerätehash gespeichert, Mit <code>{Dumper $defs{<device name>}}</code> in der Befehlszeile können die Daten angezeigt werden und daraus userReadings erstellt werden.</li><br>
|
||||
</ul>
|
||||
<u><b>Anforderungen</b></u>
|
||||
@ -1190,30 +1062,38 @@ __END__
|
||||
<code>
|
||||
'{<br>
|
||||
    "<name_1>" : {<br>
|
||||
      "condition" : "<condition to separate name_1 from other zones>"<br>
|
||||
      "condition" : "<condition to separate name_1 from other zones>",<br>
|
||||
      "cuttingHeight" : "<cutting height for the first zone>"<br>
|
||||
  },<br>
|
||||
    "<name_2>" : {<br>
|
||||
      "condition" : "<condition to separate name_2 from other zones, except name_1>"<br>
|
||||
      "condition" : "<condition to separate name_2 from other zones, except name_1>",<br>
|
||||
      "cuttingHeight" : "<cutting height for the second zone>"<br>
|
||||
  },<br>
|
||||
    "<name_3>" : {<br>
|
||||
      "condition" : "<condition to separate name_3 from other zones, except name_1 and name_2>"<br>
|
||||
      "condition" : "<condition to separate name_3 from other zones, except name_1 and name_2>",<br>
|
||||
      "cuttingHeight" : "<cutting height for the third zone>"<br>
|
||||
  },<br>
|
||||
    "<name_n-1>" : {<br>
|
||||
      "condition" : "<condition to separate name_n-1 from other zones ,except the zones already seperated>"<br>
|
||||
      "condition" : "<condition to separate name_n-1 from other zones ,except the zones already seperated>",<br>
|
||||
      "cuttingHeight" : "<cutting height for the nth-1 zone>"<br>
|
||||
  },<br>
|
||||
    "<name n>" : {<br>
|
||||
      "condition" : "Use undef because the last zone remains."<br>
|
||||
      "condition" : "Use 'undef' because the last zone remains.",<br>
|
||||
      "cuttingHeight" : "<cutting height for the nth zone>"<br>
|
||||
  }<br>
|
||||
}'<br>
|
||||
</code><br>
|
||||
Beispiel mit zwei Zonen und gedachten Linien bestimmt durch die Punkte Latitude 52.6484600648553, 52.64839739580418 (horizontal) und 9.54799477359984 (vertikal). Alle Wegpunkte deren Latitude über einer horizontalen Linie mit der Latitude 52.6484600648553 liegen oder alle Wegpunkte deren Latitude über einer horizontalen Linie mit der Latitude 52.64839739580418 liegen und deren Longitude rechts von einer vertikale Linie mit der Longitude 9.54799477359984 liegen, gehören zur Zone 01_oben Alle anderen Wegpunkte gehören zur Zone 02_unten.
|
||||
Beispiel mit zwei Zonen und gedachten Linien bestimmt durch die Punkte Latitude 52.6484600648553, 52.64839739580418 (horizontal) und 9.54799477359984 (vertikal). Alle Wegpunkte deren Latitude über einer horizontalen Linie mit der Latitude 52.6484600648553 liegen oder alle Wegpunkte deren Latitude über einer horizontalen Linie mit der Latitude 52.64839739580418 liegen und deren Longitude rechts von einer vertikale Linie mit der Longitude 9.54799477359984 liegen, gehören zur Zone 01_oben. Alle anderen Wegpunkte gehören zur Zone 02_unten.<br>
|
||||
In den Zonen sind unterschiedliche Schnitthöhen eingestellt.<br>
|
||||
<code>
|
||||
'{<br>
|
||||
    "01_oben" : {<br>
|
||||
      "condition" : "$latitude > 52.6484600648553 || $longitude > 9.54799477359984 && $latitude > 52.64839739580418"<br>
|
||||
      "condition" : "$latitude > 52.6484600648553 || $longitude > 9.54799477359984 && $latitude > 52.64839739580418",<br>
|
||||
      "cuttingHeight" : "7"<br>
|
||||
  },<br>
|
||||
    "02_unten" : {<br>
|
||||
      "condition" : "undef"<br>
|
||||
      "condition" : "undef",<br>
|
||||
      "cuttingHeight" : "3"<br>
|
||||
  }<br>
|
||||
}'<br>
|
||||
</code></li>
|
||||
|
@ -34,7 +34,7 @@ use POSIX;
|
||||
use GPUtils qw(:all);
|
||||
|
||||
use Time::HiRes qw(gettimeofday);
|
||||
use Blocking;
|
||||
# use Blocking;
|
||||
use Storable qw(dclone retrieve store);
|
||||
|
||||
# Import der FHEM Funktionen
|
||||
@ -148,19 +148,24 @@ mowingPathLineWidth="1"';
|
||||
|
||||
my $mapZonesTpl = '{
|
||||
"A_Zone_1" : {
|
||||
"condition" : "<condition to separate Zone_1 from other zones>"
|
||||
"condition" : "<condition to separate Zone_1 from other zones>",
|
||||
"cuttingHeight" : "<cutting height for the first zone>"
|
||||
},
|
||||
"B_Zone_2" : {
|
||||
"condition" : "<condition to separate Zone_2 from other zones, except myZone_1>"
|
||||
"condition" : "<condition to separate Zone_2 from other zones, except myZone_1>",
|
||||
"cuttingHeight" : "<cutting height for the second zone>"
|
||||
},
|
||||
"C_Zone_3" : {
|
||||
"condition" : "<condition to separate Zone_3 from other zones, except myZone_1 and myZone_2>"
|
||||
"condition" : "<condition to separate Zone_3 from other zones, except myZone_1 and myZone_2>",
|
||||
"cuttingHeight" : "<cutting height for the third zone>"
|
||||
},
|
||||
"D_Zone_x" : {
|
||||
"condition" : "<condition to separate Zone_x from other zones ,except the zones already seperated>"
|
||||
"condition" : "<condition to separate Zone_x from other zones ,except the zones already seperated>",
|
||||
"cuttingHeight" : "<cutting height for the nth-1 zone>"
|
||||
},
|
||||
"E_LastZone" : {
|
||||
"condition" : "Use undef because the last zone remains."
|
||||
"condition" : "Use undef because the last zone remains.",
|
||||
"cuttingHeight" : "<cutting height for the nth zone>"
|
||||
}
|
||||
}';
|
||||
|
||||
@ -168,7 +173,7 @@ my $mapZonesTpl = '{
|
||||
%$hash = (%$hash,
|
||||
helper => {
|
||||
passObj => FHEM::Core::Authentication::Passwords->new($type),
|
||||
interval => 600,
|
||||
interval => 420,
|
||||
client_id => $client_id,
|
||||
grant_type => 'client_credentials',
|
||||
mowerNumber => $mowerNumber,
|
||||
@ -734,6 +739,14 @@ sub AlignArray {
|
||||
$tmp = dclone( \@ar );
|
||||
ZoneHandling ( $hash, $tmp, $cnt );
|
||||
|
||||
}
|
||||
# set cutting height per zone
|
||||
if ( AttrVal($name, 'mapZones', 0) && $act =~ /^MOWING$/ && $actold =~ /^MOWING$/
|
||||
&& defined( $hash->{helper}{currentZone} ) && defined( $hash->{helper}{mapZones}{$hash->{helper}{currentZone}}{cuttingHeight} )) {
|
||||
|
||||
CMD( $hash ,'cuttingHeight', $hash->{helper}{mapZones}{$hash->{helper}{currentZone}}{cuttingHeight} )
|
||||
if ( $hash->{helper}{mapZones}{$hash->{helper}{currentZone}}{cuttingHeight} != $hash->{helper}{mower}{attributes}{settings}{cuttingHeight} );
|
||||
|
||||
}
|
||||
|
||||
if ( $act =~ /^(CHARGING|PARKED_IN_CS)$/ && $actold =~ /^(PARKED_IN_CS|CHARGING)$/ ) {
|
||||
@ -773,6 +786,7 @@ sub AlignArray {
|
||||
return undef;
|
||||
|
||||
}
|
||||
|
||||
#########################
|
||||
sub isErrorThanPrepare {
|
||||
my ( $hash, $poshash ) = @_;
|
||||
@ -858,8 +872,8 @@ sub ZoneHandling {
|
||||
|
||||
}
|
||||
|
||||
$hash->{helper}{mapZones}{$zonekeys[$k]}{zoneLength} += calcPathLength( $hash, $i, $i + 1 );
|
||||
$hash->{helper}{mapZones}{$zonekeys[$k]}{zoneCnt}++;
|
||||
$hash->{helper}{mapZones}{$zonekeys[$k]}{zoneLength} += calcPathLength( $hash, $i, $i + 1 );
|
||||
last;
|
||||
|
||||
} elsif ( $k == @zonekeys-2 ) { # last zone
|
||||
@ -871,8 +885,8 @@ sub ZoneHandling {
|
||||
|
||||
}
|
||||
|
||||
$hash->{helper}{mapZones}{$zonekeys[$k+1]}{zoneLength} += calcPathLength( $hash, $i, $i + 1 );
|
||||
$hash->{helper}{mapZones}{$zonekeys[$k+1]}{zoneCnt}++;
|
||||
$hash->{helper}{mapZones}{$zonekeys[$k+1]}{zoneLength} += calcPathLength( $hash, $i, $i + 1 );
|
||||
|
||||
}
|
||||
|
||||
@ -881,12 +895,80 @@ sub ZoneHandling {
|
||||
}
|
||||
|
||||
my $sumDayCnt=0;
|
||||
map { $sumDayCnt += $hash->{helper}{mapZones}{$_}{zoneCnt} } @zonekeys;
|
||||
map { $hash->{helper}{mapZones}{$_}{currentDayCntPct} = sprintf( "%.0f", $hash->{helper}{mapZones}{$_}{zoneCnt} / $sumDayCnt * 100 ) } @zonekeys if ( $sumDayCnt );
|
||||
|
||||
my $sumDayArea=0;
|
||||
map { $sumDayArea += $hash->{helper}{mapZones}{$_}{zoneLength} } @zonekeys;
|
||||
map { $hash->{helper}{mapZones}{$_}{currentDayAreaPct} = sprintf( "%.0f", $hash->{helper}{mapZones}{$_}{zoneLength} / $sumDayArea * 100 ) } @zonekeys if ( $sumDayArea );
|
||||
|
||||
map { $sumDayCnt += $hash->{helper}{mapZones}{$_}{zoneCnt};
|
||||
$sumDayArea += $hash->{helper}{mapZones}{$_}{zoneLength};
|
||||
} @zonekeys;
|
||||
|
||||
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 );
|
||||
} @zonekeys;
|
||||
|
||||
$hash->{helper}{newzonedatasets} = $cnt;
|
||||
|
||||
}
|
||||
|
||||
#########################
|
||||
sub setCuttingHeight {
|
||||
my ( $hash, $poshash, $cnt ) = @_;
|
||||
my $name = $hash->{NAME};
|
||||
my $zone = '';
|
||||
my $nextzone = '';
|
||||
my @pos = @$poshash;
|
||||
my $longitude = 0;
|
||||
my $latitude = 0;
|
||||
my @zonekeys = sort (keys %{$hash->{helper}{mapZones}});
|
||||
my $i = 0;
|
||||
my $k = 0;
|
||||
|
||||
map{ $hash->{helper}{mapZones}{$_}{curZoneCnt} = 0 } @zonekeys;
|
||||
|
||||
for ( $i = 0; $i < $cnt; $i++){
|
||||
|
||||
$longitude = $pos[$i]{longitude};
|
||||
$latitude = $pos[$i]{latitude};
|
||||
|
||||
for ( $k = 0; $k < @zonekeys-1; $k++){
|
||||
|
||||
if ( eval ("$hash->{helper}{mapZones}{$zonekeys[$k]}{condition}") ) {
|
||||
|
||||
if ( $hash->{helper}{mapZones}{$zonekeys[$k]}{curZoneCnt} == $i) { # find current zone and count consecutive way points
|
||||
|
||||
$hash->{helper}{mapZones}{$zonekeys[$k]}{curZoneCnt}++;
|
||||
$hash->{helper}{currentZone} = $zonekeys[$k];
|
||||
|
||||
}
|
||||
|
||||
$hash->{helper}{mapZones}{$zonekeys[$k]}{zoneCnt}++;
|
||||
$hash->{helper}{mapZones}{$zonekeys[$k]}{zoneLength} += calcPathLength( $hash, $i, $i + 1 );
|
||||
last;
|
||||
|
||||
} elsif ( $k == @zonekeys-2 ) { # last zone
|
||||
|
||||
if ( $hash->{helper}{mapZones}{$zonekeys[$k+1]}{curZoneCnt} == $i) { # find current zone and count consecutive way points
|
||||
|
||||
$hash->{helper}{mapZones}{$zonekeys[$k+1]}{curZoneCnt}++;
|
||||
$hash->{helper}{currentZone} = $zonekeys[$k+1];
|
||||
|
||||
}
|
||||
|
||||
$hash->{helper}{mapZones}{$zonekeys[$k+1]}{zoneCnt}++;
|
||||
$hash->{helper}{mapZones}{$zonekeys[$k+1]}{zoneLength} += calcPathLength( $hash, $i, $i + 1 );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
my $sumDayCnt=0;
|
||||
my $sumDayArea=0;
|
||||
map { $sumDayCnt += $hash->{helper}{mapZones}{$_}{zoneCnt};
|
||||
$hash->{helper}{mapZones}{$_}{currentDayCntPct} = ( $sumDayCnt ? sprintf( "%.0f", $hash->{helper}{mapZones}{$_}{zoneCnt} / $sumDayCnt * 100 ) : 0 );
|
||||
$sumDayArea += $hash->{helper}{mapZones}{$_}{zoneLength};
|
||||
$hash->{helper}{mapZones}{$_}{currentDayAreaPct} = ( $sumDayArea ? sprintf( "%.0f", $hash->{helper}{mapZones}{$_}{zoneLength} / $sumDayArea * 100 ) : 0 );
|
||||
} @zonekeys;
|
||||
|
||||
$hash->{helper}{newzonedatasets} = $cnt;
|
||||
|
||||
@ -1080,6 +1162,141 @@ sub posMinMax {
|
||||
return undef;
|
||||
}
|
||||
|
||||
#########################
|
||||
sub fillReadings {
|
||||
my ( $hash ) = @_;
|
||||
my $name = $hash->{NAME};
|
||||
|
||||
readingsBulkUpdateIfChanged($hash, "batteryPercent", $hash->{helper}{mower}{attributes}{battery}{batteryPercent} );
|
||||
my $pref = 'mower';
|
||||
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} );
|
||||
readingsBulkUpdateIfChanged($hash, $pref.'_commandStatus', 'cleared' );
|
||||
|
||||
if ( AttrVal($name, 'mapZones', 0) && $hash->{helper}{currentZone} && $hash->{helper}{mapZones}{$hash->{helper}{currentZone}}{curZoneCnt} ) {
|
||||
my $curZon = $hash->{helper}{currentZone};
|
||||
my $curZonCnt = $hash->{helper}{mapZones}{$curZon}{curZoneCnt};
|
||||
readingsBulkUpdateIfChanged($hash, $pref.'_currentZone', $curZon . '(' . $curZonCnt . '/' . $hash->{helper}{newzonedatasets} . ')' );
|
||||
}
|
||||
|
||||
my $tstamp = $hash->{helper}{mower}{attributes}{$pref}{errorCodeTimestamp};
|
||||
my $timestamp = FmtDateTimeGMT($tstamp/1000);
|
||||
readingsBulkUpdateIfChanged($hash, $pref."_errorCodeTimestamp", $tstamp ? $timestamp : '-' );
|
||||
|
||||
my $errc = $hash->{helper}{mower}{attributes}{$pref}{errorCode};
|
||||
readingsBulkUpdateIfChanged($hash, $pref.'_errorCode', $tstamp ? $errc : '-');
|
||||
|
||||
my $errd = $errortable->{$errc};
|
||||
readingsBulkUpdateIfChanged($hash, $pref.'_errorDescription', $tstamp ? $errd : '-');
|
||||
|
||||
$pref = 'system';
|
||||
readingsBulkUpdateIfChanged($hash, $pref."_name", $hash->{helper}{mower}{attributes}{$pref}{name} );
|
||||
my $model = $hash->{helper}{mower}{attributes}{$pref}{model};
|
||||
$model =~ s/AUTOMOWER./AM/;
|
||||
$hash->{MODEL} = $model if ( $model && $hash->{MODEL} ne $model );
|
||||
$pref = 'planner';
|
||||
readingsBulkUpdateIfChanged($hash, "planner_restrictedReason", $hash->{helper}{mower}{attributes}{$pref}{restrictedReason} );
|
||||
readingsBulkUpdateIfChanged($hash, "planner_overrideAction", $hash->{helper}{mower}{attributes}{$pref}{override}{action} );
|
||||
|
||||
$tstamp = $hash->{helper}{mower}{attributes}{$pref}{nextStartTimestamp};
|
||||
$timestamp = FmtDateTimeGMT($tstamp/1000);
|
||||
readingsBulkUpdateIfChanged($hash, "planner_nextStart", $tstamp ? $timestamp : '-' );
|
||||
|
||||
$pref = 'statistics';
|
||||
readingsBulkUpdateIfChanged($hash, $pref."_numberOfCollisions", $hash->{helper}->{mower}{attributes}{$pref}{numberOfCollisions} );
|
||||
readingsBulkUpdateIfChanged($hash, $pref."_newGeoDataSets", $hash->{helper}{newdatasets} );
|
||||
$pref = 'settings';
|
||||
readingsBulkUpdateIfChanged($hash, $pref."_headlight", $hash->{helper}->{mower}{attributes}{$pref}{headlight}{mode} );
|
||||
readingsBulkUpdateIfChanged($hash, $pref."_cuttingHeight", $hash->{helper}->{mower}{attributes}{$pref}{cuttingHeight} );
|
||||
$pref = 'status';
|
||||
my $connected = $hash->{helper}{mower}{attributes}{metadata}{connected};
|
||||
readingsBulkUpdateIfChanged($hash, $pref."_connected", ( $connected ? "CONNECTED($connected)" : "OFFLINE($connected)") );
|
||||
|
||||
my $storediff = $hash->{helper}{mower}{attributes}{metadata}{statusTimestamp} - $hash->{helper}{mowerold}{attributes}{metadata}{statusTimestamp};
|
||||
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 ));
|
||||
|
||||
return undef;
|
||||
}
|
||||
|
||||
#########################
|
||||
sub calculateStatistics {
|
||||
my ( $hash ) = @_;
|
||||
my $name = $hash->{NAME};
|
||||
my @time = localtime();
|
||||
my $secs = ( $time[2] * 3600 ) + ( $time[1] * 60 ) + $time[0];
|
||||
my $interval = $hash->{helper}->{interval};
|
||||
# do at midnight
|
||||
if ( $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;
|
||||
|
||||
if ( AttrVal($name, 'mapZones', 0) && defined( $hash->{helper}{mapZones} ) ) {
|
||||
|
||||
my @zonekeys = sort (keys %{$hash->{helper}{mapZones}});
|
||||
my $sumCurrentWeekCnt=0;
|
||||
my $sumCurrentWeekArea=0;
|
||||
map {
|
||||
$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};
|
||||
$hash->{helper}{mapZones}{$_}{zoneCnt} = 0;
|
||||
$hash->{helper}{mapZones}{$_}{zoneLength} = 0;
|
||||
} @zonekeys;
|
||||
|
||||
map {
|
||||
$hash->{helper}{mapZones}{$_}{lastDayCntPct} = $hash->{helper}{mapZones}{$_}{currentDayCntPct};
|
||||
$hash->{helper}{mapZones}{$_}{currentWeekCntPct} = ( $sumCurrentWeekCnt ? sprintf( "%.0f", $hash->{helper}{mapZones}{$_}{currentWeekCnt} / $sumCurrentWeekCnt * 100 ) : '' );
|
||||
$hash->{helper}{mapZones}{$_}{lastDayAreaPct} = $hash->{helper}{mapZones}{$_}{currentDayAreaPct};
|
||||
$hash->{helper}{mapZones}{$_}{currentWeekAreaPct} = ( $sumCurrentWeekArea ? sprintf( "%.0f", $hash->{helper}{mapZones}{$_}{currentWeekArea} / $sumCurrentWeekArea * 100 ) : '' );
|
||||
$hash->{helper}{mapZones}{$_}{currentDayCntPct} = '';
|
||||
$hash->{helper}{mapZones}{$_}{currentDayAreaPct} = '';
|
||||
} @zonekeys;
|
||||
|
||||
}
|
||||
# do on days
|
||||
if ( $time[6] == 1 ) {
|
||||
|
||||
$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;
|
||||
|
||||
if ( AttrVal($name, 'mapZones', 0) && defined( $hash->{helper}{mapZones} ) ) {
|
||||
|
||||
my @zonekeys = sort (keys %{$hash->{helper}{mapZones}});
|
||||
map {
|
||||
$hash->{helper}{mapZones}{$_}{lastWeekCntPct} = $hash->{helper}{mapZones}{$_}{currentWeekCntPct};
|
||||
$hash->{helper}{mapZones}{$_}{lastWeekAreaPct} = $hash->{helper}{mapZones}{$_}{currentWeekAreaPct};
|
||||
$hash->{helper}{mapZones}{$_}{currentWeekCntPct} = '';
|
||||
$hash->{helper}{mapZones}{$_}{currentWeekAreaPct} = '';
|
||||
} @zonekeys;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#clear position arrays
|
||||
if ( AttrVal( $name, 'weekdaysToResetWayPoints', 1 ) =~ $time[6] ) {
|
||||
|
||||
$hash->{helper}{areapos} = [];
|
||||
$hash->{helper}{otherpos} = [];
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return undef;
|
||||
}
|
||||
|
||||
#########################
|
||||
sub listStatisticsData {
|
||||
my ( $hash ) = @_;
|
||||
@ -1094,10 +1311,10 @@ sub listStatisticsData {
|
||||
$ret .= '<tr class="col_header"><td> Hash Path </td><td> Value </td><td> Unit </td></tr>';
|
||||
$cnt++;$ret .= '<tr class="column '.( $cnt % 2 ? 'odd' : 'even' ).'"><td> $hash->{helper}{mower}{attributes}{statistics}{<b>numberOfChargingCycles</b>}  </td><td> ' . $hash->{helper}{mower}{attributes}{statistics}{numberOfChargingCycles} . ' </td><td> </td></tr>';
|
||||
$cnt++;$ret .= '<tr class="column '.( $cnt % 2 ? 'odd' : 'even' ).'"><td> $hash->{helper}{mower}{attributes}{statistics}{<b>numberOfCollisions</b>}  </td><td> ' . $hash->{helper}{mower}{attributes}{statistics}{numberOfCollisions} . ' </td><td> </td></tr>';
|
||||
$cnt++;$ret .= '<tr class="column '.( $cnt % 2 ? 'odd' : 'even' ).'"><td> $hash->{helper}{mower}{attributes}{statistics}{<b>totalChargingTime</b>}  </td><td> ' . $hash->{helper}{mower}{attributes}{statistics}{totalChargingTime} . ' </td><td> s </td></tr>';
|
||||
$cnt++;$ret .= '<tr class="column '.( $cnt % 2 ? 'odd' : 'even' ).'"><td> $hash->{helper}{mower}{attributes}{statistics}{<b>totalCuttingTime</b>}  </td><td> ' . $hash->{helper}{mower}{attributes}{statistics}{totalCuttingTime} . ' </td><td> s </td></tr>';
|
||||
$cnt++;$ret .= '<tr class="column '.( $cnt % 2 ? 'odd' : 'even' ).'"><td> $hash->{helper}{mower}{attributes}{statistics}{<b>totalRunningTime</b>}  </td><td> ' . $hash->{helper}{mower}{attributes}{statistics}{totalRunningTime} . '<sup>1</sup> </td><td> s </td></tr>';
|
||||
$cnt++;$ret .= '<tr class="column '.( $cnt % 2 ? 'odd' : 'even' ).'"><td> $hash->{helper}{mower}{attributes}{statistics}{<b>totalSearchingTime</b>}  </td><td> ' . $hash->{helper}{mower}{attributes}{statistics}{totalSearchingTime} . ' </td><td> s </td></tr>';
|
||||
$cnt++;$ret .= '<tr class="column '.( $cnt % 2 ? 'odd' : 'even' ).'"><td> $hash->{helper}{mower}{attributes}{statistics}{<b>totalChargingTime</b>}  </td><td> ' . sprintf( "%.0f", $hash->{helper}{mower}{attributes}{statistics}{totalChargingTime} / 3600 ) . ' </td><td> h </td></tr>';
|
||||
$cnt++;$ret .= '<tr class="column '.( $cnt % 2 ? 'odd' : 'even' ).'"><td> $hash->{helper}{mower}{attributes}{statistics}{<b>totalCuttingTime</b>}  </td><td> ' . sprintf( "%.0f", $hash->{helper}{mower}{attributes}{statistics}{totalCuttingTime} / 3600 ) . ' </td><td> h </td></tr>';
|
||||
$cnt++;$ret .= '<tr class="column '.( $cnt % 2 ? 'odd' : 'even' ).'"><td> $hash->{helper}{mower}{attributes}{statistics}{<b>totalRunningTime</b>}  </td><td> ' . sprintf( "%.0f", $hash->{helper}{mower}{attributes}{statistics}{totalRunningTime} / 3600 ) . '<sup>1</sup> </td><td> h </td></tr>';
|
||||
$cnt++;$ret .= '<tr class="column '.( $cnt % 2 ? 'odd' : 'even' ).'"><td> $hash->{helper}{mower}{attributes}{statistics}{<b>totalSearchingTime</b>}  </td><td> ' . sprintf( "%.0f", $hash->{helper}{mower}{attributes}{statistics}{totalSearchingTime} / 3600 ) . ' </td><td> h </td></tr>';
|
||||
|
||||
# $cnt++;$ret .= '<tr class="column '.( $cnt % 2 ? 'odd' : 'even' ).'"><td> $hash->{helper}{statistics}{currentSpeed}  </td><td> ' . $hash->{helper}{statistics}{currentSpeed} . ' </td><td> m/s </td></tr>';
|
||||
$cnt++;$ret .= '<tr class="column '.( $cnt % 2 ? 'odd' : 'even' ).'"><td> $hash->{helper}{statistics}{<b>currentDayTrack</b>}  </td><td> ' . sprintf( "%.0f", $hash->{helper}{statistics}{currentDayTrack} ) . ' </td><td> m </td></tr>';
|
||||
|
Loading…
Reference in New Issue
Block a user