From 3d93169a094378438c2b6cd1556c3b82af25633b Mon Sep 17 00:00:00 2001 From: Olaf Bock Date: Sun, 12 Nov 2017 11:09:20 +0100 Subject: [PATCH 1/2] Nuki response HTTP 503: Handling of response corrected --- 73_NUKIBridge.pm | 25 +++++++++++++++++-------- 74_NUKIDevice.pm | 19 ++++++++++--------- 2 files changed, 27 insertions(+), 17 deletions(-) diff --git a/73_NUKIBridge.pm b/73_NUKIBridge.pm index 8b859df..082462f 100644 --- a/73_NUKIBridge.pm +++ b/73_NUKIBridge.pm @@ -46,8 +46,8 @@ use JSON; use HttpUtils; -my $version = "0.6.1"; -my $bridgeapi = "1.5"; +my $version = "0.6.2"; +my $bridgeapi = "1.6"; # 1.6 ist gleich 1.5 bei den hier verwendeten Funktionen @@ -344,6 +344,15 @@ sub NUKIBridge_Call($$$$$) { $uri .= "&url=" . $lockAction if( defined($lockAction) and $path eq "callback/add" ); $uri .= "&nukiId=" . $nukiId if( defined($nukiId) ); +# Hier ist die zentrale Anlaufstelle für alle Anfragen an die NUKIBridge +# Wenn die Brige noch mit einer Anfrage beschäftigt ist, wird sie mit HTTP 503 not available antworten +# Normale Statusanfragen von können dann übersprungen werden, da die Bridge ja offensichtlich beschäftigt ist +# und damit verbunden. Das sind alle Anfragen mit $path=info +# +# Anfragen mit einer Aktion dürfen nicht übersprungen werden, sondern müssen ausgeführt werden, wenn die +# Bridge wieder verfügbar ist. +# Diese Anfragen sind mit einer $lockAction belegt + HttpUtils_NonblockingGet( { @@ -373,7 +382,7 @@ sub NUKIBridge_Distribution($$$) { Log3 $name, 5, "NUKIBridge ($name) - Response JSON: $json"; Log3 $name, 5, "NUKIBridge ($name) - Response ERROR: $err"; Log3 $name, 5, "NUKIBridge ($name) - Response CODE: $param->{code}" if( defined($param->{code}) and ($param->{code}) ); - + readingsBeginUpdate($hash); if( defined( $err ) ) { @@ -391,13 +400,13 @@ sub NUKIBridge_Distribution($$$) { } } - if( $json eq "" and exists( $param->{code} ) and $param->{code} ne 200 ) { + if( $json !~ m/^[\[{].*[}\]]$/ and exists( $param->{code} ) and $param->{code} ne 200 ) { - if( $param->{code} eq 503 ) { - NUKIDevice_Parse($param->{chash},$param->{code}) if( $hash != $param->{chash} ); - Log3 $name, 4, "NUKIBridge ($name) - smartlock is offline"; + if( $param->{code} eq 503 and $json ne "" ) { + readingsBulkUpdate( $hash, "state", "unavailable"); + Log3 $name, 4, "NUKIBridge ($name) - Bridge is unavailable"; readingsEndUpdate( $hash, 1 ); - return "received http code ".$param->{code}.": smartlock is offline"; + return "received http code ".$param->{code}.": Bridge is unavailable"; } readingsBulkUpdate( $hash, "lastError", "Internal error, " .$param->{code} ); diff --git a/74_NUKIDevice.pm b/74_NUKIDevice.pm index 6f6cb3f..e7ab59d 100644 --- a/74_NUKIDevice.pm +++ b/74_NUKIDevice.pm @@ -33,7 +33,7 @@ use warnings; use JSON; -my $version = "0.6.1"; +my $version = "0.6.2"; @@ -386,10 +386,7 @@ sub NUKIDevice_Parse($$) { } elsif( $result =~ m'HTTP/1.1 200 OK' ) { Log3 $name, 4, "NUKIDevice ($name) - empty answer received"; return undef; - } elsif( $result !~ m/^[\[{].*[}\]]$/ ) { - Log3 $name, 3, "NUKIDevice ($name) - invalid json detected: $result"; - return "NUKIDevice ($name) - invalid json detected: $result"; - } + } if( $result =~ /\d{3}/ ) { if( $result eq 400 ) { @@ -405,12 +402,16 @@ sub NUKIDevice_Parse($$) { } if( $result eq 503 ) { - readingsSingleUpdate( $hash, "state", "smartlock is offline", 1 ); - Log3 $name, 3, "NUKIDevice ($name) - smartlock is offline"; + readingsSingleUpdate( $hash, "state", "smartlock is unavailable", 1 ); + Log3 $name, 3, "NUKIDevice ($name) - smartlock is unavailable"; return; } } + if( $result !~ m/^[\[{].*[}\]]$/ ) { + Log3 $name, 3, "NUKIDevice ($name) - invalid json detected by NUKIDevice_Parse: $result"; + return "NUKIDevice ($name) - invalid json detected by NUKIDevice_Parse: $result"; + } ######################################### #### verarbeiten des JSON Strings ####### @@ -523,8 +524,8 @@ sub NUKIDevice_CGI() { Log3 $name, 4, "NUKIDevice ($name) - empty answer received"; return undef; } elsif( $json !~ m/^[\[{].*[}\]]$/ ) { - Log3 $name, 3, "NUKIDevice ($name) - invalid json detected: $json"; - return "NUKIDevice ($name) - invalid json detected: $json"; + Log3 $name, 3, "NUKIDevice ($name) - invalid json detected by NUKIDevice_CGI: $json"; + return "NUKIDevice ($name) - invalid json detected by NUKIDevice_CGI: $json"; } my $decode_json = decode_json($json); From 0790966e6af7973a61aee3a012204670427753e8 Mon Sep 17 00:00:00 2001 From: Olaf Bock Date: Sun, 19 Nov 2017 09:26:37 +0100 Subject: [PATCH 2/2] Bridge request handling serialized --- 73_NUKIBridge.pm | 100 +++++++++++++++++++++++++++++++++++++++-------- 74_NUKIDevice.pm | 5 +++ 2 files changed, 89 insertions(+), 16 deletions(-) diff --git a/73_NUKIBridge.pm b/73_NUKIBridge.pm index 082462f..7d0647d 100644 --- a/73_NUKIBridge.pm +++ b/73_NUKIBridge.pm @@ -35,6 +35,10 @@ # +################################ +# Version 0.6.2, Nov 2017: Olaf Bock (github at github.com) +# - Errorhandling for HTTP 503 modified +# - Nuki Bridge Calls serialized ################################ @@ -47,7 +51,7 @@ use JSON; use HttpUtils; my $version = "0.6.2"; -my $bridgeapi = "1.6"; # 1.6 ist gleich 1.5 bei den hier verwendeten Funktionen +my $bridgeapi = "1.6"; # 1.6 is the same as 1.5 with respect to the here used funktions @@ -82,7 +86,6 @@ sub NUKIBridge_CallBlocking($$$); - sub NUKIBridge_Initialize($) { my ($hash) = @_; @@ -138,13 +141,16 @@ sub NUKIBridge_Define($$) { $hash->{VERSION} = $version; $hash->{BRIDGEAPI} = $bridgeapi; $hash->{helper}{aliveCount} = 0; + $hash->{CALLSTACK} = 0; # New added with 0.6.2 to serialize the Bridge calls. Counter of open calls Log3 $name, 3, "NUKIBridge ($name) - defined with host $host on port $port, Token $token"; $attr{$name}{room} = "NUKI" if( !defined( $attr{$name}{room} ) ); - readingsSingleUpdate($hash, 'state', 'Initialized', 1 ); +# $attr{$name}{event-on-change-reading} = "state" if( !defined( $attr{$name}{event-on-change-reading} ) ); # New added with 0.6.2 to trigger the notify +# I can´t get this running. Always Bareword event not allowed while strict subs error -> Make sure to define it in the cfg file + readingsSingleUpdate($hash, 'state', 'Initialized', 1 ); RemoveInternalTimer($hash); @@ -168,6 +174,9 @@ sub NUKIBridge_Undef($$) { RemoveInternalTimer( $hash ); + if( exists( $defs{'nCallReady'} ) ) { # New added with 0.6.2. Remove eventually defined notify + fhem ("delete nCallReady"); + } delete $modules{NUKIBridge}{defptr}{$hash->{HOST}}; return undef; @@ -228,6 +237,13 @@ sub NUKIBridge_Set($@) { return undef; + } elsif($cmd eq 'retry') { # New added with 0.6.2. Command "retry" for processing open actions + return "usage: retry" if( @args != 0 ); + + NUKIBridge_Call($hash,$hash,"retry",undef,undef) if( !IsDisabled($name) ); + + return undef; + } elsif($cmd eq 'fwUpdate') { return "usage: fwUpdate" if( @args != 0 ); @@ -265,7 +281,7 @@ sub NUKIBridge_Set($@) { } else { my $list = ""; - $list .= "info:noArg autocreate:noArg callbackRemove:0,1,2 "; + $list .= "info:noArg autocreate:noArg retry:noArg callbackRemove:0,1,2 "; # New added with 0.6.2: retry $list .= "clearLog:noArg fwUpdate:noArg reboot:noArg factoryReset:noArg" if( ReadingsVal($name,'bridgeType','Software') eq 'Hardware' ); return "Unknown argument $cmd, choose one of $list"; } @@ -335,7 +351,18 @@ sub NUKIBridge_Call($$$$$) { my $host = $hash->{HOST}; my $port = $hash->{PORT}; my $token = $hash->{TOKEN}; - + + my $retry = 0; # New added with 0.6.2: Process open requests (when called by notify) + my $lastRequest = time; + if (exists ($hash->{helper}{BridgeCallStack}) and scalar @{$hash->{helper}{BridgeCallStack}} > 0) { + my $dummy; + ($dummy,$dummy,$dummy,$dummy,$dummy, $lastRequest) = @{$hash->{helper}{BridgeCallStack}[0]}; + if ($path eq 'retry') { + ($hash,$chash,$path,$lockAction,$nukiId, $lastRequest) = @{$hash->{helper}{BridgeCallStack}[0]}; + $retry = 1; + Log3 $name, 5, "NUKIBridge ($name) - retry last operation $path"; + } + } my $uri = "http://" . $hash->{HOST} . ":" . $port; $uri .= "/" . $path if( defined $path); @@ -344,15 +371,35 @@ sub NUKIBridge_Call($$$$$) { $uri .= "&url=" . $lockAction if( defined($lockAction) and $path eq "callback/add" ); $uri .= "&nukiId=" . $nukiId if( defined($nukiId) ); -# Hier ist die zentrale Anlaufstelle für alle Anfragen an die NUKIBridge -# Wenn die Brige noch mit einer Anfrage beschäftigt ist, wird sie mit HTTP 503 not available antworten -# Normale Statusanfragen von können dann übersprungen werden, da die Bridge ja offensichtlich beschäftigt ist -# und damit verbunden. Das sind alle Anfragen mit $path=info -# -# Anfragen mit einer Aktion dürfen nicht übersprungen werden, sondern müssen ausgeführt werden, wenn die -# Bridge wieder verfügbar ist. -# Diese Anfragen sind mit einer $lockAction belegt + if (ReadingsVal($name,"state","connected") eq "processing..." ) { # New added with 0.6.2: state processing... + Log3 $name, 5, "Last request was $lastRequest". " Now is ".time; + if ($path eq "info" ) { # New added with 0.6.2. Info requests can be skipped if other is ongoing + my $timeDiff = time - $lastRequest; + if ($timeDiff < 60) { + Log3 $name, 5, "NUKIBridge ($name) - processing other request -- skip info request"; + } else { + Log3 $name, 4, "NUKIBridge ($name) - timeout last request: $timeDiff sec -- reset state"; + readingsSingleUpdate($hash, 'state', 'connected', 1 ); # New added with 0.6.2. Just assume it is connected to trigger open request on the stack + } + return; + } else { # New added with 0.6.2. Real actions commands should not be skipped. Queue them on the stack + push (@{$hash->{helper}{BridgeCallStack}}, [$hash,$chash,$path,$lockAction,$nukiId, time]); + $hash->{CALLSTACK} = scalar @{$hash->{helper}{BridgeCallStack}}; + Log3 $name, 5, "NUKIBridge ($name) - processing other request -- adding to CallStack. Stacksize ".scalar @{$hash->{helper}{BridgeCallStack}}; + fhem ("define nCallReady notify ".$hash->{NAME}.":connected set ".$hash->{NAME}." retry"); + Log3 $name, 5, "NUKIBridge ($name) - defined notify"; + } + } else { # New added with 0.6.2. Bridge is ready. Just do what is requested + readingsSingleUpdate($hash, 'state', 'processing...', 1 ); + if (!$retry) { + push (@{$hash->{helper}{BridgeCallStack}}, [$hash,$chash,$path,$lockAction,$nukiId, time]); + $hash->{CALLSTACK} = scalar @{$hash->{helper}{BridgeCallStack}}; + Log3 $name, 5, "NUKIBridge ($name) - ready and adding request to CallStack. Stacksize ".scalar @{$hash->{helper}{BridgeCallStack}}; + } else { + Log3 $name, 5, "NUKIBridge ($name) - retry w/o adding request to CallStack. Stacksize ".scalar @{$hash->{helper}{BridgeCallStack}}; + } + } HttpUtils_NonblockingGet( { @@ -396,15 +443,23 @@ sub NUKIBridge_Distribution($$$) { readingsBulkUpdate( $hash, "lastError", $err ) if( ReadingsVal($name,"state","not connected") eq "not connected" ); Log3 $name, 4, "NUKIBridge ($name) - error while requesting: $err"; readingsEndUpdate( $hash, 1 ); + if( !exists( $defs{'nCallReady'} ) ) { # New added with 0.6.2. An error occurred. Define notify to do it again + Log3 $name, 5, "NUKIBridge ($name) - notify not existing. Defined now."; + fhem ("define nCallReady notify ".$hash->{NAME}.":connected set ".$hash->{NAME}." retry"); + } return $err; } } if( $json !~ m/^[\[{].*[}\]]$/ and exists( $param->{code} ) and $param->{code} ne 200 ) { - if( $param->{code} eq 503 and $json ne "" ) { + if( $param->{code} eq 503 and $json ne "" ) { # New added with 0.6.2. Bridge unavailable. Define notify to do it again readingsBulkUpdate( $hash, "state", "unavailable"); - Log3 $name, 4, "NUKIBridge ($name) - Bridge is unavailable"; + Log3 $name, 4, "NUKIBridge ($name) - Bridge is unavailable. Try again"; + if( !exists( $defs{'nCallReady'} ) ) { + Log3 $name, 5, "NUKIBridge ($name) - notify not existing. Defined now."; + fhem ("define nCallReady notify ".$hash->{NAME}.":connected set ".$hash->{NAME}." retry"); + } readingsEndUpdate( $hash, 1 ); return "received http code ".$param->{code}.": Bridge is unavailable"; } @@ -449,7 +504,20 @@ sub NUKIBridge_Distribution($$$) { readingsEndUpdate( $hash, 1 ); return $param->{code}; } - + + # New added with 0.6.2. Callback arrived o.k. Above code didn´t delect an error. Delete request from call stack + shift @{$hash->{helper}{BridgeCallStack}}; + $hash->{CALLSTACK} = scalar @{$hash->{helper}{BridgeCallStack}}; + Log3 $name, 5, "NUKIBridge ($name) - Request is processed ok. Deleting from CallStack. Stacksize ".scalar @{$hash->{helper}{BridgeCallStack}}; + if (scalar @{$hash->{helper}{BridgeCallStack}} == 0) { + if( exists( $defs{'nCallReady'} ) ) { + fhem ("delete nCallReady"); + Log3 $name, 5, "NUKIBridge ($name) - Last pending request is processed ok. Deleting notify"; + } + } + readingsSingleUpdate( $hash, "state", "connected", 1 ); # New added with 0.6.2. Whatever arrives here is a valid response from the bridge + + if( $hash == $param->{chash} ) { # zum testen da ich kein Nuki Smartlock habe diff --git a/74_NUKIDevice.pm b/74_NUKIDevice.pm index e7ab59d..8f922cd 100644 --- a/74_NUKIDevice.pm +++ b/74_NUKIDevice.pm @@ -25,6 +25,11 @@ # ############################################################################### +################################ +# Version 0.6.2, Nov 2017: Olaf Bock (github at github.com) +# - Errorhandling for HTTP 503 modified +################################ + package main;