From e634cf44378af9f0b79b9de53d8243a7417467df Mon Sep 17 00:00:00 2001 From: Marko Oldenburg Date: Sat, 27 Nov 2021 18:45:00 +0100 Subject: [PATCH 1/2] code clean and new battery readings --- FHEM/73_NUKIBridge.pm | 3 +- controls_NukiSmart.txt | 8 +- lib/FHEM/Devices/Nuki/Bridge.pm | 203 ++++++++++++++++---------------- lib/FHEM/Devices/Nuki/Device.pm | 58 +++++---- 4 files changed, 143 insertions(+), 129 deletions(-) diff --git a/FHEM/73_NUKIBridge.pm b/FHEM/73_NUKIBridge.pm index 7869c1b..2434332 100644 --- a/FHEM/73_NUKIBridge.pm +++ b/FHEM/73_NUKIBridge.pm @@ -64,8 +64,7 @@ sub Initialize { $hash->{NotifyFn} = \&FHEM::Devices::Nuki::Bridge::Notify; $hash->{AttrFn} = \&FHEM::Devices::Nuki::Bridge::Attr; $hash->{AttrList} = - 'disable:1 ' - . 'port ' + 'disable:1 ' . 'port ' . 'webhookFWinstance:' . $webhookFWinstance . ' ' . 'webhookHttpHostname ' diff --git a/controls_NukiSmart.txt b/controls_NukiSmart.txt index 1bcbe69..e1218f8 100644 --- a/controls_NukiSmart.txt +++ b/controls_NukiSmart.txt @@ -1,4 +1,4 @@ -UPD 2021-11-27_18:02:39 9224 FHEM/73_NUKIBridge.pm -UPD 2021-11-27_18:01:59 7548 FHEM/74_NUKIDevice.pm -UPD 2021-11-27_18:08:11 40444 lib/FHEM/Devices/Nuki/Bridge.pm -UPD 2021-11-27_15:28:59 15728 lib/FHEM/Devices/Nuki/Device.pm +UPD 2021-11-27_18:39:55 9217 FHEM/73_NUKIBridge.pm +UPD 2021-11-27_18:40:10 7548 FHEM/74_NUKIDevice.pm +UPD 2021-11-27_18:39:14 40112 lib/FHEM/Devices/Nuki/Bridge.pm +UPD 2021-11-27_18:44:10 16135 lib/FHEM/Devices/Nuki/Device.pm diff --git a/lib/FHEM/Devices/Nuki/Bridge.pm b/lib/FHEM/Devices/Nuki/Bridge.pm index a5e042c..fdf19e2 100644 --- a/lib/FHEM/Devices/Nuki/Bridge.pm +++ b/lib/FHEM/Devices/Nuki/Bridge.pm @@ -145,11 +145,12 @@ sub Define { use version 0.60; our $VERSION = FHEM::Meta::Get( $hash, 'version' ); my ( $name, undef, $host, $token ) = split( m{\s+}xms, $def ); -# return ('too few parameters: define NUKIBridge ') -# if ( !defined($host) -# || !defined($token) ); - my $port = ::AttrVal($name, 'port', 8080); + # return ('too few parameters: define NUKIBridge ') + # if ( !defined($host) + # || !defined($token) ); + + my $port = ::AttrVal( $name, 'port', 8080 ); my $infix = 'NUKIBridge'; $hash->{HOST} = $host // 'discover'; $hash->{PORT} = $port; @@ -165,18 +166,18 @@ sub Define { $hash->{WEBHOOK_REGISTER} = "unregistered"; - ::readingsSingleUpdate($hash, 'state', 'Initialized', 1); + ::readingsSingleUpdate( $hash, 'state', 'Initialized', 1 ); ::RemoveInternalTimer($hash); - return BridgeDiscover($hash,'discover') + return BridgeDiscover( $hash, 'discover' ) if ( $hash->{HOST} eq 'discover' && $hash->{TOKEN} eq 'discover' ); - + ::Log3( $name, 3, "NUKIBridge ($name) - defined with host $host on port $port, Token $token" ); - + if ( addExtension( $name, @@ -187,7 +188,7 @@ sub Define { { $hash->{fhem}{infix} = $infix; } - + $::modules{NUKIBridge}{defptr}{ $hash->{HOST} } = $hash; return; @@ -220,15 +221,15 @@ sub Attr { if ( $attrName eq 'disable' ) { if ( $cmd eq 'set' && $attrVal == 1 ) { - ::readingsSingleUpdate($hash, 'state', 'disabled', 1); + ::readingsSingleUpdate( $hash, 'state', 'disabled', 1 ); ::Log3( $name, 3, "NUKIBridge ($name) - disabled" ); } elsif ( $cmd eq 'del' ) { - ::readingsSingleUpdate($hash, 'state', 'active', 1); + ::readingsSingleUpdate( $hash, 'state', 'active', 1 ); ::Log3( $name, 3, "NUKIBridge ($name) - enabled" ); } } - + if ( $attrName eq 'port' ) { if ( $cmd eq 'set' ) { $hash->{PORT} = $attrVal; @@ -236,7 +237,8 @@ sub Attr { } elsif ( $cmd eq 'del' ) { $hash->{PORT} = 8080; - ::Log3( $name, 3, "NUKIBridge ($name) - set bridge port to default" ); + ::Log3( $name, 3, + "NUKIBridge ($name) - set bridge port to default" ); } } @@ -244,10 +246,10 @@ sub Attr { if ( $cmd eq 'set' ) { ::Log3( $name, 3, "NUKIBridge ($name) - enable disabledForIntervals" ); - ::readingsSingleUpdate($hash, 'state', 'Unknown', 1); + ::readingsSingleUpdate( $hash, 'state', 'Unknown', 1 ); } elsif ( $cmd eq 'del' ) { - ::readingsSingleUpdate($hash, 'state', 'active', 1); + ::readingsSingleUpdate( $hash, 'state', 'active', 1 ); ::Log3( $name, 3, "NUKIBridge ($name) - delete disabledForIntervals" ); } @@ -395,10 +397,8 @@ sub removeExtension { ::Log3( $name, 2, "NUKIBridge ($name) - Unregistering NUKIBridge for webhook URL $url..." - ) - if ( defined($name) ); - - + ) if ( defined($name) ); + delete $::data{FWEXT}{$url}; return; @@ -655,10 +655,11 @@ sub Distribution { my $err = shift; my $json = shift; - my $hash = $param->{hash}; -# my $doTrigger = $param->{doTrigger}; - my $name = $hash->{NAME}; - my $host = $hash->{HOST}; + my $hash = $param->{hash}; + + # my $doTrigger = $param->{doTrigger}; + my $name = $hash->{NAME}; + my $host = $hash->{HOST}; my $dhash = $hash; @@ -779,7 +780,7 @@ sub Distribution { ::readingsEndUpdate( $hash, 1 ); - ::readingsSingleUpdate($hash, 'state', 'connected', 1); + ::readingsSingleUpdate( $hash, 'state', 'connected', 1 ); ::Log3( $name, 5, "NUKIBridge ($name) - Bridge ist online" ); if ( $param->{endpoint} eq 'callback/list' ) { @@ -1110,9 +1111,9 @@ sub getCallbackList { if ( scalar( @{ $decode_json->{callbacks} } ) > 0 ) { for my $cb ( @{ $decode_json->{callbacks} } ) { - $aHref = - "{host} + $aHref = "{host} . "/fhem?cmd=set+" . $name . "+callbackRemove+" @@ -1272,77 +1273,77 @@ sub ParseJSON { } sub BridgeDiscover { - my $hash = shift; - my $endpoint = shift; - my $bridge = shift; - my $name = $hash->{NAME}; - my $url = ( $endpoint eq 'discover' && !defined($bridge) - ? 'https://api.nuki.io/discover/bridges' - : 'http://' . $bridge->{'ip'} . ':' . $bridge->{'port'} . '/auth' ); - my $timeout = ( $endpoint eq 'discover' && !defined($bridge) - ? 5 - : 35 ); - + my $hash = shift; + my $endpoint = shift; + my $bridge = shift; + my $name = $hash->{NAME}; + my $url = ( + $endpoint eq 'discover' && !defined($bridge) + ? 'https://api.nuki.io/discover/bridges' + : 'http://' . $bridge->{'ip'} . ':' . $bridge->{'port'} . '/auth' + ); + my $timeout = ( + $endpoint eq 'discover' && !defined($bridge) + ? 5 + : 35 + ); + if ( $endpoint eq 'discover' ) { ::Log3( $name, 3, - "NUKIBridge ($name) - Bridge device defined. run discover mode" - ); + "NUKIBridge ($name) - Bridge device defined. run discover mode" ); - ::readingsSingleUpdate($hash, 'state', 'run discovery', 1); + ::readingsSingleUpdate( $hash, 'state', 'run discovery', 1 ); } elsif ( $endpoint eq 'getApiToken' ) { ::Log3( $name, 3, - "NUKIBridge ($name) - Enables the api (if not yet enabled) and get the api token." +"NUKIBridge ($name) - Enables the api (if not yet enabled) and get the api token." ); } - ::HttpUtils_NonblockingGet( - { - url => $url, - timeout => $timeout, - hash => $hash, - header => 'Accept: application/json', - endpoint => $endpoint, - host => $bridge->{'ip'}, - port => $bridge->{'port'}, - method => 'GET', - callback => \&BridgeDiscoverRequest, - } + { + url => $url, + timeout => $timeout, + hash => $hash, + header => 'Accept: application/json', + endpoint => $endpoint, + host => $bridge->{'ip'}, + port => $bridge->{'port'}, + method => 'GET', + callback => \&BridgeDiscoverRequest, + } ); - + ::Log3( $name, 3, "NUKIBridge ($name) - Send Discover request to Nuki Cloud" ) - if ( $endpoint eq 'discover' ); - - ::Log3( $name, 3, - "NUKIBridge ($name) - get API Token from the Bridge" ) - if ( $endpoint eq 'getApiToken' ); + if ( $endpoint eq 'discover' ); + + ::Log3( $name, 3, "NUKIBridge ($name) - get API Token from the Bridge" ) + if ( $endpoint eq 'getApiToken' ); return; } sub BridgeDiscoverRequest { - my $param = shift; - my $err = shift; - my $json = shift; - - my $hash = $param->{hash}; - my $name = $hash->{NAME}; - - + my $param = shift; + my $err = shift; + my $json = shift; + + my $hash = $param->{hash}; + my $name = $hash->{NAME}; + if ( defined($err) - && $err ne '' ) + && $err ne '' ) { - return ::Log3( $name, 3, - "NUKIBridge ($name) - Error: $err"); + return ::Log3( $name, 3, "NUKIBridge ($name) - Error: $err" ); } - elsif ( exists( $param->{code}) - && $param->{code} != 200 ) + elsif ( exists( $param->{code} ) + && $param->{code} != 200 ) { return ::Log3( $name, 3, - "NUKIBridge ($name) - HTTP error Code present. Code: $param->{code}"); + "NUKIBridge ($name) - HTTP error Code present. Code: $param->{code}" + ); } my $decode_json; @@ -1351,27 +1352,30 @@ sub BridgeDiscoverRequest { ::Log3( $name, 3, "NUKIBridge ($name) - JSON error while request: $@" ); return; } - + if ( $param->{endpoint} eq 'discover' ) { - return ::readingsSingleUpdate($hash, 'state', 'no bridges discovered', 1) - if ( scalar(@{$decode_json->{bridges}}) == 0 + return ::readingsSingleUpdate( $hash, 'state', 'no bridges discovered', + 1 ) + if ( scalar( @{ $decode_json->{bridges} } ) == 0 && $decode_json->{errorCode} == 0 ); - return BridgeDiscover_getAPIToken($hash,$decode_json); + return BridgeDiscover_getAPIToken( $hash, $decode_json ); } elsif ( $param->{endpoint} eq 'getApiToken' ) { - ::readingsSingleUpdate($hash, 'state', 'modefined bridge device in progress', 1); - - $decode_json->{host} = $param->{host}; - $decode_json->{port} = $param->{port}; - - return ModefinedBridgeDevices($hash,$decode_json) + ::readingsSingleUpdate( $hash, 'state', + 'modefined bridge device in progress', 1 ); + + $decode_json->{host} = $param->{host}; + $decode_json->{port} = $param->{port}; + + return ModefinedBridgeDevices( $hash, $decode_json ) if ( $decode_json->{success} == 1 ); - return ::readingsSingleUpdate($hash, 'state', 'get api token failed', 1); + return ::readingsSingleUpdate( $hash, 'state', 'get api token failed', + 1 ); } - + return; } @@ -1380,16 +1384,17 @@ sub BridgeDiscover_getAPIToken { my $decode_json = shift; my $name = $hash->{NAME}; - my $pullApiKeyMessage = 'When issuing this API-call the bridge turns on its LED for 30 seconds. + my $pullApiKeyMessage = + 'When issuing this API-call the bridge turns on its LED for 30 seconds. The button of the bridge has to be pressed within this timeframe. Otherwise the bridge returns a negative success and no token.'; - ::readingsSingleUpdate($hash, 'state', $pullApiKeyMessage, 1); - - for ( @{$decode_json->{bridges}} ) { - - BridgeDiscover($hash,'getApiToken',$_); + ::readingsSingleUpdate( $hash, 'state', $pullApiKeyMessage, 1 ); + + for ( @{ $decode_json->{bridges} } ) { + + BridgeDiscover( $hash, 'getApiToken', $_ ); } - + return; } @@ -1398,17 +1403,15 @@ sub ModefinedBridgeDevices { my $decode_json = shift; my $name = $hash->{NAME}; - ::CommandAttr( undef, $name . ' port ' . $decode_json->{port} ) if ( $decode_json->{port} != 8080 ); ::CommandDefMod( undef, - $name - . ' NUKIBridge ' - . $decode_json->{host} . ' ' - . $decode_json->{token} ); - + $name + . ' NUKIBridge ' + . $decode_json->{host} . ' ' + . $decode_json->{token} ); + return; } - 1; diff --git a/lib/FHEM/Devices/Nuki/Device.pm b/lib/FHEM/Devices/Nuki/Device.pm index 065dbb5..fa8e410 100644 --- a/lib/FHEM/Devices/Nuki/Device.pm +++ b/lib/FHEM/Devices/Nuki/Device.pm @@ -198,11 +198,11 @@ sub Define { ? $deviceType : 0; - $hash->{NUKIID} = $nukiId; - $hash->{DEVICETYPEID} = $deviceType; - $hash->{VERSION} = version->parse($VERSION)->normal; - $hash->{STATE} = 'Initialized'; - $hash->{NOTIFYDEV} = 'global,autocreate,' . $name; + $hash->{NUKIID} = $nukiId; + $hash->{DEVICETYPEID} = $deviceType; + $hash->{VERSION} = version->parse($VERSION)->normal; + $hash->{STATE} = 'Initialized'; + $hash->{NOTIFYDEV} = 'global,autocreate,' . $name; my $iodev = ::AttrVal( $name, 'IODev', 'none' ); @@ -446,25 +446,24 @@ sub Parse { WriteReadings( $hash, $decode_json ); ::Log3( $name, 4, "NUKIDevice ($name) - find logical device: $hash->{NAME}" ); - + return $hash->{NAME}; ################## ## Zwischenlösung so für die Umstellung, kann später gelöscht werden -# if ( ::AttrVal( $name, 'model', '' ) eq '' ) { -# ::CommandDefMod( undef, -# $name -# . ' NUKIDevice ' -# . $hash->{NUKIID} . ' ' -# . $decode_json->{deviceType} ); -# ::CommandAttr( undef, -# $name -# . ' model ' -# . $deviceTypes{ $decode_json->{deviceType} } ); -# ::Log3( $name, 2, "NUKIDevice ($name) - redefined Defmod" ); -# } + # if ( ::AttrVal( $name, 'model', '' ) eq '' ) { + # ::CommandDefMod( undef, + # $name + # . ' NUKIDevice ' + # . $hash->{NUKIID} . ' ' + # . $decode_json->{deviceType} ); + # ::CommandAttr( undef, + # $name + # . ' model ' + # . $deviceTypes{ $decode_json->{deviceType} } ); + # ::Log3( $name, 2, "NUKIDevice ($name) - redefined Defmod" ); + # } - } else { ::Log3( $name, 4, @@ -557,11 +556,18 @@ sub WriteReadings { && $t ne 'deviceType' && $t ne 'paired' && $t ne 'batteryCritical' + && $t ne 'batteryChargeState' + && $t ne 'batteryCharging' && $t ne 'timestamp' ); - ::readingsBulkUpdate( $hash, $t, - ( $v =~ m/^[0-9]$/ ? $lockStates{$v}{ $hash->{DEVICETYPEID} } : $v ) ) - if ( $t eq 'state' ); + ::readingsBulkUpdate( + $hash, $t, + ( + $v =~ m/^[0-9]$/ + ? $lockStates{$v}{ $hash->{DEVICETYPEID} } + : $v + ) + ) if ( $t eq 'state' ); ::readingsBulkUpdate( $hash, $t, $modes{$v}{ $hash->{DEVICETYPEID} } ) if ( $t eq 'mode' ); @@ -571,10 +577,16 @@ sub WriteReadings { ::readingsBulkUpdate( $hash, $t, ( $v == 1 ? 'true' : 'false' ) ) if ( $t eq 'paired' ); + + ::readingsBulkUpdate( $hash, $t, ( $v == 1 ? 'true' : 'false' ) ) + if ( $t eq 'batteryCharging' ); ::readingsBulkUpdate( $hash, 'batteryState', - ( ( $v eq 'true' or $v == 1 ) ? 'low' : 'ok' ) ) + ( $v == 1 ? 'low' : 'ok' ) ) if ( $t eq 'batteryCritical' ); + + ::readingsBulkUpdate( $hash, 'batteryPercent', $v ) + if ( $t eq 'batteryChargeState' ); } ::readingsEndUpdate( $hash, 1 ); -- 2.45.2 From 25a682224b479226ffd698b16910a3ee82217465 Mon Sep 17 00:00:00 2001 From: Marko Oldenburg Date: Sat, 27 Nov 2021 22:51:25 +0100 Subject: [PATCH 2/2] change model and fix problems --- controls_NukiSmart.txt | 8 ++++---- lib/FHEM/Devices/Nuki/Bridge.pm | 10 +++++++++- lib/FHEM/Devices/Nuki/Device.pm | 20 ++++++++++++++++++-- 3 files changed, 31 insertions(+), 7 deletions(-) diff --git a/controls_NukiSmart.txt b/controls_NukiSmart.txt index 23c5644..ffcdf25 100644 --- a/controls_NukiSmart.txt +++ b/controls_NukiSmart.txt @@ -1,4 +1,4 @@ -UPD 2021-11-27_21:26:06 9224 FHEM/73_NUKIBridge.pm -UPD 2021-11-27_21:30:28 7569 FHEM/74_NUKIDevice.pm -UPD 2021-11-27_21:26:06 40444 lib/FHEM/Devices/Nuki/Bridge.pm -UPD 2021-11-27_21:32:02 15123 lib/FHEM/Devices/Nuki/Device.pm +UPD 2021-11-27_21:42:00 9224 FHEM/73_NUKIBridge.pm +UPD 2021-11-27_21:36:27 7569 FHEM/74_NUKIDevice.pm +UPD 2021-11-27_22:32:28 40699 lib/FHEM/Devices/Nuki/Bridge.pm +UPD 2021-11-27_22:49:00 15481 lib/FHEM/Devices/Nuki/Device.pm diff --git a/lib/FHEM/Devices/Nuki/Bridge.pm b/lib/FHEM/Devices/Nuki/Bridge.pm index a5e042c..aa4c8dc 100644 --- a/lib/FHEM/Devices/Nuki/Bridge.pm +++ b/lib/FHEM/Devices/Nuki/Bridge.pm @@ -501,7 +501,14 @@ sub GetCheckBridgeAlive { if ( !::IsDisabled($name) && $hash->{helper}->{iowrite} == 0 ) { - Write( $hash, 'info', undef ); + if ( $hash->{helper}->{runInfo} == 0 ) { + Write( $hash, 'info', undef ); + $hash->{helper}->{runInfo} = 1; + } + else { + Write( $hash, 'list', undef ); + $hash->{helper}->{runInfo} = 0; + } ::Log3( $name, 4, "NUKIBridge ($name) - run Write" ); } @@ -521,6 +528,7 @@ sub FirstRun { Write( $hash, 'list', undef ) if ( !::IsDisabled($name) ); + $hash->{helper}->{runInfo} = 0; return ::InternalTimer( ::gettimeofday() + 5, \&FHEM::Devices::Nuki::Bridge::GetCheckBridgeAlive, $hash ); } diff --git a/lib/FHEM/Devices/Nuki/Device.pm b/lib/FHEM/Devices/Nuki/Device.pm index f07c6cd..660701d 100644 --- a/lib/FHEM/Devices/Nuki/Device.pm +++ b/lib/FHEM/Devices/Nuki/Device.pm @@ -111,6 +111,9 @@ my %deviceTypes = ( 4 => 'smartlock3' ); +my %deviceTypeIds = reverse(%deviceTypes); + + my %modes = ( 2 => { 0 => 'door mode', @@ -180,7 +183,14 @@ my %lockStates = ( } ); -my %deviceTypeIds = reverse(%deviceTypes); +my %doorsensorStates = ( + 1 => 'deactivated', + 2 => 'door closed', + 3 => 'door opened', + 4 => 'door state unknown', + 5 => 'calibrating' +); + sub Define { my $hash = shift; @@ -205,6 +215,7 @@ sub Define { $hash->{STATE} = 'Initialized'; $hash->{NOTIFYDEV} = 'global,autocreate,' . $name; + my $iodev = ::AttrVal( $name, 'IODev', 'none' ); ::AssignIoPort( $hash, $iodev ) if ( !$hash->{IODev} ); @@ -541,7 +552,9 @@ sub WriteReadings { && $t ne 'deviceType' && $t ne 'paired' && $t ne 'batteryCritical' - && $t ne 'timestamp' ); + && $t ne 'timestamp' + && $t ne 'doorsensorState' + && $t ne 'doorsensorStateName' ); ::readingsBulkUpdate( $hash, $t, ( $v =~ m/^[0-9]$/ ? $lockStates{$v}{ $hash->{DEVICETYPEID} } : $v ) ) @@ -552,6 +565,9 @@ sub WriteReadings { ::readingsBulkUpdate( $hash, $t, $deviceTypes{$v} ) if ( $t eq 'deviceType' ); + + ::readingsBulkUpdate( $hash, $t, $doorsensorStates{$v} ) + if ( $t eq 'doorsensorState' ); ::readingsBulkUpdate( $hash, $t, ( $v == 1 ? 'true' : 'false' ) ) if ( $t eq 'paired' ); -- 2.45.2