70_BRAVIA.pm: own package, new command stack, new reading application

git-svn-id: https://svn.fhem.de/fhem/trunk/fhem@18986 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
vuffiraa 2019-03-21 20:32:55 +00:00
parent e046f09934
commit d258712d7b
3 changed files with 567 additions and 524 deletions

View File

@ -1,5 +1,8 @@
# Add changes at the top of the list. Keep it in ASCII, and 80-char wide. # 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. # Do not insert empty lines here, update check depends on it.
- changed: 70_BRAVIA: optimized command queue,
introduced own package,
new reading application
- feature: 93_Log2Syslog: parseProfile = Automatic for automatic protocol - feature: 93_Log2Syslog: parseProfile = Automatic for automatic protocol
detection (default) if a collector is defined detection (default) if a collector is defined
- feature: 93_DbRep: attribute "sqlCmdVars" to set SQL variables or PRAGMA - feature: 93_DbRep: attribute "sqlCmdVars" to set SQL variables or PRAGMA

View File

@ -165,7 +165,7 @@ sub GetStatus($;$) {
my $name = $hash->{NAME}; my $name = $hash->{NAME};
my $interval = $hash->{INTERVAL}; my $interval = $hash->{INTERVAL};
my @successor = (); my @successor = ();
Log3($name, 5, "BOTVAC $name: called function GetStatus()"); Log3($name, 5, "BOTVAC $name: called function GetStatus()");
# use actionInterval if state is busy or paused # use actionInterval if state is busy or paused
@ -260,7 +260,7 @@ sub Set($@) {
$usage .= " findMe:noArg" if ( GetServiceVersion($hash, "findMe") eq "basic-1" ); $usage .= " findMe:noArg" if ( GetServiceVersion($hash, "findMe") eq "basic-1" );
$usage .= " startManual:noArg" if ( GetServiceVersion($hash, "manualCleaning") ne "" ); $usage .= " startManual:noArg" if ( GetServiceVersion($hash, "manualCleaning") ne "" );
$usage .= " statusRequest:noArg schedule:on,off syncRobots:noArg"; $usage .= " statusRequest:noArg schedule:on,off syncRobots:noArg";
# house cleaning # house cleaning
$usage .= " nextCleaningMode:eco,turbo" if ($houseCleaningSrv =~ /basic-\d/); $usage .= " nextCleaningMode:eco,turbo" if ($houseCleaningSrv =~ /basic-\d/);
$usage .= " nextCleaningNavigationMode:normal,extra#care" if ($houseCleaningSrv eq "minimal-2"); $usage .= " nextCleaningNavigationMode:normal,extra#care" if ($houseCleaningSrv eq "minimal-2");
@ -442,7 +442,7 @@ sub Set($@) {
Log3($name, 2, "BOTVAC Can't set robot, run 'syncRobots' before"); Log3($name, 2, "BOTVAC Can't set robot, run 'syncRobots' before");
} }
} }
# reloadMaps # reloadMaps
elsif ( $a[1] eq "reloadMaps" ) { elsif ( $a[1] eq "reloadMaps" ) {
Log3($name, 2, "BOTVAC set $name $arg"); Log3($name, 2, "BOTVAC set $name $arg");
@ -490,7 +490,7 @@ sub Set($@) {
readingsSingleUpdate($hash, $a[1], $a[2], 0); readingsSingleUpdate($hash, $a[1], $a[2], 0);
} }
# wsCommand || wsCommand # wsCommand || wsCommand
elsif ( $a[1] =~ /wsCombo|wsCommand/) { elsif ( $a[1] =~ /wsCombo|wsCommand/) {
Log3($name, 2, "BOTVAC set $name $arg"); Log3($name, 2, "BOTVAC set $name $arg");
@ -500,7 +500,7 @@ sub Set($@) {
my $cmd = ($a[1] eq "wsCombo" ? "combo" : "command"); my $cmd = ($a[1] eq "wsCombo" ? "combo" : "command");
wsEncode($hash, "{ \"$cmd\": \"$a[2]\" }"); wsEncode($hash, "{ \"$cmd\": \"$a[2]\" }");
} }
# password # password
elsif ( $a[1] eq "password") { elsif ( $a[1] eq "password") {
Log3($name, 2, "BOTVAC set $name " . $a[1]); Log3($name, 2, "BOTVAC set $name " . $a[1]);
@ -619,7 +619,7 @@ sub SendCommand($$;$$@) {
my $URL = "https://"; my $URL = "https://";
my $response; my $response;
my $return; my $return;
my %sslArgs; my %sslArgs;
if ($service ne "sessions" && $service ne "dashboard") { if ($service ne "sessions" && $service ne "dashboard") {
@ -687,7 +687,7 @@ sub SendCommand($$;$$@) {
$data .= "\"category\":2"; $data .= "\"category\":2";
$data .= ",\"mode\":"; $data .= ",\"mode\":";
$data .= (GetCleaningParameter($hash, "cleaningMode", "eco") eq "eco" ? "1" : "2"); $data .= (GetCleaningParameter($hash, "cleaningMode", "eco") eq "eco" ? "1" : "2");
$data .= ",\"modifier\":1"; $data .= ",\"modifier\":1";
} elsif ($version eq "minimal-2") { } elsif ($version eq "minimal-2") {
$data .= "\"category\":2"; $data .= "\"category\":2";
$data .= ",\"navigationMode\":"; $data .= ",\"navigationMode\":";
@ -711,7 +711,7 @@ sub SendCommand($$;$$@) {
my $zone = GetCleaningParameter($hash, "cleaningZone", ""); my $zone = GetCleaningParameter($hash, "cleaningZone", "");
$data .= ",\"boundaryId\":\"".$zone."\"" if ($zone ne ""); $data .= ",\"boundaryId\":\"".$zone."\"" if ($zone ne "");
} }
} }
$data .= "}"; $data .= "}";
} }
elsif ($cmd eq "startSpot") { elsif ($cmd eq "startSpot") {
@ -724,7 +724,7 @@ sub SendCommand($$;$$@) {
$data .= (GetCleaningParameter($hash, "cleaningMode", "eco") eq "eco" ? "1" : "2"); $data .= (GetCleaningParameter($hash, "cleaningMode", "eco") eq "eco" ? "1" : "2");
} }
if ($version eq "basic-1" or $version eq "minimal-2") { if ($version eq "basic-1" or $version eq "minimal-2") {
$data .= ",\"modifier\":"; $data .= ",\"modifier\":";
$data .= (GetCleaningParameter($hash, "cleaningModifier", "normal") eq "normal" ? "1" : "2"); $data .= (GetCleaningParameter($hash, "cleaningModifier", "normal") eq "normal" ? "1" : "2");
} }
if ($version eq "micro-2" or $version eq "minimal-2") { if ($version eq "micro-2" or $version eq "minimal-2") {
@ -732,14 +732,14 @@ sub SendCommand($$;$$@) {
$data .= (GetCleaningParameter($hash, "cleaningNavigationMode", "normal") eq "normal" ? "1" : "2"); $data .= (GetCleaningParameter($hash, "cleaningNavigationMode", "normal") eq "normal" ? "1" : "2");
} }
if ($version eq "basic-1" or $version eq "basic-3") { if ($version eq "basic-1" or $version eq "basic-3") {
$data .= ",\"spotWidth\":"; $data .= ",\"spotWidth\":";
$data .= GetCleaningParameter($hash, "cleaningSpotWidth", "200"); $data .= GetCleaningParameter($hash, "cleaningSpotWidth", "200");
$data .= ",\"spotHeight\":"; $data .= ",\"spotHeight\":";
$data .= GetCleaningParameter($hash, "cleaningSpotHeight", "200"); $data .= GetCleaningParameter($hash, "cleaningSpotHeight", "200");
} }
$data .= "}"; $data .= "}";
} }
elsif ($cmd eq "setMapBoundaries" or $cmd eq "getMapBoundaries") { elsif ($cmd eq "setMapBoundaries" or $cmd eq "getMapBoundaries") {
if (defined($option) and ref($option) eq "HASH") { if (defined($option) and ref($option) eq "HASH") {
$data .= ",\"params\":{"; $data .= ",\"params\":{";
foreach( keys %$option ) { foreach( keys %$option ) {
@ -749,7 +749,7 @@ sub SendCommand($$;$$@) {
$data .= "}"; $data .= "}";
} }
} }
$data .= "}"; $data .= "}";
my $now = time(); my $now = time();
@ -804,7 +804,7 @@ sub ReceiveCommand($$$) {
my @successor = @{$param->{successor}}; my @successor = @{$param->{successor}};
my $rc = ( $param->{buf} ) ? $param->{buf} : $param; my $rc = ( $param->{buf} ) ? $param->{buf} : $param;
my $loadMap; my $loadMap;
my $return; my $return;
my $reqId = 0; my $reqId = 0;
@ -824,7 +824,7 @@ sub ReceiveCommand($$$) {
# keep last state # keep last state
#readingsBulkUpdateIfChanged( $hash, "state", "Error" ); #readingsBulkUpdateIfChanged( $hash, "state", "Error" );
# stop pulling for current interval # stop pulling for current interval
Log3($name, 4, "BOTVAC $name: drop successors"); Log3($name, 4, "BOTVAC $name: drop successors");
LogSuccessors($hash, @successor); LogSuccessors($hash, @successor);
@ -833,7 +833,7 @@ sub ReceiveCommand($$$) {
# data received # data received
elsif ($data) { elsif ($data) {
if ( !defined($cmd) ) { if ( !defined($cmd) ) {
Log3($name, 4, "BOTVAC $name: RCV $service"); Log3($name, 4, "BOTVAC $name: RCV $service");
} else { } else {
@ -945,13 +945,13 @@ sub ReceiveCommand($$$) {
} }
} }
} }
#remove outdated calendar information #remove outdated calendar information
foreach ( keys %currentEvents ) { foreach ( keys %currentEvents ) {
delete( $hash->{READINGS}{$_} ); delete( $hash->{READINGS}{$_} );
} }
} }
} }
elsif ( $cmd eq "getMapBoundaries" ) { elsif ( $cmd eq "getMapBoundaries" ) {
if ( ref($return->{data}) eq "HASH" ) { if ( ref($return->{data}) eq "HASH" ) {
$reqId = $return->{reqId}; $reqId = $return->{reqId};
@ -997,7 +997,7 @@ sub ReceiveCommand($$$) {
readingsBulkUpdateIfChanged($hash, "floorplan_".$reqId."_zones", $zonesList); readingsBulkUpdateIfChanged($hash, "floorplan_".$reqId."_zones", $zonesList);
} }
} }
} }
elsif ( $cmd eq "getGeneralInfo" ) { elsif ( $cmd eq "getGeneralInfo" ) {
if ( ref($return->{data}) eq "HASH" ) { if ( ref($return->{data}) eq "HASH" ) {
my $generalInfo = $return->{data}; my $generalInfo = $return->{data};
@ -1017,11 +1017,11 @@ sub ReceiveCommand($$$) {
# sendToBase, setMapBoundaries, getRobotManualCleaningInfo # sendToBase, setMapBoundaries, getRobotManualCleaningInfo
if ( ref($return) eq "HASH" ) { if ( ref($return) eq "HASH" ) {
push(@successor , ["robots", "maps"]) push(@successor , ["robots", "maps"])
if ($cmd eq "setMapBoundaries" or if ($cmd eq "setMapBoundaries" or
(defined($return->{state}) and (defined($return->{state}) and
($return->{state} == 1 or $return->{state} == 4) and # Idle or Error ($return->{state} == 1 or $return->{state} == 4) and # Idle or Error
$return->{state} != ReadingsNum($name, "stateId", $return->{state}))); $return->{state} != ReadingsNum($name, "stateId", $return->{state})));
#readingsBulkUpdateIfChanged($hash, "version", $return->{version}); #readingsBulkUpdateIfChanged($hash, "version", $return->{version});
#readingsBulkUpdateIfChanged($hash, "data", $return->{data}); #readingsBulkUpdateIfChanged($hash, "data", $return->{data});
readingsBulkUpdateIfChanged($hash, "result", $return->{result}) if (defined($return->{result})); readingsBulkUpdateIfChanged($hash, "result", $return->{result}) if (defined($return->{result}));
@ -1090,14 +1090,14 @@ sub ReceiveCommand($$$) {
} }
} }
} }
# Sessions # Sessions
elsif ( $service eq "sessions" ) { elsif ( $service eq "sessions" ) {
if ( ref($return) eq "HASH" and defined($return->{access_token})) { if ( ref($return) eq "HASH" and defined($return->{access_token})) {
readingsBulkUpdateIfChanged($hash, ".accessToken", $return->{access_token}); readingsBulkUpdateIfChanged($hash, ".accessToken", $return->{access_token});
} }
} }
# dashboard # dashboard
elsif ( $service eq "dashboard" ) { elsif ( $service eq "dashboard" ) {
if ( ref($return) eq "HASH" ) { if ( ref($return) eq "HASH" ) {
@ -1121,7 +1121,7 @@ sub ReceiveCommand($$$) {
$hash->{helper}{ROBOTS} = \@robotList; $hash->{helper}{ROBOTS} = \@robotList;
SetRobot($hash, ReadingsNum($name, "robot", 0)); SetRobot($hash, ReadingsNum($name, "robot", 0));
push(@successor , ["robots", "maps"]); push(@successor , ["robots", "maps"]);
} }
} }
@ -1167,7 +1167,7 @@ sub ReceiveCommand($$$) {
} }
} }
} }
# loadmap # loadmap
elsif ( $service eq "loadmap" ) { elsif ( $service eq "loadmap" ) {
readingsBulkUpdate($hash, ".map_cache", $data) readingsBulkUpdate($hash, ".map_cache", $data)
@ -1181,12 +1181,12 @@ sub ReceiveCommand($$$) {
} }
readingsEndUpdate( $hash, 1 ); readingsEndUpdate( $hash, 1 );
if ($loadMap) { if ($loadMap) {
my $url = ReadingsVal($name, ".map_url", ""); my $url = ReadingsVal($name, ".map_url", "");
push(@successor , ["loadmap", $url]) if ($url ne ""); push(@successor , ["loadmap", $url]) if ($url ne "");
} }
if (@successor) { if (@successor) {
my @nextCmd = @{shift(@successor)}; my @nextCmd = @{shift(@successor)};
my $cmdLength = @nextCmd; my $cmdLength = @nextCmd;
@ -1279,13 +1279,13 @@ sub StorePassword($$) {
$key = Digest::MD5::md5_hex(unpack "H*", $key); $key = Digest::MD5::md5_hex(unpack "H*", $key);
$key .= Digest::MD5::md5_hex($key); $key .= Digest::MD5::md5_hex($key);
} }
for my $char (split //, $password) { for my $char (split //, $password) {
my $encode=chop($key); my $encode=chop($key);
$enc_pwd.=sprintf("%.2x",ord($char)^ord($encode)); $enc_pwd.=sprintf("%.2x",ord($char)^ord($encode));
$key=$encode.$key; $key=$encode.$key;
} }
my $err = setKeyValue($index, $enc_pwd); my $err = setKeyValue($index, $enc_pwd);
return "error while saving the password - $err" if(defined($err)); return "error while saving the password - $err" if(defined($err));
@ -1298,16 +1298,16 @@ sub ReadPassword($) {
my $index = $hash->{TYPE}."_".$hash->{NAME}."_passwd"; my $index = $hash->{TYPE}."_".$hash->{NAME}."_passwd";
my $key = getUniqueId().$index; my $key = getUniqueId().$index;
my ($password, $err); my ($password, $err);
Log3($name, 4, "BOTVAC $name: Read password from file"); Log3($name, 4, "BOTVAC $name: Read password from file");
($err, $password) = getKeyValue($index); ($err, $password) = getKeyValue($index);
if ( defined($err) ) { if ( defined($err) ) {
Log3($name, 3, "BOTVAC $name: unable to read password from file: $err"); Log3($name, 3, "BOTVAC $name: unable to read password from file: $err");
return undef; return undef;
} }
if ( defined($password) ) { if ( defined($password) ) {
if ( eval "use Digest::MD5;1" ) { if ( eval "use Digest::MD5;1" ) {
$key = Digest::MD5::md5_hex(unpack "H*", $key); $key = Digest::MD5::md5_hex(unpack "H*", $key);
@ -1333,22 +1333,22 @@ sub CheckRegistration($$$$$) {
if (ReadingsVal($name, ".secretKey", "") eq "") { if (ReadingsVal($name, ".secretKey", "") eq "") {
my @nextCmd = ($service, $cmd, $option); my @nextCmd = ($service, $cmd, $option);
unshift(@successor, [$service, $cmd, $option]); unshift(@successor, [$service, $cmd, $option]);
my @succ_item; my @succ_item;
my $msg = " successor:"; my $msg = " successor:";
for (my $i = 0; $i < @successor; $i++) { for (my $i = 0; $i < @successor; $i++) {
@succ_item = @{$successor[$i]}; @succ_item = @{$successor[$i]};
$msg .= " $i: "; $msg .= " $i: ";
$msg .= join(",", map { defined($_) ? $_ : '' } @succ_item); $msg .= join(",", map { defined($_) ? $_ : '' } @succ_item);
} }
Log3($name, 4, "BOTVAC created".$msg); Log3($name, 4, "BOTVAC created".$msg);
SendCommand($hash, "sessions", undef, undef, @successor) if (ReadingsVal($name, ".accessToken", "") eq ""); SendCommand($hash, "sessions", undef, undef, @successor) if (ReadingsVal($name, ".accessToken", "") eq "");
SendCommand($hash, "dashboard", undef, undef, @successor) if (ReadingsVal($name, ".accessToken", "") ne ""); SendCommand($hash, "dashboard", undef, undef, @successor) if (ReadingsVal($name, ".accessToken", "") ne "");
return 1; return 1;
} }
return; return;
} }
@ -1557,15 +1557,14 @@ sub LogSuccessors($@) {
my ($hash,@successor) = @_; my ($hash,@successor) = @_;
my $name = $hash->{NAME}; my $name = $hash->{NAME};
my $msg = "BOTVAC $name: RCV successors"; my $msg = "BOTVAC $name: successors";
my @succ_item; my @succ_item;
for (my $i = 0; $i < @successor; $i++) { for (my $i = 0; $i < @successor; $i++) {
@succ_item = @{$successor[$i]}; @succ_item = @{$successor[$i]};
$msg .= " $i: "; $msg .= " $i: ";
$msg .= join(",", map { defined($_) ? $_ : '' } @succ_item); $msg .= join(",", map { defined($_) ? $_ : '' } @succ_item);
} }
Log3($name, 4, $msg); Log3($name, 4, $msg) if (@successor > 0);
} }
sub ShowMap($;$$) { sub ShowMap($;$$) {
@ -1575,23 +1574,23 @@ sub ShowMap($;$$) {
$img .= ' width="'.$width.'"' if (defined($width)); $img .= ' width="'.$width.'"' if (defined($width));
$img .= ' width="'.$height.'"' if (defined($height)); $img .= ' width="'.$height.'"' if (defined($height));
$img .= ' alt="Map currently not available">'; $img .= ' alt="Map currently not available">';
return $img; return $img;
} }
sub GetMap() { sub GetMap() {
my ($request) = @_; my ($request) = @_;
if ($request =~ /^\/BOTVAC\/(\w+)\/map/) { if ($request =~ /^\/BOTVAC\/(\w+)\/map/) {
my $name = $1; my $name = $1;
my $width = $3; my $width = $3;
my $height = $5; my $height = $5;
return ("image/png", ReadingsVal($name, ".map_cache", "")); return ("image/png", ReadingsVal($name, ".map_cache", ""));
} }
return ("text/plain; charset=utf-8", "No BOTVAC device for webhook $request"); return ("text/plain; charset=utf-8", "No BOTVAC device for webhook $request");
} }
####################################### #######################################
@ -1604,7 +1603,7 @@ sub wsOpen($$$) {
Log3($name, 4, "BOTVAC(ws) $name: Establishing socket connection"); Log3($name, 4, "BOTVAC(ws) $name: Establishing socket connection");
$hash->{DeviceName} = join(':', $ip_address, $port); $hash->{DeviceName} = join(':', $ip_address, $port);
::DevIo_CloseDev($hash) if(::DevIo_IsOpen($hash)); ::DevIo_CloseDev($hash) if(::DevIo_IsOpen($hash));
if (::DevIo_OpenDev($hash, 0, "BOTVAC::wsHandshake")) { if (::DevIo_OpenDev($hash, 0, "BOTVAC::wsHandshake")) {
Log3($name, 2, "BOTVAC(ws) $name: ERROR: Can't open websocket to $hash->{DeviceName}"); Log3($name, 2, "BOTVAC(ws) $name: ERROR: Can't open websocket to $hash->{DeviceName}");
@ -1640,7 +1639,7 @@ sub wsHandshake($) {
my $date = FmtDateTimeRFC1123($now); my $date = FmtDateTimeRFC1123($now);
my $message = lc($serial) . "\n" . $date . "\n"; my $message = lc($serial) . "\n" . $date . "\n";
my $hmac = hmac_sha256_hex($message, ReadingsVal($name, ".secretKey", "")); my $hmac = hmac_sha256_hex($message, ReadingsVal($name, ".secretKey", ""));
my $wsHandshakeCmd = "GET $path HTTP/1.1\r\n"; my $wsHandshakeCmd = "GET $path HTTP/1.1\r\n";
$wsHandshakeCmd .= "Host: $host:$port\r\n"; $wsHandshakeCmd .= "Host: $host:$port\r\n";
$wsHandshakeCmd .= "Sec-WebSocket-Key: $wsKey\r\n"; $wsHandshakeCmd .= "Sec-WebSocket-Key: $wsKey\r\n";
@ -1650,13 +1649,13 @@ sub wsHandshake($) {
$wsHandshakeCmd .= "Date: $date\r\n"; $wsHandshakeCmd .= "Date: $date\r\n";
$wsHandshakeCmd .= "Authorization: NEATOAPP $hmac\r\n"; $wsHandshakeCmd .= "Authorization: NEATOAPP $hmac\r\n";
$wsHandshakeCmd .= "Connection: Upgrade\r\n"; $wsHandshakeCmd .= "Connection: Upgrade\r\n";
$wsHandshakeCmd .= "\r\n"; $wsHandshakeCmd .= "\r\n";
Log3($name, 4, "BOTVAC(ws) $name: Starting Websocket Handshake"); Log3($name, 4, "BOTVAC(ws) $name: Starting Websocket Handshake");
wsWrite($hash,$wsHandshakeCmd); wsWrite($hash,$wsHandshakeCmd);
$hash->{HELPER}{wsKey} = $wsKey; $hash->{HELPER}{wsKey} = $wsKey;
return undef; return undef;
} }
@ -1696,7 +1695,7 @@ sub wsCheckHandshake($$) {
sub wsWrite($@) { sub wsWrite($@) {
my ($hash,$string) = @_; my ($hash,$string) = @_;
my $name = $hash->{NAME}; my $name = $hash->{NAME};
Log3($name, 4, "BOTVAC(ws) $name: WriteFn called:\n$string"); Log3($name, 4, "BOTVAC(ws) $name: WriteFn called:\n$string");
::DevIo_SimpleWrite($hash, $string, 0); ::DevIo_SimpleWrite($hash, $string, 0);
@ -1728,7 +1727,7 @@ sub wsCallback(@) {
my ($param, $err, $data) = @_; my ($param, $err, $data) = @_;
my $hash = $param->{hash}; my $hash = $param->{hash};
my $name = $hash->{NAME}; my $name = $hash->{NAME};
if($err){ if($err){
Log3($name, 3, "received callback with error:\n$err"); Log3($name, 3, "received callback with error:\n$err");
} elsif($data){ } elsif($data){
@ -1793,10 +1792,10 @@ sub wsEncode($$;$$) {
$wsString .= pack 'N', $len >> 32; $wsString .= pack 'N', $len >> 32;
$wsString .= pack 'N', ($len & 0xffffffff); $wsString .= pack 'N', ($len & 0xffffffff);
} }
if ($masked) { if ($masked) {
my $mask = pack 'N', int(rand(2**32)); my $mask = pack 'N', int(rand(2**32));
$wsString .= $mask; $wsString .= $mask;
$wsString .= wsMasking($payload, $mask); $wsString .= wsMasking($payload, $mask);
} else { } else {
$wsString .= $payload; $wsString .= $payload;
} }
@ -1815,9 +1814,9 @@ sub wsPong($) {
sub wsDecode($$) { sub wsDecode($$) {
my ($hash,$wsString) = @_; my ($hash,$wsString) = @_;
my $name = $hash->{NAME}; my $name = $hash->{NAME};
Log3($name, 5, "BOTVAC(ws) $name: String:\n" . $wsString); Log3($name, 5, "BOTVAC(ws) $name: String:\n" . $wsString);
while (length $wsString) { while (length $wsString) {
my $FIN = (ord(substr($wsString,0,1)) & 0b10000000) >> 7; my $FIN = (ord(substr($wsString,0,1)) & 0b10000000) >> 7;
my $OPCODE = (ord(substr($wsString,0,1)) & 0b00001111); my $OPCODE = (ord(substr($wsString,0,1)) & 0b00001111);
@ -2050,7 +2049,7 @@ sub wsMasking($$) {
<ul> <ul>
<li><code>house</code> - cleaning without a persisted map</li> <li><code>house</code> - cleaning without a persisted map</li>
<li><code>map</code> - cleaning with a persisted map</li> <li><code>map</code> - cleaning with a persisted map</li>
<li><code>zone</code> - cleaning in a specific zone, set zone with nextCleaningZone</li> <li><code>zone</code> - cleaning in a specific zone, set zone with nextCleaningZone</li>
</ul> </ul>
</li> </li>
<br> <br>

File diff suppressed because it is too large Load Diff