diff --git a/fhem/contrib/Roomba/99_RoombaUtils.pm b/fhem/contrib/Roomba/99_RoombaUtils.pm index 4683dec91..3a4a9bb00 100644 --- a/fhem/contrib/Roomba/99_RoombaUtils.pm +++ b/fhem/contrib/Roomba/99_RoombaUtils.pm @@ -203,6 +203,14 @@ my %roombastates_de = ("charge" => "Wird geladen", sub command($$@){ my ($name,$cmd,@evt) = @_; my $hash = $main::defs{$name}; + if( $cmd eq "start"){ + my $hash = $main::defs{$name}; + my $iodev= $hash->{IODev}->{NAME}; + main::fhem("attr $iodev disconnectAfter 300"); + if(main::Value($iodev) ne "opened"{ + main::fhem("set $iodev connect"); + } + } $cmd = 'cmd {"command": "'.$cmd.'", "time": '.time().', "initiator": "localApp"}'; return $cmd; } @@ -212,13 +220,9 @@ sub setting($$$){ my $hash = $main::defs{$name}; $hash->{helper}{setting} = $key; my (@evt,$val,$cmd); - if( $key !~ /^local.*/){ - @evt = split(' ',$data); - $val = (defined($evt[1]))?$evt[1]:"false"; - $cmd = 'delta {"state": {"'.$key.'":'.$val.'}}'; - }else{ - $cmd = 'delta {"state": {"vacHigh":'.main::ReadingsVal($name,"sVacHigh","false").'}}'; - } + @evt = split(' ',$data); + $val = (defined($evt[1]))?$evt[1]:"false"; + $cmd = 'delta {"state": {"'.$key.'":'.$val.'}}'; return $cmd; } @@ -384,72 +388,34 @@ sub pose($$){ #-- Reduction not useful push(@{$hash->{helper}{theta}},$theta); push(@{$hash->{helper}{path}},$pxp,$pyp); + my $count = $hash->{helper}{pcount}; + $count++; + $hash->{helper}{pcount}=$count; - my %ret = ("positionTheta",$theta,"position","(".$pxp.",".$pyp.")"); + my %ret = ("positionTheta",$theta,"positionCount",$count,"position","(".$pxp.",".$pyp.")"); return {%ret}; } sub mission($$){ my ($name,$evt) = @_; - my $hash = $main::defs{$name}; + my $oldphase = main::ReadingsVal($name,"cmPhase",""); #-- getting events of the type # {"state":{"reported":{"dock":{"known":true},"cleanMissionStatus":{"cycle":"quick","phase":"run","expireM":0,"rechrgM":0,"error":0,"notReady":0,"mssnM":0,"sqft":0,"initiator":"localApp","nmain::Log 1,"[RoombaUtils] Device $name phase transition $oldphase -> $phase";Mssn":30}}}} my $dec = decode_json($evt); my $cyc = $dec->{'state'}->{'reported'}->{'cleanMissionStatus'}->{'cycle'}; my $phase = $dec->{'state'}->{'reported'}->{'cleanMissionStatus'}->{'phase'}; - # Normal Sequence is "" -> charge -> run -> hmPostMsn -> charge - # Mid mission recharge is "" -> charge -> run -> hmMidMsn -> charge - # -> run -> hmPostMsn -> charge - # Stuck is "" -> charge -> run -> hmPostMsn -> stuck - # -> run/charge/stop/hmUsrDock -> charge - # Start program during run is "" -> run -> hmPostMsn -> charge - # Need to identify a new mission to initialize map, and end of mission to - # finalise map. - # Assume charge -> run = start of mission (init map) - # stuck -> charge = init map ??? - # Assume hmPostMsn -> charge = end of mission (finalize map) - # hmPostMsn -> charge = end of mission (finalize map) - # hmUsrDock -> charge finalize map - # hmUsrDock -> stop finalize map - # Anything else = continue with existing map - my $oldphase=main::ReadingsVal($name,"cmPhase",""); - if( $oldphase.$phase eq "stuckcharge" || - $oldphase.$phase eq "chargerun" || - $oldphase.$phase eq "hmUsrDockrun" ){ - main::Log 1,"[RoombaUtils] transition $oldphase -> $phase should start intialization"; - initmap($hash); - }elsif( $oldphase.$phase eq "runstop" ){ - main::Log 1,"[RoombaUtils] pausing $oldphase -> $phase"; - }elsif( $oldphase.$phase eq "stoprun" ){ - main::Log 1,"[RoombaUtils] resuming $oldphase -> $phase"; - }elsif( - $oldphase.$phase eq "hmPostMsncharge" || - $oldphase.$phase eq "hmPostMsnstop" || - $oldphase.$phase eq "hmUsrDockcharge" || - $oldphase.$phase eq "hmUsrDockstop" || - $oldphase.$phase eq "stophmUsrDock" ){ - main::Log 1,"[RoombaUtils] transition $oldphase -> $phase should start finalization"; - finalizemap($hash); - }elsif( $oldphase.$phase eq "runrun" || - $oldphase.$phase eq "stopstop" || - $oldphase.$phase eq "chargestop" || - $oldphase.$phase eq "chargecharge"){ - # do nothing - }else{ - main::Log 1,"[RoombaUtils] Device $name phase transition $oldphase -> $phase"; - } - my $number= $dec->{'state'}->{'reported'}->{'cleanMissionStatus'}->{'nMssn'}; my $exp = $dec->{'state'}->{'reported'}->{'cleanMissionStatus'}->{'expireM'}; my $rech = $dec->{'state'}->{'reported'}->{'cleanMissionStatus'}->{'rechrgM'}; + #-- Manage mission + missionmanager($hash,$oldphase,$phase); $exp = ($exp == 0)?"Never":$exp." min"; my $time = $dec->{'state'}->{'reported'}->{'cleanMissionStatus'}->{'mssnM'}; $time = $time." min"; my $error = $dec->{'state'}->{'reported'}->{'cleanMissionStatus'}->{'error'}; my $eemsg = $roombaerrs_en{$error}; my $demsg = $roombaerrs_de{$error}; - my $notr = $dec->{'state'}->{'reported'}->{'cleanMissionStatus'}->{'notReady'}; my $sqm = int($dec->{'state'}->{'reported'}->{'cleanMissionStatus'}->{'sqft'}*10/10.7639)/10; my $init = $dec->{'state'}->{'reported'}->{'cleanMissionStatus'}->{'initiator'}; @@ -462,6 +428,9 @@ sub mission($$){ "cmPhaseD",$roombastates_de{$phase},"cmArea",$sqm." m²","cmExpire",$exp,"cmError",$eemsg,"cmErrorD",$demsg,"cmInitiator",$init); $ret{"cmNotReady"} = numtobool($notr) if(defined($notr)); + my $bat = $dec->{'state'}->{'reported'}->{'batPct'}; + $ret{"battery"} = $bat + if(defined($bat)); return {%ret}; } @@ -471,7 +440,7 @@ sub schedule($){ my @weekdays = ("Sun","Mon","Tue","Wed","Thu","Fri","Sat"); #-- getting events of the type # {"state":{"reported":{"cleanSchedule":{"cycle":["none","none","none","none","none","none","none"],"h":[9,9,9,9,9,9,9],"m":[0,0,0,0,0,0,0]},"bbchg3":{"avgMin":374,"hOnDock":199,"nAvail":32,"estCap":12311,"nLithChrg":8,"nNimhChrg":0,"nDocks":35}}}} - my $dec = decode_json($evt); + my $dec = decode_json($evt); my @acyc = @{$dec->{'state'}->{'reported'}->{'cleanSchedule'}->{'cycle'}}; my @ahours = @{$dec->{'state'}->{'reported'}->{'cleanSchedule'}->{'h'}}; my @amin = @{$dec->{'state'}->{'reported'}->{'cleanSchedule'}->{'m'}}; @@ -594,10 +563,72 @@ sub numtobool($){ my $ret = (($num==1)?"true":"false"); return $ret; } - + ############################################################################# # -# Map +# missionmanager +# +############################################################################# + +sub missionmanager($$$){ + my ($hash,$oldphase,$phase)=@_; + + my $name = $hash->{NAME}; + my $iodev = $hash->{IODev}->{NAME}; + + # Normal Sequence is "" -> charge -> run -> hmPostMsn -> charge + # Mid mission recharge is "" -> charge -> run -> hmMidMsn -> charge + # -> run -> hmPostMsn -> charge + # Stuck is "" -> charge -> run -> hmPostMsn -> stuck + # -> run/charge/stop/hmUsrDock -> charge + # Start program during run is "" -> run -> hmPostMsn -> charge + # Need to identify a new mission to initialize map, and end of mission to + # finalise map. + # Assume charge -> run = start of mission (init map) + # stuck -> charge = init map ??? + # Assume hmPostMsn -> charge = end of mission (finalize map) + # hmPostMsn -> charge = end of mission (finalize map) + # hmUsrDock -> charge finalize map + # hmUsrDock -> stop finalize map + # Anything else = continue with existing map + + if( $oldphase.$phase eq "stuckcharge" || + $oldphase.$phase eq "chargerun" || + $oldphase.$phase eq "hmUsrDockrun" ){ + main::Log 1,"[RoombaUtils] Device $name $oldphase -> $phase should start intialization"; + initmap($hash); + main::fhem("attr $iodev disconnectAfter 60"); + }elsif( $oldphase.$phase eq "runstop" ){ + main::Log 1,"[RoombaUtils] Device $name pausing $oldphase -> $phase"; + }elsif( $oldphase.$phase eq "stoprun" ){ + main::Log 1,"[RoombaUtils] Device $name resuming $oldphase -> $phase"; + }elsif( + $oldphase.$phase eq "hmPostMsncharge" || + $oldphase.$phase eq "hmPostMsnstop" || + $oldphase.$phase eq "hmUsrDockcharge" || + $oldphase.$phase eq "hmUsrDockstop" || + $oldphase.$phase eq "stophmUsrDock" ){ + main::Log 1,"[RoombaUtils] Device $name $oldphase -> $phase should start intialization"; + finalizemap($hash); + main::fhem("attr $iodev disconnectAfter 7"); + }elsif( + $oldphase.$phase eq "hmUsrDockhmUsrDock"){ + main::Log 1,"[RoombaUtils] Device $name arrived in dock after user docking"; + }elsif( $oldphase.$phase eq "runrun" || + $oldphase.$phase eq "stopstop" || + $oldphase.$phase eq "chargestop" || + $oldphase.$phase eq "chargecharge" || + $oldphase.$phase eq "hmUsrDockhmUsrDock"){ + # do nothing + }else{ + main::Log 1,"[RoombaUtils] Device $name phase transition $oldphase -> $phase"; + } + +} + +############################################################################# +# +# initmap # ############################################################################# @@ -605,20 +636,30 @@ sub initmap($){ my ($hash) = @_; $hash->{helper}{initmap}=1; $hash->{helper}{path}=(); - $hash->{helper}{hull}=(); + $hash->{helper}{pcount}=0; $hash->{helper}{theta}=(); $hash->{helper}{thetaold}=undef; main::Log 1,"[RoombaUtils] Initialization of map for device ".$hash->{NAME}; main::fhem("setreading ".($hash->{NAME})." cmMap initialized"); } + +############################################################################# +# +# listmaps +# +############################################################################# + sub listmaps($){ my ($name) = @_; my $hash = $main::defs{$name}; my $out = ""; + #my $now = main::TimeNow(); my $run = 0; + #main::Log 1,"[RoombaUtils] mapping for device $name"; + my ($fhb,$fhc); my $svgdir = main::AttrVal($name,"SVG_dir","/opt/fhem/www/images"); @@ -642,10 +683,19 @@ sub listmaps($){ } close($fhb); } - #main::fhem("setreading $name cmMapList ",$out,1); - return $out; + #main::Log 1,"[RoombaUtils] setting READING cmMapList for device $name at time $now"; + #$hash->{READINGS}{cmMapList}{VAL}=$out; + #$hash->{READINGS}{cmMapList}{TIME}=$now; + main::fhem("setreading $name cmMapList $out"); + return } +############################################################################# +# +# delmap +# +############################################################################# + sub delmap($$){ my ($name,$evt) = @_; return @@ -654,8 +704,11 @@ sub delmap($$){ my $hash = $main::defs{$name}; my $out = ""; + #my $now = main::TimeNow(); my $run = 0; + #main::Log 1,"[RoombaUtils] deleting run $rundel for device $name"; + my ($fhb,$fhc); my $svgdir = main::AttrVal($name,"SVG_dir","/opt/fhem/www/images"); @@ -697,13 +750,21 @@ sub delmap($$){ close($fhb); rename $svgdir."/".$filename2b.".tmp",$svgdir."/".$filename2b; } - return $out; + + main::fhem("setreading $name cmMapList $out"); + return } +############################################################################# +# +# finalizemap +# +############################################################################# + sub finalizemap($){ my ($hash) = @_; - my $name = $hash->{NAME}; + my $name = $hash->{NAME}; if(!defined($hash->{helper}{path})){ main::Log 1,"[RoombaUtils] Finalization of map for device $name not possible, path undefined"; return @@ -823,9 +884,7 @@ sub finalizemap($){ #-- save a lot of memory $hash->{helper}{path}=(); - $hash->{helper}{hull}=(); $hash->{helper}{theta}=(); - $hash->{helper}{velocity}=(); ####################################################### #-- prepare content and filename for file 2 @@ -955,9 +1014,8 @@ sub finalizemap($){ #-- write output to collection file print $fhb $out2; close($fhb); - - main::fhem("setreading ".($hash->{NAME})." cmMap finalized as ".$filename2c.""); - + main::fhem("setreading $name cmMap finalized as ".$filename2c.""); + listmaps($name) } 1;