2
0
mirror of https://github.com/fhem/fhem-mirror.git synced 2025-03-09 20:57:11 +00:00

70_BOTVAC.pm: add cleaning statistics

git-svn-id: https://svn.fhem.de/fhem/trunk@19229 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
vuffiraa 2019-04-20 12:16:15 +00:00
parent 7315df7306
commit c3af66840f
2 changed files with 97 additions and 17 deletions

View File

@ -1,5 +1,6 @@
# 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: 70_BOTVAC: add cleaning statistics
- feature: 98_livetracking: CSV data from OwnTracks
- new: 98_Weather: add wundergroundAPI for Weather Underground
- feature: 98_backup: add support for FileLog path

View File

@ -215,8 +215,54 @@ sub Get($@) {
} else {
return "no such reading: $what";
}
} elsif ( $what =~ /^(statistics)$/ ) {
if (defined( $hash->{helper}{MAPS} and @{$hash->{helper}{MAPS}} > 0)) {
my $ret = "";
my $mapcount = @{$hash->{helper}{MAPS}};
my $cw = 9; #column width
my $cws = $cw * 11 + 4; #column width sum
$ret .= "<b>".sprintf("%-".($cws)."s","Report: ".ReadingsVal($name,"name","name").", ".InternalVal($name,"VENDOR","VENDOR").", ".ReadingsVal($name,"model","model"))."</b>\n\n";
$ret .= "<b>".sprintf("%-".($cw-5)."s","Map").sprintf("%-".($cw+1)."s","Expected").sprintf("%-".($cw-4)."s","Map").sprintf("%-".($cw-4)."s","Map").sprintf("%-".($cw-2)."s","Charge").sprintf("%-".($cw+1)."s","Discharge").sprintf("%-".($cw-2)."s","Area").sprintf("%-".($cw+11)."s","Cleaning").sprintf("%-".($cw-2)."s","Charge").sprintf("%-".$cw."s","Status").sprintf("%-".($cw+2)."s","Date").sprintf("%-".($cw+10)."s","Time")."</b>\n";
$ret .= "<b>".sprintf("%-".($cw-5)."s","No.").sprintf("%-".($cw-4)."s","Area").sprintf("%-".($cw-4)."s","Time").sprintf("%-".($cw-4)."s","Area").sprintf("%-".($cw-4)."s","Time").sprintf("%-".($cw-2)."s","Delta").sprintf("%-".(($cw+1))."s","Speed").sprintf("%-".($cw-2)."s","Speed").sprintf("%-".($cw-2)."s","Cat.").sprintf("%-".($cw-3)."s","Mode").sprintf("%-".($cw-2)."s","Freq.").sprintf("%-".($cw-2)."s","During").sprintf("%-".$cw."s","").sprintf("%-".($cw+2)."s","").sprintf("%-".($cw+10)."s","")."</b>\n";
$ret .= "<b>".sprintf("%-".($cw-5)."s","").sprintf("%-".($cw-4)."s","qm").sprintf("%-".($cw-4)."s","min").sprintf("%-".($cw-4)."s","qm").sprintf("%-".($cw-4)."s","min").sprintf("%-".($cw-2)."s","%").sprintf("%-".($cw+1)."s","%/min").sprintf("%-".($cw-2)."s","qm/min").sprintf("%-".($cw-2)."s","").sprintf("%-".($cw-3)."s","").sprintf("%-".($cw-2)."s","").sprintf("%-".($cw-2)."s","Run").sprintf("%-".$cw."s","").sprintf("%-".($cw+2)."s","YYYY-MM-DD").sprintf("%-".($cw+10)."s","hh:mm:ss")."</b>\n";
$ret .= sprintf("-"x($cws))."\n";
for (my $i=0;$i<$mapcount;$i++) {
my $map = \$hash->{helper}{MAPS}[$i];
my $t1 = GetSecondsFromString("$$map->{end_at}");
my $t2 = GetSecondsFromString("$$map->{start_at}");
my $dt = $t1-$t2-$$map->{time_in_suspended_cleaning}-$$map->{time_in_error}-$$map->{time_in_pause};
my $dc = $$map->{run_charge_at_start}-$$map->{run_charge_at_end};
my $expa = int($$map->{cleaned_area}*100/$dc+.5) if ($dc > 0);
my $expt = int($dt*100/$dc/60+.5) if ($dc > 0);
$ret .= sprintf("%-".($cw-5)."s",$i+1); # Map No.
$ret .= sprintf("%-".($cw-4)."s",($expa>0?$expa:0)); # Expected Area
$ret .= sprintf("%-".($cw-4)."s",($expt>0?$expt:0)); # Expected Time
$ret .= sprintf("%-".($cw-4)."s",int($$map->{cleaned_area}+.5)); # Map Area
$ret .= sprintf("%-".($cw-4)."s",($dt>0)?(int($dt/60+.5)):0); # Map Time
$ret .= sprintf("%-".($cw-2)."s",($dc>0?$dc:0)); # Charge Delta
$ret .= sprintf("%-".($cw+1)."s",($dt>0 and $dc>0)?(int($dc*600/$dt+.5)/10):0); # Discharge Speed
$ret .= sprintf("%-".($cw-2)."s",($expt>0 and $expa>0)?(int($expa*10/$expt+.5))/10:0); # Area Speed
$ret .= sprintf("%-".($cw-2)."s",GetCategoryText($$map->{category})); # Cleaning Category
$ret .= sprintf("%-".($cw-3)."s",GetModeText($$map->{mode})); # Cleaning Mode
$ret .= sprintf("%-".($cw-2)."s",GetModifierText($$map->{modifier})); # Cleaning Frequency
$ret .= sprintf("%-".($cw-2)."s",$$map->{suspended_cleaning_charging_count}."x"); # Charge During Run
$ret .= sprintf("%-".$cw."s",$$map->{status}); # Status
$ret .= sprintf("%-".($cws)."s",GetTimeFromString($$map->{generated_at})); # Date
$ret .= "\n";
$ret .= "<div style=\"color:#d9d9d9\" >".sprintf("-"x($cws))."</div>" if ($i%5==4);
}
$ret .= "\nManufacturer Specification\n";
$ret .= "Vorwerk VR220(VR300), battery 84 Wh, eco (90 min, 120 qm, power 65 W), turbo (60 min, 90 qm, power 85 W)\n";
return $ret;
} else {
return "maps for $what are not available yet";
}
} else {
return "Unknown argument $what, choose one of batteryPercent:noArg";
return "Unknown argument $what, choose one of batteryPercent:noArg statistics:noArg";
}
}
@ -964,7 +1010,7 @@ sub ReceiveCommand($$$) {
for (my $i = 0; $i < @boundaries; $i++) {
my $currentBoundary = "{";
$currentBoundary .= "\"id\":\"".$boundaries[$i]->{id}."\"," if ($boundaries[$i]->{type} eq "polygon");
$currentBoundary .= "\"type\":\"".$boundaries[$i]->{type}."\",";
$currentBoundary .= "\"type\":\"".$boundaries[$i]->{type}."\",";
if (ref($boundaries[$i]->{vertices}) eq "ARRAY") {
my @vertices = @{$boundaries[$i]->{vertices}};
$currentBoundary .= "\"vertices\":[";
@ -974,8 +1020,8 @@ sub ReceiveCommand($$$) {
$currentBoundary .= "[".$xy[0].",".$xy[1]."],";
}
}
$tmp = chop($currentBoundary); #remove last ","
$currentBoundary .= "],";
$tmp = chop($currentBoundary); #remove last ","
$currentBoundary .= "],";
}
$currentBoundary .= "\"name\":\"".$boundaries[$i]->{name}."\",";
$currentBoundary .= "\"color\":\"".$boundaries[$i]->{color}."\",";
@ -983,18 +1029,18 @@ sub ReceiveCommand($$$) {
$currentBoundary .= "\"enabled\":".$tmp.",";
$tmp = chop($currentBoundary); #remove last ","
$currentBoundary .= "},\n";
if ($boundaries[$i]->{type} eq "polygon") {
$zonesList .= $currentBoundary;
} else {
$boundariesList .= $currentBoundary;
}
if ($boundaries[$i]->{type} eq "polygon") {
$zonesList .= $currentBoundary;
} else {
$boundariesList .= $currentBoundary;
}
}
$tmp = chomp($boundariesList); #remove last "\n"
$tmp = chomp($zonesList); #remove last "\n"
$tmp = chop($boundariesList); #remove last ","
$tmp = chop($zonesList); #remove last ","
$tmp = chomp($zonesList); #remove last "\n"
$tmp = chop($boundariesList); #remove last ","
$tmp = chop($zonesList); #remove last ","
readingsBulkUpdateIfChanged($hash, "floorplan_".$reqId."_boundaries", $boundariesList);
readingsBulkUpdateIfChanged($hash, "floorplan_".$reqId."_zones", $zonesList);
readingsBulkUpdateIfChanged($hash, "floorplan_".$reqId."_zones", $zonesList);
}
}
}
@ -1135,14 +1181,28 @@ sub ReceiveCommand($$$) {
if ( ref($return) eq "HASH" ) {
if ( ref($return->{maps} ) eq "ARRAY" ) {
my @maps = @{$return->{maps}};
$hash->{helper}{MAPS} = $return->{maps};
if (@maps) {
# take first - newest
my $map = $maps[0];
readingsBulkUpdateIfChanged($hash, "map_status", $map->{status});
readingsBulkUpdateIfChanged($hash, "map_id", $map->{id});
foreach my $key (keys $map) {
readingsBulkUpdateIfChanged($hash, "map_".$key, defined($map->{$key})?$map->{$key}:"")
if ($key !~ "url|url_valid_for_seconds|generated_at|start_at|end_at");
}
readingsBulkUpdateIfChanged($hash, "map_date", GetTimeFromString($map->{generated_at}));
readingsBulkUpdateIfChanged($hash, "map_area", $map->{cleaned_area});
readingsBulkUpdateIfChanged($hash, ".map_url", $map->{url});
my $t1 = GetSecondsFromString($map->{end_at});
my $t2 = GetSecondsFromString($map->{start_at});
my $dt = $t1-$t2-$map->{time_in_suspended_cleaning}-$map->{time_in_error}-$map->{time_in_pause};
my $dc = $map->{run_charge_at_start}-$map->{run_charge_at_end};
my $expa = int($map->{cleaned_area}*100/$dc+.5) if ($dc > 0);
my $expt = int($dt*100/$dc/60+.5) if ($dc > 0);
readingsBulkUpdateIfChanged($hash, "map_duration", int($dt/6+.5)/10); # min
readingsBulkUpdateIfChanged($hash, "map_expected_area", $expa>0?$expa:0); # qm
readingsBulkUpdateIfChanged($hash, "map_run_discharge", $dc>0?$dc:0); # %
readingsBulkUpdateIfChanged($hash, "map_expected_time", $expt>0?$expt:0); # min
readingsBulkUpdateIfChanged($hash, "map_area_per_time", ($expt>0 and $expa>0)?(int($expa*10/$expt+.5)/10):0); # qm/min
readingsBulkUpdateIfChanged($hash, "map_discharge_per_time", ($dt>0 and $dc>0)?(int($dc*600/$dt+.5)/10):0); # %/min
$loadMap = 1;
# getPersistentMaps
push(@successor , ["robots", "persistent_maps"]);
@ -1226,6 +1286,18 @@ sub GetTimeFromString($) {
}
}
sub GetSecondsFromString($) {
my ($timeStr) = @_;
eval {
use Time::Local;
if(defined($timeStr) and $timeStr =~ m/^(\d{4})-(\d{2})-(\d{2})T([0-2]\d):([0-5]\d):([0-5]\d)Z$/) {
my $time = timelocal($6, $5, $4, $3, $2 - 1, $1 - 1900);
return $time;
}
}
}
sub SetRobot($$) {
my ( $hash, $robot ) = @_;
my $name = $hash->{NAME};
@ -1905,7 +1977,14 @@ sub wsMasking($$) {
<li><code>get &lt;name&gt; batteryPercent</code>
<br>
requests the state of the battery from Robot
</li><br>
</li>
<br>
<a name="statistics"></a>
<li><code>get &lt;name&gt; statistics</code>
<br>
display statistical data, extracted from available maps of recent cleanings
</li>
<br>
</ul>
<a name="BOTVACset"></a>