diff --git a/fhem/CHANGED b/fhem/CHANGED index 62d18dfee..448c2b8dd 100644 --- a/fhem/CHANGED +++ b/fhem/CHANGED @@ -1,5 +1,6 @@ # 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. + - change: 73_NUKIBridge: 74_NUKIDevice use packages - change: 98_Heating_Control: move to contrib - bugfix: 98_livetracking: DoTrigger after readingsEndUpdate, decimals - bugfix: 73_NUKIBridge: fix bridge endpoint setter diff --git a/fhem/FHEM/73_NUKIBridge.pm b/fhem/FHEM/73_NUKIBridge.pm index 8a0cfa36b..21bd09d63 100644 --- a/fhem/FHEM/73_NUKIBridge.pm +++ b/fhem/FHEM/73_NUKIBridge.pm @@ -38,10 +38,16 @@ package main; +use strict; +use warnings; + +package FHEM::NUKIBridge; + use strict; use warnings; use HttpUtils; use FHEM::Meta; +use GPUtils qw(GP_Import GP_Export); # try to use JSON::MaybeXS wrapper # for chance of better performance + open code @@ -114,6 +120,49 @@ if ($@) { } } +## Import der FHEM Funktionen +#-- Run before package compilation +BEGIN { + + # Import from main context + GP_Import( + qw( + readingsSingleUpdate + readingsBulkUpdate + readingsBeginUpdate + readingsEndUpdate + readingFnAttributes + defs + modules + Log3 + CommandAttr + AttrVal + IsDisabled + deviceEvents + init_done + gettimeofday + InternalTimer + InternalVal + ReadingsVal + RemoveInternalTimer + HttpUtils_NonblockingGet + asyncOutput + data + TimeNow + devspec2array + Dispatch) + ); +} + +#-- Export to main context with different name +GP_Export( + qw( + GetCheckBridgeAlive + Initialize + CGI + BridgeCall + ) +); my %bridgeType = ( '1' => 'Hardware', @@ -136,31 +185,11 @@ my %lockActionsOpener = ( 'deactivateContinuousMode' => 5 ); -# Declare functions -sub NUKIBridge_Initialize ($); -sub NUKIBridge_Define ($$); -sub NUKIBridge_Undef ($$); -sub NUKIBridge_Attr(@); -sub NUKIBridge_addExtension($$$); -sub NUKIBridge_removeExtension($); -sub NUKIBridge_Set($@); -sub NUKIBridge_Get($@); -sub NUKIBridge_GetCheckBridgeAlive($); -sub NUKIBridge_firstRun($); -sub NUKIBridge_Write($@); -sub NUKIBridge_Call($); -sub NUKIBridge_Distribution($$$); -sub NUKIBridge_ResponseProcessing($$$); -sub NUKIBridge_CGI(); -sub NUKIBridge_InfoProcessing($$); -sub NUKIBridge_getLogfile($$); -sub NUKIBridge_getCallbackList($$); - -sub NUKIBridge_Initialize($) { +sub Initialize($) { my ($hash) = @_; # Provider - $hash->{WriteFn} = 'NUKIBridge_Write'; + $hash->{WriteFn} = 'FHEM::NUKIBridge::Write'; $hash->{Clients} = ':NUKIDevice:'; $hash->{MatchList} = { '1:NUKIDevice' => '^{.*}$' }; @@ -168,11 +197,12 @@ sub NUKIBridge_Initialize($) { join( ",", devspec2array('TYPE=FHEMWEB:FILTER=TEMPORARY!=1') ); # Consumer - $hash->{SetFn} = 'NUKIBridge_Set'; - $hash->{GetFn} = 'NUKIBridge_Get'; - $hash->{DefFn} = 'NUKIBridge_Define'; - $hash->{UndefFn} = 'NUKIBridge_Undef'; - $hash->{AttrFn} = 'NUKIBridge_Attr'; + $hash->{SetFn} = 'FHEM::NUKIBridge::Set'; + $hash->{GetFn} = 'FHEM::NUKIBridge::Get'; + $hash->{DefFn} = 'FHEM::NUKIBridge::Define'; + $hash->{UndefFn} = 'FHEM::NUKIBridge::Undef'; + $hash->{NotifyFn} = 'FHEM::NUKIBridge::Notify'; + $hash->{AttrFn} = 'FHEM::NUKIBridge::Attr'; $hash->{AttrList} = 'disable:1 ' . 'webhookFWinstance:' @@ -183,11 +213,11 @@ sub NUKIBridge_Initialize($) { return FHEM::Meta::InitMod( __FILE__, $hash ); } -sub NUKIBridge_Define($$) { +sub Define($$) { my ( $hash, $def ) = @_; my @a = split( "[ \t][ \t]*", $def ); - + return $@ unless ( FHEM::Meta::SetInternals($hash) ); use version 0.60; our $VERSION = FHEM::Meta::Get( $hash, 'version' ); @@ -202,8 +232,9 @@ sub NUKIBridge_Define($$) { $hash->{HOST} = $host; $hash->{PORT} = $port; $hash->{TOKEN} = $token; + $hash->{NOTIFYDEV} = 'global,' . $name; $hash->{VERSION} = version->parse($VERSION)->normal; - $hash->{BRIDGEAPI} = FHEM::Meta::Get( $hash, 'x_apiversion' );; + $hash->{BRIDGEAPI} = FHEM::Meta::Get( $hash, 'x_apiversion' ); $hash->{helper}->{aliveCount} = 0; $hash->{helper}->{actionQueue} = []; $hash->{helper}->{iowrite} = 0; @@ -216,12 +247,7 @@ sub NUKIBridge_Define($$) { CommandAttr( undef, $name . ' room NUKI' ) if ( AttrVal( $name, 'room', 'none' ) eq 'none' ); - if ( - NUKIBridge_addExtension( - $name, 'NUKIBridge_CGI', $infix . "-" . $host - ) - ) - { + if ( addExtension( $name, 'NUKIBridge_CGI', $infix . "-" . $host ) ) { $hash->{fhem}{infix} = $infix; } @@ -231,28 +257,19 @@ sub NUKIBridge_Define($$) { RemoveInternalTimer($hash); - if ($init_done) { - NUKIBridge_firstRun($hash) - if ( ( $hash->{HOST} ) and ( $hash->{TOKEN} ) ); - } - else { - InternalTimer( gettimeofday() + 15, 'NUKIBridge_firstRun', $hash ) - if ( ( $hash->{HOST} ) and ( $hash->{TOKEN} ) ); - } - $modules{NUKIBridge}{defptr}{ $hash->{HOST} } = $hash; return undef; } -sub NUKIBridge_Undef($$) { +sub Undef($$) { my ( $hash, $arg ) = @_; my $host = $hash->{HOST}; my $name = $hash->{NAME}; if ( defined( $hash->{fhem}{infix} ) ) { - NUKIBridge_removeExtension( $hash->{fhem}{infix} ); + removeExtension( $hash->{fhem}{infix} ); } RemoveInternalTimer($hash); @@ -261,7 +278,7 @@ sub NUKIBridge_Undef($$) { return undef; } -sub NUKIBridge_Attr(@) { +sub Attr(@) { my ( $cmd, $name, $attrName, $attrVal ) = @_; my $hash = $defs{$name}; @@ -354,7 +371,7 @@ sub NUKIBridge_Attr(@) { . $hash->{WEBHOOK_URI}; Log3( $name, 3, "NUKIBridge ($name) - URL ist: $url" ); - NUKIBridge_Write( $hash, 'callback/add', $url, undef, undef ) + Write( $hash, 'callback/add', $url, undef, undef ) if ($init_done); $hash->{WEBHOOK_REGISTER} = 'sent'; } @@ -366,7 +383,33 @@ sub NUKIBridge_Attr(@) { return undef; } -sub NUKIBridge_addExtension($$$) { +sub Notify($$) { + + my ( $hash, $dev ) = @_; + my $name = $hash->{NAME}; + return if ( IsDisabled($name) ); + + my $devname = $dev->{NAME}; + my $devtype = $dev->{TYPE}; + my $events = deviceEvents( $dev, 1 ); + return if ( !$events ); + + FirstRun($hash) + if ( + ( + grep /^INITIALIZED$/, @{$events} + or grep /^REREADCFG$/, @{$events} + or grep /^MODIFIED.$name$/, @{$events} + or grep /^DEFINED.$name$/, @{$events} + ) + and $devname eq 'global' + and $init_done + ); + + return; +} + +sub addExtension($$$) { my ( $name, $func, $link ) = @_; my $url = '/' . $link; @@ -382,7 +425,7 @@ sub NUKIBridge_addExtension($$$) { return 1; } -sub NUKIBridge_removeExtension($) { +sub removeExtension($) { my ($link) = @_; my $url = '/' . $link; @@ -394,7 +437,7 @@ sub NUKIBridge_removeExtension($) { delete $data{FWEXT}{$url}; } -sub NUKIBridge_Set($@) { +sub Set($@) { my ( $hash, $name, $cmd, @args ) = @_; my ( $arg, @params ) = @args; @@ -402,68 +445,50 @@ sub NUKIBridge_Set($@) { if ( lc($cmd) eq 'getdevicelist' ) { return 'usage: getDeviceList' if ( @args != 0 ); - NUKIBridge_Write( $hash, 'list', undef, undef, undef ) + Write( $hash, 'list', undef, undef, undef ) if ( !IsDisabled($name) ); return undef; } elsif ( $cmd eq 'info' ) { return 'usage: statusRequest' if ( @args != 0 ); - NUKIBridge_Write( $hash, 'info', undef, undef, undef ) + Write( $hash, 'info', undef, undef, undef ) if ( !IsDisabled($name) ); return undef; } elsif ( lc($cmd) eq 'fwupdate' ) { return 'usage: fwUpdate' if ( @args != 0 ); - NUKIBridge_Write( $hash, 'fwupdate', undef, undef, undef ) + Write( $hash, 'fwupdate', undef, undef, undef ) if ( !IsDisabled($name) ); return undef; } elsif ( $cmd eq 'reboot' ) { return 'usage: reboot' if ( @args != 0 ); - NUKIBridge_Write( $hash, 'reboot', undef, undef, undef ) + Write( $hash, 'reboot', undef, undef, undef ) if ( !IsDisabled($name) ); return undef; } elsif ( lc($cmd) eq 'clearlog' ) { return 'usage: clearLog' if ( @args != 0 ); - NUKIBridge_Write( $hash, 'clearlog', undef, undef, undef ) + Write( $hash, 'clearlog', undef, undef, undef ) if ( !IsDisabled($name) ); } elsif ( lc($cmd) eq 'factoryreset' ) { return 'usage: clearLog' if ( @args != 0 ); - NUKIBridge_Write( $hash, 'factoryReset', undef, undef, undef ) + Write( $hash, 'factoryReset', undef, undef, undef ) if ( !IsDisabled($name) ); } elsif ( lc($cmd) eq 'callbackremove' ) { return 'usage: callbackRemove' if ( @args > 1 ); my $id = ( @args > 0 ? join( ' ', @args ) : 0 ); - - NUKIBridge_Write( $hash, 'callback/remove', $id, undef, undef ) - if ( !IsDisabled($name) ); - -# my $resp = NUKIBridge_CallBlocking( $hash, 'callback/remove', $id ) -# if ( !IsDisabled($name) ); -# -# if ( -# ( -# $resp->{success} eq 'true' -# or $resp->{success} == 1 -# ) -# and !IsDisabled($name) -# ) -# { -# return ( 'Success Callback ' . $id . ' removed' ); -# } -# else { -# return ('remove Callback failed'); -# } + Write( $hash, 'callback/remove', $id, undef, undef ) + if ( !IsDisabled($name) ); } else { my $list = ''; @@ -474,7 +499,7 @@ sub NUKIBridge_Set($@) { } } -sub NUKIBridge_Get($@) { +sub Get($@) { my ( $hash, $name, $cmd, @args ) = @_; my ( $arg, @params ) = @args; @@ -482,12 +507,12 @@ sub NUKIBridge_Get($@) { if ( lc($cmd) eq 'logfile' ) { return 'usage: logFile' if ( @args != 0 ); - NUKIBridge_Write($hash,'log',undef,undef,undef); + Write( $hash, 'log', undef, undef, undef ); } elsif ( lc($cmd) eq 'callbacklist' ) { return 'usage: callbackList' if ( @args != 0 ); - NUKIBridge_Write($hash,'callback/list',undef,undef,undef); + Write( $hash, 'callback/list', undef, undef, undef ); } else { my $list = ''; @@ -499,47 +524,45 @@ sub NUKIBridge_Get($@) { } } -sub NUKIBridge_GetCheckBridgeAlive($) { +sub GetCheckBridgeAlive($) { my ($hash) = @_; my $name = $hash->{NAME}; RemoveInternalTimer($hash); - Log3( $name, 4, "NUKIBridge ($name) - NUKIBridge_GetCheckBridgeAlive" ); + Log3( $name, 4, "NUKIBridge ($name) - GetCheckBridgeAlive" ); if ( !IsDisabled($name) - and $hash->{helper}->{iowrite} == 0 ) { + and $hash->{helper}->{iowrite} == 0 ) + { - NUKIBridge_Write( $hash, 'info', undef, undef, undef ); + Write( $hash, 'info', undef, undef, undef ); - Log3( $name, 4, "NUKIBridge ($name) - run NUKIBridge_Write" ); + Log3( $name, 4, "NUKIBridge ($name) - run Write" ); } -# InternalTimer( gettimeofday() + 15 + int( rand(15) ), -# 'NUKIBridge_GetCheckBridgeAlive', $hash ); - InternalTimer( gettimeofday() + 10, + InternalTimer( gettimeofday() + 30, 'NUKIBridge_GetCheckBridgeAlive', $hash ); Log3( $name, 4, -"NUKIBridge ($name) - Call InternalTimer for NUKIBridge_GetCheckBridgeAlive" - ); + "NUKIBridge ($name) - Call InternalTimer for GetCheckBridgeAlive" ); } -sub NUKIBridge_firstRun($) { +sub FirstRun($) { my ($hash) = @_; my $name = $hash->{NAME}; RemoveInternalTimer($hash); - NUKIBridge_Write( $hash, 'list', undef, undef, undef ) + Write( $hash, 'list', undef, undef, undef ) if ( !IsDisabled($name) ); - InternalTimer( gettimeofday() + 15, + InternalTimer( gettimeofday() + 5, 'NUKIBridge_GetCheckBridgeAlive', $hash ); return undef; } -sub NUKIBridge_Write($@) { +sub Write($@) { my ( $hash, $endpoint, $param, $nukiId, $deviceType ) = @_; my $obj = { @@ -550,15 +573,17 @@ sub NUKIBridge_Write($@) { }; $hash->{helper}->{lastDeviceAction} = $obj - if ( defined($param) - and $param ); + if ( ( defined($param) + and $param) + or (defined($nukiId) + and $nukiId) ); unshift( @{ $hash->{helper}->{actionQueue} }, $obj ); - NUKIBridge_Call($hash); + BridgeCall($hash); } -sub NUKIBridge_CreateUri($$) { +sub CreateUri($$) { my ( $hash, $obj ) = @_; my $name = $hash->{NAME}; @@ -571,11 +596,11 @@ sub NUKIBridge_CreateUri($$) { my $deviceType = $obj->{deviceType}; my $uri = 'http://' . $host . ':' . $port; - $uri .= '/' . $endpoint if ( defined $endpoint ); + $uri .= '/' . $endpoint if ( defined $endpoint ); $uri .= '?token=' . $token if ( defined($token) ); - if ( defined($param) - and defined($deviceType) ) + if ( defined($param) + and defined($deviceType) ) { $uri .= '&action=' . $lockActionsSmartLock{$param} if ( $endpoint ne 'callback/add' @@ -603,7 +628,7 @@ sub NUKIBridge_CreateUri($$) { return $uri; } -sub NUKIBridge_Call($) { +sub BridgeCall($) { my $hash = shift; my $name = $hash->{NAME}; @@ -612,39 +637,44 @@ sub NUKIBridge_Call($) { my $nukiId = $obj->{nukiId}; if ( $hash->{helper}->{iowrite} == 0 ) { - my $uri = NUKIBridge_CreateUri( $hash, $obj ); + my $uri = CreateUri( $hash, $obj ); if ( defined($uri) and $uri ) { $hash->{helper}->{iowrite} = 1; my $param = { - url => $uri, - timeout => 30, - hash => $hash, - nukiId => $nukiId, - endpoint => $endpoint, - header => 'Accept: application/json', - method => 'GET', - callback => \&NUKIBridge_Distribution, - }; + url => $uri, + timeout => 30, + hash => $hash, + nukiId => $nukiId, + endpoint => $endpoint, + header => 'Accept: application/json', + method => 'GET', + callback => \&Distribution, + }; $param->{cl} = $hash->{CL} - if ( ($endpoint eq 'callback/list' - or $endpoint eq 'log') - and ref( $hash->{CL} ) eq 'HASH' ); + if ( + ( + $endpoint eq 'callback/list' + or $endpoint eq 'log' + ) + and ref( $hash->{CL} ) eq 'HASH' + ); HttpUtils_NonblockingGet($param); - Log3( $name, 4, "NUKIBridge ($name) - Send HTTP POST with URL $uri" ); + Log3( $name, 4, + "NUKIBridge ($name) - Send HTTP POST with URL $uri" ); } } else { - push( @{ $hash->{helper}->{actionQueue} },$obj ) - if ( defined($endpoint) + push( @{ $hash->{helper}->{actionQueue} }, $obj ) + if ( defined($endpoint) and $endpoint eq 'lockAction' ); } } -sub NUKIBridge_Distribution($$$) { +sub Distribution($$$) { my ( $param, $err, $json ) = @_; my $hash = $param->{hash}; @@ -663,7 +693,7 @@ sub NUKIBridge_Distribution($$$) { Log3( $name, 4, "NUKIBridge ($name) - Response ERROR: $err" ); Log3( $name, 4, "NUKIBridge ($name) - Response CODE: $param->{code}" ) if ( defined( $param->{code} ) and ( $param->{code} ) ); - + $hash->{helper}->{iowrite} = 0 if ( $hash->{helper}->{iowrite} == 1 ); @@ -672,10 +702,10 @@ sub NUKIBridge_Distribution($$$) { if ( defined($err) ) { if ( $err ne '' ) { if ( $param->{endpoint} eq 'info' ) { - readingsBulkUpdate( $hash, 'state', 'not connected' ) - if ( $hash->{helper}{aliveCount} > 1 ); + readingsBulkUpdate( $hash, 'state', 'not connected' ); +# if ( $hash->{helper}{aliveCount} > 1 ); Log3( $name, 5, "NUKIBridge ($name) - Bridge ist offline" ); - $hash->{helper}{aliveCount} = $hash->{helper}{aliveCount} + 1; +# $hash->{helper}{aliveCount} = $hash->{helper}{aliveCount} + 1; } readingsBulkUpdate( $hash, 'lastError', $err ) @@ -688,7 +718,7 @@ sub NUKIBridge_Distribution($$$) { asyncOutput( $param->{cl}, "Request Error: $err\r\n" ) if ( $param->{cl} && $param->{cl}{canAsyncOutput} ); - + return $err; } } @@ -710,9 +740,11 @@ sub NUKIBridge_Distribution($$$) { @{ $hash->{helper}->{actionQueue} }, $hash->{helper}->{lastDeviceAction} ); - delete $hash->{helper}->{lastDeviceAction}; + + InternalTimer( gettimeofday() + 1, + 'NUKIBridge_BridgeCall', $hash ); } - + asyncOutput( $param->{cl}, "Request Error: $err\r\n" ) if ( $param->{cl} && $param->{cl}{canAsyncOutput} ); @@ -732,7 +764,7 @@ sub NUKIBridge_Distribution($$$) { . ' without any data after requesting' ); asyncOutput( $param->{cl}, "Request Error: $err\r\n" ) - if ( $param->{cl} && $param->{cl}{canAsyncOutput} ); + if ( $param->{cl} && $param->{cl}{canAsyncOutput} ); } if ( ( $json =~ /Error/i ) and exists( $param->{code} ) ) { @@ -752,7 +784,7 @@ sub NUKIBridge_Distribution($$$) { readingsEndUpdate( $hash, 1 ); asyncOutput( $param->{cl}, "Request Error: $err\r\n" ) - if ( $param->{cl} && $param->{cl}{canAsyncOutput} ); + if ( $param->{cl} && $param->{cl}{canAsyncOutput} ); return $param->{code}; } @@ -763,17 +795,20 @@ sub NUKIBridge_Distribution($$$) { readingsEndUpdate( $hash, 1 ); + readingsSingleUpdate( $hash, 'state', 'connected', 1 ); + Log3( $name, 5, "NUKIBridge ($name) - Bridge ist online" ); + if ( $param->{endpoint} eq 'callback/list' ) { - NUKIBridge_getCallbackList($param,$json); + getCallbackList( $param, $json ); return undef; } elsif ( $param->{endpoint} eq 'log' ) { - NUKIBridge_getLogfile($param,$json); + getLogfile( $param, $json ); return undef; } if ( $hash == $dhash ) { - NUKIBridge_ResponseProcessing( $hash, $json, $param->{endpoint} ); + ResponseProcessing( $hash, $json, $param->{endpoint} ); } else { my $decode_json = eval { decode_json($json) }; @@ -788,14 +823,14 @@ sub NUKIBridge_Distribution($$$) { Dispatch( $hash, $json, undef ); } - InternalTimer( gettimeofday() + 1, 'NUKIBridge_Call', $hash ) + InternalTimer( gettimeofday() + 3, 'NUKIBridge_BridgeCall', $hash ) if ( defined( $hash->{helper}->{actionQueue} ) and scalar( @{ $hash->{helper}->{actionQueue} } ) > 0 ); return undef; } -sub NUKIBridge_ResponseProcessing($$$) { +sub ResponseProcessing($$$) { my ( $hash, $json, $endpoint ) = @_; my $name = $hash->{NAME}; @@ -840,7 +875,7 @@ sub NUKIBridge_ResponseProcessing($$$) { @buffer = split( '"scanResults": \[', $json ) if ( $endpoint eq 'info' ); - my ( $json, $tail ) = NUKIBridge_ParseJSON( $hash, $buffer[1] ); + my ( $json, $tail ) = ParseJSON( $hash, $buffer[1] ); while ($json) { Log3( $name, 5, @@ -860,7 +895,7 @@ sub NUKIBridge_ResponseProcessing($$$) { Dispatch( $hash, $json, undef ) unless ( not defined($tail) and not($tail) ); - ( $json, $tail ) = NUKIBridge_ParseJSON( $hash, $tail ); + ( $json, $tail ) = ParseJSON( $hash, $tail ); Log3( $name, 5, "NUKIBridge ($name) - Nach Sub: Laenge JSON: " @@ -872,12 +907,9 @@ sub NUKIBridge_ResponseProcessing($$$) { } } - NUKIBridge_InfoProcessing( $hash, $decode_json ) + InfoProcessing( $hash, $decode_json ) if ( $endpoint eq 'info' ); - readingsSingleUpdate( $hash, 'state', 'connected', 1 ); - Log3( $name, 5, "NUKIBridge ($name) - Bridge ist online" ); - $hash->{helper}{aliveCount} = 0; } else { @@ -891,7 +923,7 @@ $json" return undef; } -sub NUKIBridge_CGI() { +sub CGI() { my ($request) = @_; my $hash; @@ -946,7 +978,7 @@ matching NukiId at device $name" return ( 'text/plain; charset=utf-8', 'Call failure: ' . $request ); } -sub NUKIBridge_InfoProcessing($$) { +sub InfoProcessing($$) { my ( $hash, $decode_json ) = @_; my $name = $hash->{NAME}; @@ -975,8 +1007,8 @@ sub NUKIBridge_InfoProcessing($$) { readingsEndUpdate( $hash, 1 ); } -sub NUKIBridge_getLogfile($$) { - my ($param,$json) = @_; +sub getLogfile($$) { + my ( $param, $json ) = @_; my $hash = $param->{hash}; my $name = $hash->{NAME}; @@ -989,10 +1021,11 @@ sub NUKIBridge_getLogfile($$) { Log3( $name, 4, "NUKIBridge ($name) - Log data are collected and processed" ); - + if ( $param->{cl} and $param->{cl}->{TYPE} eq 'FHEMWEB' ) { if ( ref($decode_json) eq 'ARRAY' and scalar( @{$decode_json} ) > 0 ) { - Log3( $name, 4, "NUKIBridge ($name) - created Table with log file" ); + Log3( $name, 4, + "NUKIBridge ($name) - created Table with log file" ); my $ret = ''; $ret .= '
'; $ret .= ''; @@ -1034,8 +1067,8 @@ sub NUKIBridge_getLogfile($$) { } } -sub NUKIBridge_getCallbackList($$) { - my ($param,$json) = @_; +sub getCallbackList($$) { + my ( $param, $json ) = @_; my $hash = $param->{hash}; my $name = $hash->{NAME}; @@ -1048,12 +1081,13 @@ sub NUKIBridge_getCallbackList($$) { Log3( $name, 4, "NUKIBridge ($name) - Callback data are collected and processed" ); - + if ( $param->{cl} and $param->{cl}->{TYPE} eq 'FHEMWEB' ) { if ( ref( $decode_json->{callbacks} ) eq 'ARRAY' and scalar( @{ $decode_json->{callbacks} } ) > 0 ) { - Log3( $name, 4, "NUKIBridge ($name) - created Table with log file" ); + Log3( $name, 4, + "NUKIBridge ($name) - created Table with log file" ); my $ret = '
'; @@ -1075,7 +1109,7 @@ sub NUKIBridge_getCallbackList($$) { $ret .= '
'; - + asyncOutput( $param->{cl}, $ret ) if ( $param->{cl} and $param->{cl}{canAsyncOutput} ); return; @@ -1083,7 +1117,7 @@ sub NUKIBridge_getCallbackList($$) { } } -sub NUKIBridge_ParseJSON($$) { +sub ParseJSON($$) { my ( $hash, $buffer ) = @_; my $name = $hash->{NAME}; @@ -1302,7 +1336,7 @@ sub NUKIBridge_ParseJSON($$) { ], "release_status": "under develop", "license": "GPL_2", - "version": "v1.9.4", + "version": "v1.9.13", "x_apiversion": "1.9", "author": [ "Marko Oldenburg " diff --git a/fhem/FHEM/74_NUKIDevice.pm b/fhem/FHEM/74_NUKIDevice.pm index e16e87fd8..f4a86c40e 100644 --- a/fhem/FHEM/74_NUKIDevice.pm +++ b/fhem/FHEM/74_NUKIDevice.pm @@ -27,9 +27,16 @@ package main; +use strict; +use warnings; + + +package FHEM::NUKIDevice; + use strict; use warnings; use FHEM::Meta; +use GPUtils qw(GP_Import GP_Export); main::LoadModule('NUKIBridge'); @@ -104,16 +111,41 @@ if ($@) { } } +## Import der FHEM Funktionen +#-- Run before package compilation +BEGIN { -# Declare functions -sub NUKIDevice_Initialize($); -sub NUKIDevice_Define($$); -sub NUKIDevice_Undef($$); -sub NUKIDevice_Attr(@); -sub NUKIDevice_Set($$@); -sub NUKIDevice_GetUpdate($); -sub NUKIDevice_Parse($$); -sub NUKIDevice_WriteReadings($$); + # Import from main context + GP_Import( + qw( + readingsSingleUpdate + readingsBulkUpdate + readingsBeginUpdate + readingsEndUpdate + readingFnAttributes + makeDeviceName + defs + modules + Log3 + CommandAttr + AttrVal + IsDisabled + deviceEvents + init_done + InternalVal + ReadingsVal + AssignIoPort + IOWrite + data) + ); +} + +#-- Export to main context with different name +GP_Export( + qw( + Initialize + ) +); my %deviceTypes = ( 0 => 'smartlock', @@ -180,18 +212,19 @@ my %lockStates = ( my %deviceTypeIds = reverse(%deviceTypes); -sub NUKIDevice_Initialize($) { +sub Initialize($) { my ($hash) = @_; $hash->{Match} = '^{.*}$'; - $hash->{SetFn} = 'NUKIDevice_Set'; - $hash->{DefFn} = 'NUKIDevice_Define'; - $hash->{UndefFn} = 'NUKIDevice_Undef'; - $hash->{AttrFn} = 'NUKIDevice_Attr'; - $hash->{ParseFn} = 'NUKIDevice_Parse'; + $hash->{SetFn} = 'FHEM::NUKIDevice::Set'; + $hash->{DefFn} = 'FHEM::NUKIDevice::Define'; + $hash->{UndefFn} = 'FHEM::NUKIDevice::Undef'; + $hash->{NotifyFn} = 'FHEM::NUKIDevice::Notify'; + $hash->{AttrFn} = 'FHEM::NUKIDevice::Attr'; + $hash->{ParseFn} = 'FHEM::NUKIDevice::Parse'; - $hash->{AttrList} = + $hash->{AttrList} = 'IODev ' . 'model:opener,smartlock ' . 'disable:1 ' @@ -200,7 +233,7 @@ sub NUKIDevice_Initialize($) { return FHEM::Meta::InitMod( __FILE__, $hash ); } -sub NUKIDevice_Define($$) { +sub Define($$) { my ( $hash, $def ) = @_; my @a = split( '[ \t][ \t]*', $def ); @@ -218,6 +251,7 @@ sub NUKIDevice_Define($$) { $hash->{DEVICETYPE} = ( defined $deviceType ) ? $deviceType : 0; $hash->{VERSION} = version->parse($VERSION)->normal; $hash->{STATE} = 'Initialized'; + $hash->{NOTIFYDEV} = 'global,autocreate,' . $name; my $iodev = AttrVal( $name, 'IODev', 'none' ); @@ -254,35 +288,28 @@ sub NUKIDevice_Define($$) { CommandAttr( undef, $name . ' model ' . $deviceTypes{$deviceType} ) if ( AttrVal( $name, 'model', 'none' ) eq 'none' ); - if ($init_done) { - InternalTimer( gettimeofday() + int( rand(10) ), - "NUKIDevice_GetUpdate", $hash ); - } - else { - InternalTimer( gettimeofday() + 15 + int( rand(5) ), - "NUKIDevice_GetUpdate", $hash ); - } - $modules{NUKIDevice}{defptr}{$nukiId} = $hash; + GetUpdate($hash) + if ( ReadingsVal($name,'success','none') eq 'none' + and $init_done ); + return undef; } -sub NUKIDevice_Undef($$) { +sub Undef($$) { my ( $hash, $arg ) = @_; my $nukiId = $hash->{NUKIID}; my $name = $hash->{NAME}; - RemoveInternalTimer($hash); - Log3( $name, 3, "NUKIDevice ($name) - undefined with NukiId: $nukiId" ); delete( $modules{NUKIDevice}{defptr}{$nukiId} ); return undef; } -sub NUKIDevice_Attr(@) { +sub Attr(@) { my ( $cmd, $name, $attrName, $attrVal ) = @_; my $hash = $defs{$name}; @@ -322,7 +349,33 @@ sub NUKIDevice_Attr(@) { return undef; } -sub NUKIDevice_Set($$@) { +sub Notify($$) { + + my ( $hash, $dev ) = @_; + my $name = $hash->{NAME}; + return if ( IsDisabled($name) ); + + my $devname = $dev->{NAME}; + my $devtype = $dev->{TYPE}; + my $events = deviceEvents( $dev, 1 ); + return if ( !$events ); + + GetUpdate($hash) + if ( + ( + grep /^INITIALIZED$/, @{$events} + or grep /^REREADCFG$/, @{$events} + or grep /^MODIFIED.$name$/, @{$events} + or grep /^DEFINED.$name$/, @{$events} + ) + and $devname eq 'global' + and $init_done + ); + + return; +} + +sub Set($$@) { my ( $hash, $name, @aa ) = @_; my ( $cmd, @args ) = @aa; @@ -331,7 +384,7 @@ sub NUKIDevice_Set($$@) { if ( lc($cmd) eq 'statusrequest' ) { return ('usage: statusRequest') if ( @args != 0 ); - NUKIDevice_GetUpdate($hash); + GetUpdate($hash); return undef; } elsif ($cmd eq 'lock' @@ -371,22 +424,20 @@ sub NUKIDevice_Set($$@) { return undef; } -sub NUKIDevice_GetUpdate($) { +sub GetUpdate($) { my $hash = shift; my $name = $hash->{NAME}; - RemoveInternalTimer($hash); - - IOWrite( $hash, 'lockState', undef, $hash->{NUKIID}, $hash->{DEVICETYPE} ) - if ( !IsDisabled($name) ); - Log3( $name, 5, "NUKIDevice ($name) - NUKIDevice_GetUpdate Call IOWrite" ) - if ( !IsDisabled($name) ); + if ( !IsDisabled($name) ) { + IOWrite( $hash, 'lockState', undef, $hash->{NUKIID}, $hash->{DEVICETYPE} ); + Log3( $name, 2, "NUKIDevice ($name) - GetUpdate Call IOWrite" ); + } return undef; } -sub NUKIDevice_Parse($$) { +sub Parse($$) { my ( $hash, $json ) = @_; my $name = $hash->{NAME}; @@ -420,7 +471,7 @@ sub NUKIDevice_Parse($$) { if ( my $hash = $modules{NUKIDevice}{defptr}{$nukiId} ) { my $name = $hash->{NAME}; - NUKIDevice_WriteReadings( $hash, $decode_json ); + WriteReadings( $hash, $decode_json ); Log3( $name, 4, "NUKIDevice ($name) - find logical device: $hash->{NAME}" ); @@ -455,10 +506,10 @@ sub NUKIDevice_Parse($$) { Log3( $name, 5, "NUKIDevice ($name) - parse status message for $name" ); - NUKIDevice_WriteReadings( $hash, $decode_json ); + WriteReadings( $hash, $decode_json ); } -sub NUKIDevice_WriteReadings($$) { +sub WriteReadings($$) { my ( $hash, $decode_json ) = @_; my $name = $hash->{NAME}; @@ -689,7 +740,7 @@ sub NUKIDevice_WriteReadings($$) { ], "release_status": "under develop", "license": "GPL_2", - "version": "v1.9.1", + "version": "v1.9.10", "author": [ "Marko Oldenburg " ],