From f7b7c3cef2fa3fc89dd83e48c7633dfde9d689a9 Mon Sep 17 00:00:00 2001 From: vuffiraa <> Date: Sun, 9 Jun 2019 18:11:55 +0000 Subject: [PATCH] 70_BOTVAC.pm: add pollingMode add preferences use whitespace to separate boundaries remove style element from cleaning statistics git-svn-id: https://svn.fhem.de/fhem/trunk@19586 2b470e98-0d58-463d-a4d8-8e2adae1ed80 --- fhem/CHANGED | 4 + fhem/FHEM/70_BOTVAC.pm | 240 ++++++++++++++++++++++++++++------------- 2 files changed, 167 insertions(+), 77 deletions(-) diff --git a/fhem/CHANGED b/fhem/CHANGED index 4058d1d79..552d232bc 100644 --- a/fhem/CHANGED +++ b/fhem/CHANGED @@ -1,5 +1,9 @@ # 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 pollingMode + add preferences + use whitespace to separate boundaries + remove style element from cleaning statistics - bugfix: 73_AutoShuttersControl: fix litte bugs and change manual drive detection - feature: 49_SSCam: In detailview are buttons provided to open the camera diff --git a/fhem/FHEM/70_BOTVAC.pm b/fhem/FHEM/70_BOTVAC.pm index b9a812bf0..5c11d80e3 100755 --- a/fhem/FHEM/70_BOTVAC.pm +++ b/fhem/FHEM/70_BOTVAC.pm @@ -174,7 +174,7 @@ sub GetStatus($;$) { RemoveInternalTimer($hash); InternalTimer( gettimeofday() + $interval, "BOTVAC::GetStatus", $hash, 0 ); - return if ( AttrVal($name, "disable", 0) == 1 ); + return if ( AttrVal($name, "disable", 0) == 1 or ReadingsVal($name,"pollingMode",1) == 0); # check device availability if (!$update) { @@ -186,6 +186,7 @@ sub GetStatus($;$) { push(@successor, ["messages", "getSchedule"]); push(@successor, ["messages", "getGeneralInfo"]) if (GetServiceVersion($hash, "generalInfo") =~ /.*-1/); + push(@successor, ["messages", "getPreferences"]) if (GetServiceVersion($hash, "preferences") ne ""); SendCommand($hash, "messages", "getRobotState", undef, @successor); } @@ -265,7 +266,13 @@ sub Set($@) { $usage .= " dismissCurrentAlert:noArg" if ( ReadingsVal($name, "alert", "") ne "" ); $usage .= " findMe:noArg" if ( GetServiceVersion($hash, "findMe") eq "basic-1" ); $usage .= " startManual:noArg" if ( GetServiceVersion($hash, "manualCleaning") ne "" ); - $usage .= " statusRequest:noArg schedule:on,off syncRobots:noArg"; + $usage .= " statusRequest:noArg schedule:on,off syncRobots:noArg pollingMode:on,off"; + + # preferences + $usage .= " robotSounds:on,off" if ( GetServiceVersion($hash, "preferences") !~ /()|(basic-1)/ ); + $usage .= " dirtbinAlertReminderInterval:30,60,90,120,150" if ( GetServiceVersion($hash, "preferences") =~ /(basic-\d)|(advanced-\d)/ ); + $usage .= " filterChangeReminderInterval:1,2,3" if ( GetServiceVersion($hash, "preferences") =~ /(basic-\d)|(advanced-\d)/ ); + $usage .= " brushChangeReminderInterval:4,5,6,7,8" if ( GetServiceVersion($hash, "preferences") =~ /(basic-\d)|(advanced-\d)/ ); # house cleaning $usage .= " nextCleaningMode:eco,turbo" if ($houseCleaningSrv =~ /basic-\d/); @@ -308,7 +315,7 @@ sub Set($@) { my $name = $Boundaries[$i]->{name}; push @names,$name if (!(grep { $_ eq $name } @names) and ($name ne "")); } - my $BoundariesList = @names ? join(",", @names) : "textField"; + my $BoundariesList = @names ? "multiple-strict,".join(",", @names) : "textField"; $usage .= " setBoundariesOnFloorplan_0:".$BoundariesList if (ReadingsVal($name, "floorplan_0_id" ,"") ne ""); $usage .= " setBoundariesOnFloorplan_1:".$BoundariesList if (ReadingsVal($name, "floorplan_1_id" ,"") ne ""); $usage .= " setBoundariesOnFloorplan_2:".$BoundariesList if (ReadingsVal($name, "floorplan_2_id" ,"") ne ""); @@ -427,8 +434,12 @@ sub Set($@) { # statusRequest elsif ( $a[1] eq "statusRequest" ) { Log3($name, 2, "BOTVAC set $name $arg"); + + my @successor = (); + push(@successor, ["messages", "getPreferences"]) if (GetServiceVersion($hash, "preferences") ne ""); + push(@successor, ["messages", "getSchedule"]); - SendCommand( $hash, "messages", "getRobotState", undef, ["messages", "getSchedule"] ); + SendCommand( $hash, "messages", "getRobotState", undef, @successor ); } # setRobot @@ -516,6 +527,39 @@ sub Set($@) { StorePassword( $hash, $a[2] ); } + # pollingMode + elsif ( $a[1] eq "pollingMode") { + Log3($name, 4, "BOTVAC set $name $arg"); + + return "No argument given" if ( !defined( $a[2] ) ); + + readingsSingleUpdate($hash, "pollingMode", ($a[2] eq "off" ? "0" : "1"), 0); + } + + # preferences + elsif ( $a[1] =~ /^(robotSounds|dirtbinAlertReminderInterval|filterChangeReminderInterval|brushChangeReminderInterval)$/) { + my $item = $1; + my %params; + + Log3($name, 4, "BOTVAC set $name $arg"); + + return "No argument given" if ( !defined( $a[2] ) ); + + foreach my $reading ( keys %{ $hash->{READINGS} } ) { + if ($reading =~ /^pref_(.*)/) { + my $prefName = $1; + $params{$prefName} = ReadingsVal($name, $reading, "null"); + $params{$prefName} *= 43200 if ($prefName =~ /ChangeReminderInterval/ and $params{$prefName} =~ /^\d*$/); + } + } + + return "No preferences present, execute 'set statusRequest' first." unless (keys %params); + + $params{$item} = ($item =~ /ChangeReminderInterval/ && $a[2] =~ /^\d*$/ ? $a[2] * 43200 : $a[2]); + + SendCommand( $hash, "messages", "setPreferences", \%params ); + } + # return usage hint else { return $usage; @@ -563,7 +607,7 @@ sub Attr(@) if ($attr_value !~ /^\{.*\}/){ $err = "Invalid value $attr_value for attribute $attr_name. Must be a space separated list of JSON strings."; } else { - my @boundaries = split " ",$attr_value; + my @boundaries = split(/\s/, $attr_value); my @areas; if (@boundaries > 1) { foreach my $area (@boundaries) { @@ -745,7 +789,7 @@ sub SendCommand($$;$$@) { } $data .= "}"; } - elsif ($cmd eq "setMapBoundaries" or $cmd eq "getMapBoundaries") { + elsif ($cmd eq "setMapBoundaries" or $cmd eq "getMapBoundaries" or $cmd eq "setPreferences") { if (defined($option) and ref($option) eq "HASH") { $data .= ",\"params\":{"; foreach( keys %$option ) { @@ -755,7 +799,6 @@ sub SendCommand($$;$$@) { $data .= "}"; } } - $data .= "}"; my $now = time(); @@ -1022,7 +1065,7 @@ sub ReceiveCommand($$$) { } else { # getRobotState, startCleaning, pauseCleaning, stopCleaning, resumeCleaning, - # sendToBase, setMapBoundaries, getRobotManualCleaningInfo + # sendToBase, setMapBoundaries, getRobotManualCleaningInfo, getPreferences if ( ref($return) eq "HASH" ) { push(@successor , ["robots", "maps"]) if ($cmd eq "setMapBoundaries" or @@ -1048,6 +1091,16 @@ sub ReceiveCommand($$$) { readingsBulkUpdateIfChanged($hash, "wlanValidity", "unavailable"); } } + if ($cmd eq "getPreferences") { + if ( ref($return->{data}) eq "HASH") { + my $data = $return->{data}; + foreach my $key (keys %{$return->{data}}) { + my $value = $data->{$key}; + $value /= 43200 if ($key =~ /ChangeReminderInterval/ and $value =~ /^[1-9]\d*$/); + readingsBulkUpdateIfChanged($hash, "pref_$key", $value); + } + } + } if ( ref($return->{cleaning}) eq "HASH" ) { my $cleaning = $return->{cleaning}; readingsBulkUpdateIfChanged($hash, "cleaningCategory", GetCategoryText($cleaning->{category})); @@ -1298,7 +1351,7 @@ sub GetServiceVersion($$) { sub SetServices { my ($hash, $services) = @_; my $name = $hash->{NAME}; - my $serviceList = join(", ", map { "$_:$services->{$_}" } keys %$services);; + my $serviceList = join(", ", map { "$_:$services->{$_}" } keys %$services); $hash->{SERVICES} = $serviceList if (!defined($hash->{SERVICES}) or $hash->{SERVICES} ne $serviceList); } @@ -1399,7 +1452,7 @@ sub GetBoolean($) { return $booleans->{$value}; } else { return $value; - } + } } sub BuildState($$$$) { @@ -1660,79 +1713,77 @@ sub GetStatistics($) { my $model = ReadingsVal($name, "model", ""); my $ret = ""; - $ret .= ''; - $ret .= ''; - $ret .= ''; + $ret .= ''; + $ret .= '
'; $ret .= ''; - $ret .= ''; + $ret .= ''; $ret .= ''; - $ret .= ' '; - $ret .= ' '; - $ret .= ' '; - $ret .= ' '; - $ret .= ' '; - $ret .= ' '; - $ret .= ' '; - $ret .= ' '; - $ret .= ' '; - $ret .= ' '; - $ret .= ' '; - $ret .= ' '; + $ret .= ' '; + $ret .= ' '; + $ret .= ' '; + $ret .= ' '; + $ret .= ' '; + $ret .= ' '; + $ret .= ' '; + $ret .= ' '; + $ret .= ' '; + $ret .= ' '; + $ret .= ' '; + $ret .= ' '; $ret .= ''; - $ret .= ' '; - $ret .= ' '; - $ret .= ' '; - $ret .= ' '; - $ret .= ' '; - $ret .= ' '; - $ret .= ' '; - $ret .= ' '; - $ret .= ' '; - $ret .= ' '; - $ret .= ' '; - $ret .= ' '; - $ret .= ' '; + $ret .= ' '; + $ret .= ' '; + $ret .= ' '; + $ret .= ' '; + $ret .= ' '; + $ret .= ' '; + $ret .= ' '; + $ret .= ' '; + $ret .= ' '; + $ret .= ' '; + $ret .= ' '; + $ret .= ' '; + $ret .= ' '; $ret .= ''; - $ret .= ' '; - $ret .= ' '; - $ret .= ' '; - $ret .= ' '; - $ret .= ' '; - $ret .= ' '; - $ret .= ' '; - $ret .= ' '; - $ret .= ' '; - $ret .= ' '; - $ret .= ' '; - $ret .= ' '; - $ret .= ''; + $ret .= ' '; + $ret .= ' '; + $ret .= ' '; + $ret .= ' '; + $ret .= ' '; + $ret .= ' '; + $ret .= ' '; + $ret .= ' '; + $ret .= ' '; + $ret .= ' '; + $ret .= ' '; + $ret .= ' '; + $ret .= ' '; + $ret .= ''; 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 .= ''; - $ret .= ' '; # Map No. - $ret .= ' '; # Expected Area - $ret .= ' '; # Expected Time - $ret .= ' '; # Map Area - $ret .= ' '; # Map Time - $ret .= ' '; # Charge Delta - $ret .= ' '; # Discharge Speed - $ret .= ' '; # Area Speed - $ret .= ' '; # Cleaning Category - $ret .= ' '; # Cleaning Mode - $ret .= ' '; # Cleaning Frequency - $ret .= ' '; # Charge During Run - $ret .= ' '; # Status - $ret .= ' '; # Date + my $expa = ($dc > 0 ? int($$map->{cleaned_area}*100/$dc+.5) : 0); + my $expt = ($dc > 0 ? int($dt*100/$dc/60+.5) : 0); + my($gen_date,$gen_time) = split(" ", GetTimeFromString($$map->{generated_at})); + $ret .= ''; + $ret .= ' '; # Map No. + $ret .= ' '; # Expected Area + $ret .= ' '; # Expected Time + $ret .= ' '; # Map Area + $ret .= ' '; # Map Time + $ret .= ' '; # Charge Delta + $ret .= ' '; # Discharge Speed + $ret .= ' '; # Area Speed + $ret .= ' '; # Cleaning Category + $ret .= ' '; # Cleaning Mode + $ret .= ' '; # Cleaning Frequency + $ret .= ' '; # Charge During Run + $ret .= ' '; # Status + $ret .= ' '; # Date + $ret .= ' '; # Time $ret .= ''; } $ret .= '
Report: '.ReadingsVal($name,"name","name").', '.InternalVal($name,"VENDOR","VENDOR").', '.ReadingsVal($name,"model","model").'
MapExpectedMapMapChargeDischargeAreaCleaningChargeStatusDateTimeMapExpectedMapMapChargeDischargeAreaCleaningChargeStatusDateTime
No.AreaTimeAreaTimeDeltaSpeedSpeedCat.ModeFreq.DuringNo.AreaTimeAreaTimeDeltaSpeedSpeedCat.ModeFreq.During
qmminqmmin%%/minqm/minRunYYYY-MM-DD hh:mm:ss
qmminqmmin%%/minqm/minRunYYYY-MM-DDhh:mm:ss
'.($i+1).''.($expa>0?$expa:0).''.($expt>0?$expt:0).''.int($$map->{cleaned_area}+.5).''.(($dt>0)?(int($dt/60+.5)):0).''.($dc>0?$dc:0).''.(($dt>0 and $dc>0)?(int($dc*600/$dt+.5)/10):0).''.(($expt>0 and $expa>0)?(int($expa*10/$expt+.5))/10:0).''.GetCategoryText($$map->{category}).''.GetModeText($$map->{mode}).''.GetModifierText($$map->{modifier}).''.$$map->{suspended_cleaning_charging_count}.'x'.$$map->{status}.''.GetTimeFromString($$map->{generated_at}).'
'.($i+1).' '.($expa>0?$expa:0).' '.($expt>0?$expt:0).' '.int($$map->{cleaned_area}+.5).' '.(($dt>0)?(int($dt/60+.5)):0).' '.($dc>0?$dc:0).' '.(($dt>0 and $dc>0)?(int($dc*600/$dt+.5)/10):0).' '.(($expt>0 and $expa>0)?(int($expa*10/$expt+.5))/10:0).' '.GetCategoryText($$map->{category}).' '.GetModeText($$map->{mode}).' '.GetModifierText($$map->{modifier}).' '.$$map->{suspended_cleaning_charging_count}.'x '.$$map->{status}.' '.$gen_date.' '.$gen_time.'
'; @@ -1748,7 +1799,7 @@ sub GetStatistics($) { $specification = "Vorwerk VR220(VR300), battery 84 Wh, eco (90 min, 120 qm, power 65 W), turbo (60 min, 90 qm, power 85 W)
" if ($model eq "VR220"); $ret .= $specification; - $ret .= ''; + $ret .= ''; return $ret; } @@ -2250,14 +2301,49 @@ sub wsMasking($$) { set <name> stop
- stop cleaning and in case of manual cleaning mode close also the websocket connection + stop cleaning and in case of manual cleaning mode close also the websocket connection.
  • set <name> syncRobots
    - sync robot data with online account. Useful if one has more then one robot registered + sync robot data with online account. Useful if one has more then one robot registered. +
  • +
    +
  • + + set <name> pollingMode <1|0> +
    + set polling on by 1 (default) or off by 0 like attribut disable. +
  • +
    +
  • + + set <name> robotSounds <true|false> +
    + set sounds on by true or off false. +
  • +
    +
  • + + set <name> dirtbinAlertReminderInterval <30|60|90|120|150> +
    + set alert intervall in minutes. +
  • +
    +
  • + + set <name> filterChangeReminderInterval <1|2|3> +
    + set alert intervall in months. +
  • +
    +
  • + + set <name> brushChangeReminderInterval <4|5|6|7|8> +
    + set alert intervall in months.

  • @@ -2311,7 +2397,7 @@ sub wsMasking($$) { boundaries
    - Boundary entries separated by space in JSON format, e.g.
    + Boundary entries separated by whitespace in JSON format, e.g.
    {"type":"polyline","vertices":[[0.710,0.6217],[0.710,0.6923]],"name":"Bad","color":"#E54B1C","enabled":true}
    {"type":"polyline","vertices":[[0.7139,0.4101],[0.7135,0.4282],[0.4326,0.3322],[0.4326,0.2533],[0.3931,0.2533], [0.3931,0.3426],[0.7452,0.4637],[0.7617,0.4196]],"name":"Kueche","color":"#000000","enabled":true}