Merge pull request 'fix little model bugs' (#12) from patch-discoveryAuthorization into devel

Reviewed-on: #12
This commit is contained in:
Marko Oldenburg 2021-11-27 23:00:20 +01:00
commit 1b001e942a
4 changed files with 159 additions and 119 deletions

View File

@ -64,8 +64,7 @@ sub Initialize {
$hash->{NotifyFn} = \&FHEM::Devices::Nuki::Bridge::Notify; $hash->{NotifyFn} = \&FHEM::Devices::Nuki::Bridge::Notify;
$hash->{AttrFn} = \&FHEM::Devices::Nuki::Bridge::Attr; $hash->{AttrFn} = \&FHEM::Devices::Nuki::Bridge::Attr;
$hash->{AttrList} = $hash->{AttrList} =
'disable:1 ' 'disable:1 ' . 'port '
. 'port '
. 'webhookFWinstance:' . 'webhookFWinstance:'
. $webhookFWinstance . ' ' . $webhookFWinstance . ' '
. 'webhookHttpHostname ' . 'webhookHttpHostname '

View File

@ -1,4 +1,4 @@
UPD 2021-11-27_21:26:06 9224 FHEM/73_NUKIBridge.pm UPD 2021-11-27_22:52:03 9217 FHEM/73_NUKIBridge.pm
UPD 2021-11-27_21:30:28 7569 FHEM/74_NUKIDevice.pm UPD 2021-11-27_21:36:27 7569 FHEM/74_NUKIDevice.pm
UPD 2021-11-27_21:26:06 40444 lib/FHEM/Devices/Nuki/Bridge.pm UPD 2021-11-27_22:52:20 40367 lib/FHEM/Devices/Nuki/Bridge.pm
UPD 2021-11-27_21:32:02 15123 lib/FHEM/Devices/Nuki/Device.pm UPD 2021-11-27_22:56:00 15826 lib/FHEM/Devices/Nuki/Device.pm

View File

@ -145,11 +145,12 @@ sub Define {
use version 0.60; our $VERSION = FHEM::Meta::Get( $hash, 'version' ); use version 0.60; our $VERSION = FHEM::Meta::Get( $hash, 'version' );
my ( $name, undef, $host, $token ) = split( m{\s+}xms, $def ); my ( $name, undef, $host, $token ) = split( m{\s+}xms, $def );
# return ('too few parameters: define <name> NUKIBridge <HOST> <TOKEN>')
# if ( !defined($host)
# || !defined($token) );
my $port = ::AttrVal($name, 'port', 8080); # return ('too few parameters: define <name> NUKIBridge <HOST> <TOKEN>')
# if ( !defined($host)
# || !defined($token) );
my $port = ::AttrVal( $name, 'port', 8080 );
my $infix = 'NUKIBridge'; my $infix = 'NUKIBridge';
$hash->{HOST} = $host // 'discover'; $hash->{HOST} = $host // 'discover';
$hash->{PORT} = $port; $hash->{PORT} = $port;
@ -165,18 +166,18 @@ sub Define {
$hash->{WEBHOOK_REGISTER} = "unregistered"; $hash->{WEBHOOK_REGISTER} = "unregistered";
::readingsSingleUpdate($hash, 'state', 'Initialized', 1); ::readingsSingleUpdate( $hash, 'state', 'Initialized', 1 );
::RemoveInternalTimer($hash); ::RemoveInternalTimer($hash);
return BridgeDiscover($hash,'discover') return BridgeDiscover( $hash, 'discover' )
if ( $hash->{HOST} eq 'discover' if ( $hash->{HOST} eq 'discover'
&& $hash->{TOKEN} eq 'discover' ); && $hash->{TOKEN} eq 'discover' );
::Log3( $name, 3, ::Log3( $name, 3,
"NUKIBridge ($name) - defined with host $host on port $port, Token $token" "NUKIBridge ($name) - defined with host $host on port $port, Token $token"
); );
if ( if (
addExtension( addExtension(
$name, $name,
@ -187,7 +188,7 @@ sub Define {
{ {
$hash->{fhem}{infix} = $infix; $hash->{fhem}{infix} = $infix;
} }
$::modules{NUKIBridge}{defptr}{ $hash->{HOST} } = $hash; $::modules{NUKIBridge}{defptr}{ $hash->{HOST} } = $hash;
return; return;
@ -220,15 +221,15 @@ sub Attr {
if ( $attrName eq 'disable' ) { if ( $attrName eq 'disable' ) {
if ( $cmd eq 'set' && $attrVal == 1 ) { if ( $cmd eq 'set' && $attrVal == 1 ) {
::readingsSingleUpdate($hash, 'state', 'disabled', 1); ::readingsSingleUpdate( $hash, 'state', 'disabled', 1 );
::Log3( $name, 3, "NUKIBridge ($name) - disabled" ); ::Log3( $name, 3, "NUKIBridge ($name) - disabled" );
} }
elsif ( $cmd eq 'del' ) { elsif ( $cmd eq 'del' ) {
::readingsSingleUpdate($hash, 'state', 'active', 1); ::readingsSingleUpdate( $hash, 'state', 'active', 1 );
::Log3( $name, 3, "NUKIBridge ($name) - enabled" ); ::Log3( $name, 3, "NUKIBridge ($name) - enabled" );
} }
} }
if ( $attrName eq 'port' ) { if ( $attrName eq 'port' ) {
if ( $cmd eq 'set' ) { if ( $cmd eq 'set' ) {
$hash->{PORT} = $attrVal; $hash->{PORT} = $attrVal;
@ -236,7 +237,8 @@ sub Attr {
} }
elsif ( $cmd eq 'del' ) { elsif ( $cmd eq 'del' ) {
$hash->{PORT} = 8080; $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' ) { if ( $cmd eq 'set' ) {
::Log3( $name, 3, ::Log3( $name, 3,
"NUKIBridge ($name) - enable disabledForIntervals" ); "NUKIBridge ($name) - enable disabledForIntervals" );
::readingsSingleUpdate($hash, 'state', 'Unknown', 1); ::readingsSingleUpdate( $hash, 'state', 'Unknown', 1 );
} }
elsif ( $cmd eq 'del' ) { elsif ( $cmd eq 'del' ) {
::readingsSingleUpdate($hash, 'state', 'active', 1); ::readingsSingleUpdate( $hash, 'state', 'active', 1 );
::Log3( $name, 3, ::Log3( $name, 3,
"NUKIBridge ($name) - delete disabledForIntervals" ); "NUKIBridge ($name) - delete disabledForIntervals" );
} }
@ -395,10 +397,8 @@ sub removeExtension {
::Log3( $name, 2, ::Log3( $name, 2,
"NUKIBridge ($name) - Unregistering NUKIBridge for webhook URL $url..." "NUKIBridge ($name) - Unregistering NUKIBridge for webhook URL $url..."
) ) if ( defined($name) );
if ( defined($name) );
delete $::data{FWEXT}{$url}; delete $::data{FWEXT}{$url};
return; return;
@ -501,7 +501,14 @@ sub GetCheckBridgeAlive {
if ( !::IsDisabled($name) if ( !::IsDisabled($name)
&& $hash->{helper}->{iowrite} == 0 ) && $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" ); ::Log3( $name, 4, "NUKIBridge ($name) - run Write" );
} }
@ -521,6 +528,7 @@ sub FirstRun {
Write( $hash, 'list', undef ) Write( $hash, 'list', undef )
if ( !::IsDisabled($name) ); if ( !::IsDisabled($name) );
$hash->{helper}->{runInfo} = 0;
return ::InternalTimer( ::gettimeofday() + 5, return ::InternalTimer( ::gettimeofday() + 5,
\&FHEM::Devices::Nuki::Bridge::GetCheckBridgeAlive, $hash ); \&FHEM::Devices::Nuki::Bridge::GetCheckBridgeAlive, $hash );
} }
@ -655,10 +663,11 @@ sub Distribution {
my $err = shift; my $err = shift;
my $json = shift; my $json = shift;
my $hash = $param->{hash}; my $hash = $param->{hash};
# my $doTrigger = $param->{doTrigger};
my $name = $hash->{NAME}; # my $doTrigger = $param->{doTrigger};
my $host = $hash->{HOST}; my $name = $hash->{NAME};
my $host = $hash->{HOST};
my $dhash = $hash; my $dhash = $hash;
@ -779,7 +788,7 @@ sub Distribution {
::readingsEndUpdate( $hash, 1 ); ::readingsEndUpdate( $hash, 1 );
::readingsSingleUpdate($hash, 'state', 'connected', 1); ::readingsSingleUpdate( $hash, 'state', 'connected', 1 );
::Log3( $name, 5, "NUKIBridge ($name) - Bridge ist online" ); ::Log3( $name, 5, "NUKIBridge ($name) - Bridge ist online" );
if ( $param->{endpoint} eq 'callback/list' ) { if ( $param->{endpoint} eq 'callback/list' ) {
@ -1110,9 +1119,9 @@ sub getCallbackList {
if ( scalar( @{ $decode_json->{callbacks} } ) > 0 ) { if ( scalar( @{ $decode_json->{callbacks} } ) > 0 ) {
for my $cb ( @{ $decode_json->{callbacks} } ) { for my $cb ( @{ $decode_json->{callbacks} } ) {
$aHref = $aHref = "<a href=\""
"<a href=\""
# . $::FW_httpheader->{host} # . $::FW_httpheader->{host}
. "/fhem?cmd=set+" . "/fhem?cmd=set+"
. $name . $name
. "+callbackRemove+" . "+callbackRemove+"
@ -1272,77 +1281,77 @@ sub ParseJSON {
} }
sub BridgeDiscover { sub BridgeDiscover {
my $hash = shift; my $hash = shift;
my $endpoint = shift; my $endpoint = shift;
my $bridge = shift; my $bridge = shift;
my $name = $hash->{NAME}; my $name = $hash->{NAME};
my $url = ( $endpoint eq 'discover' && !defined($bridge) my $url = (
? 'https://api.nuki.io/discover/bridges' $endpoint eq 'discover' && !defined($bridge)
: 'http://' . $bridge->{'ip'} . ':' . $bridge->{'port'} . '/auth' ); ? 'https://api.nuki.io/discover/bridges'
my $timeout = ( $endpoint eq 'discover' && !defined($bridge) : 'http://' . $bridge->{'ip'} . ':' . $bridge->{'port'} . '/auth'
? 5 );
: 35 ); my $timeout = (
$endpoint eq 'discover' && !defined($bridge)
? 5
: 35
);
if ( $endpoint eq 'discover' ) { if ( $endpoint eq 'discover' ) {
::Log3( $name, 3, ::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' ) { elsif ( $endpoint eq 'getApiToken' ) {
::Log3( $name, 3, ::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( ::HttpUtils_NonblockingGet(
{ {
url => $url, url => $url,
timeout => $timeout, timeout => $timeout,
hash => $hash, hash => $hash,
header => 'Accept: application/json', header => 'Accept: application/json',
endpoint => $endpoint, endpoint => $endpoint,
host => $bridge->{'ip'}, host => $bridge->{'ip'},
port => $bridge->{'port'}, port => $bridge->{'port'},
method => 'GET', method => 'GET',
callback => \&BridgeDiscoverRequest, callback => \&BridgeDiscoverRequest,
} }
); );
::Log3( $name, 3, ::Log3( $name, 3,
"NUKIBridge ($name) - Send Discover request to Nuki Cloud" ) "NUKIBridge ($name) - Send Discover request to Nuki Cloud" )
if ( $endpoint eq 'discover' ); if ( $endpoint eq 'discover' );
::Log3( $name, 3, ::Log3( $name, 3, "NUKIBridge ($name) - get API Token from the Bridge" )
"NUKIBridge ($name) - get API Token from the Bridge" ) if ( $endpoint eq 'getApiToken' );
if ( $endpoint eq 'getApiToken' );
return; return;
} }
sub BridgeDiscoverRequest { sub BridgeDiscoverRequest {
my $param = shift; my $param = shift;
my $err = shift; my $err = shift;
my $json = shift; my $json = shift;
my $hash = $param->{hash}; my $hash = $param->{hash};
my $name = $hash->{NAME}; my $name = $hash->{NAME};
if ( defined($err) if ( defined($err)
&& $err ne '' ) && $err ne '' )
{ {
return ::Log3( $name, 3, return ::Log3( $name, 3, "NUKIBridge ($name) - Error: $err" );
"NUKIBridge ($name) - Error: $err");
} }
elsif ( exists( $param->{code}) elsif ( exists( $param->{code} )
&& $param->{code} != 200 ) && $param->{code} != 200 )
{ {
return ::Log3( $name, 3, 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; my $decode_json;
@ -1351,27 +1360,30 @@ sub BridgeDiscoverRequest {
::Log3( $name, 3, "NUKIBridge ($name) - JSON error while request: $@" ); ::Log3( $name, 3, "NUKIBridge ($name) - JSON error while request: $@" );
return; return;
} }
if ( $param->{endpoint} eq 'discover' ) { if ( $param->{endpoint} eq 'discover' ) {
return ::readingsSingleUpdate($hash, 'state', 'no bridges discovered', 1) return ::readingsSingleUpdate( $hash, 'state', 'no bridges discovered',
if ( scalar(@{$decode_json->{bridges}}) == 0 1 )
if ( scalar( @{ $decode_json->{bridges} } ) == 0
&& $decode_json->{errorCode} == 0 ); && $decode_json->{errorCode} == 0 );
return BridgeDiscover_getAPIToken($hash,$decode_json); return BridgeDiscover_getAPIToken( $hash, $decode_json );
} }
elsif ( $param->{endpoint} eq 'getApiToken' ) { elsif ( $param->{endpoint} eq 'getApiToken' ) {
::readingsSingleUpdate($hash, 'state', 'modefined bridge device in progress', 1); ::readingsSingleUpdate( $hash, 'state',
'modefined bridge device in progress', 1 );
$decode_json->{host} = $param->{host};
$decode_json->{port} = $param->{port}; $decode_json->{host} = $param->{host};
$decode_json->{port} = $param->{port};
return ModefinedBridgeDevices($hash,$decode_json)
return ModefinedBridgeDevices( $hash, $decode_json )
if ( $decode_json->{success} == 1 ); if ( $decode_json->{success} == 1 );
return ::readingsSingleUpdate($hash, 'state', 'get api token failed', 1); return ::readingsSingleUpdate( $hash, 'state', 'get api token failed',
1 );
} }
return; return;
} }
@ -1380,16 +1392,17 @@ sub BridgeDiscover_getAPIToken {
my $decode_json = shift; my $decode_json = shift;
my $name = $hash->{NAME}; 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.'; 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); ::readingsSingleUpdate( $hash, 'state', $pullApiKeyMessage, 1 );
for ( @{$decode_json->{bridges}} ) { for ( @{ $decode_json->{bridges} } ) {
BridgeDiscover($hash,'getApiToken',$_); BridgeDiscover( $hash, 'getApiToken', $_ );
} }
return; return;
} }
@ -1398,17 +1411,15 @@ sub ModefinedBridgeDevices {
my $decode_json = shift; my $decode_json = shift;
my $name = $hash->{NAME}; my $name = $hash->{NAME};
::CommandAttr( undef, $name . ' port ' . $decode_json->{port} ) ::CommandAttr( undef, $name . ' port ' . $decode_json->{port} )
if ( $decode_json->{port} != 8080 ); if ( $decode_json->{port} != 8080 );
::CommandDefMod( undef, ::CommandDefMod( undef,
$name $name
. ' NUKIBridge ' . ' NUKIBridge '
. $decode_json->{host} . ' ' . $decode_json->{host} . ' '
. $decode_json->{token} ); . $decode_json->{token} );
return; return;
} }
1; 1;

View File

@ -111,6 +111,9 @@ my %deviceTypes = (
4 => 'smartlock3' 4 => 'smartlock3'
); );
my %deviceTypeIds = reverse(%deviceTypes);
my %modes = ( my %modes = (
2 => { 2 => {
0 => 'door mode', 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 { sub Define {
my $hash = shift; my $hash = shift;
@ -199,11 +209,12 @@ sub Define {
? $deviceType ? $deviceType
: 0; : 0;
$hash->{NUKIID} = $nukiId; $hash->{NUKIID} = $nukiId;
$hash->{DEVICETYPEID} = $deviceType; $hash->{DEVICETYPEID} = $deviceType;
$hash->{VERSION} = version->parse($VERSION)->normal; $hash->{VERSION} = version->parse($VERSION)->normal;
$hash->{STATE} = 'Initialized'; $hash->{STATE} = 'Initialized';
$hash->{NOTIFYDEV} = 'global,autocreate,' . $name; $hash->{NOTIFYDEV} = 'global,autocreate,' . $name;
my $iodev = ::AttrVal( $name, 'IODev', 'none' ); my $iodev = ::AttrVal( $name, 'IODev', 'none' );
@ -447,7 +458,7 @@ sub Parse {
WriteReadings( $hash, $decode_json ); WriteReadings( $hash, $decode_json );
::Log3( $name, 4, ::Log3( $name, 4,
"NUKIDevice ($name) - find logical device: $hash->{NAME}" ); "NUKIDevice ($name) - find logical device: $hash->{NAME}" );
return $hash->{NAME}; return $hash->{NAME};
} }
else { else {
@ -541,24 +552,43 @@ sub WriteReadings {
&& $t ne 'deviceType' && $t ne 'deviceType'
&& $t ne 'paired' && $t ne 'paired'
&& $t ne 'batteryCritical' && $t ne 'batteryCritical'
&& $t ne 'timestamp' ); && $t ne 'batteryChargeState'
&& $t ne 'batteryCharging'
&& $t ne 'timestamp'
&& $t ne 'doorsensorState'
&& $t ne 'doorsensorStateName' );
::readingsBulkUpdate( $hash, $t,
( $v =~ m/^[0-9]$/ ? $lockStates{$v}{ $hash->{DEVICETYPEID} } : $v ) ) ::readingsBulkUpdate(
if ( $t eq 'state' ); $hash, $t,
(
$v =~ m/^[0-9]$/
? $lockStates{$v}{ $hash->{DEVICETYPEID} }
: $v
)
) if ( $t eq 'state' );
::readingsBulkUpdate( $hash, $t, $modes{$v}{ $hash->{DEVICETYPEID} } ) ::readingsBulkUpdate( $hash, $t, $modes{$v}{ $hash->{DEVICETYPEID} } )
if ( $t eq 'mode' ); if ( $t eq 'mode' );
::readingsBulkUpdate( $hash, $t, $deviceTypes{$v} ) ::readingsBulkUpdate( $hash, $t, $deviceTypes{$v} )
if ( $t eq 'deviceType' ); if ( $t eq 'deviceType' );
::readingsBulkUpdate( $hash, $t, $doorsensorStates{$v} )
if ( $t eq 'doorsensorState' );
::readingsBulkUpdate( $hash, $t, ( $v == 1 ? 'true' : 'false' ) ) ::readingsBulkUpdate( $hash, $t, ( $v == 1 ? 'true' : 'false' ) )
if ( $t eq 'paired' ); if ( $t eq 'paired' );
::readingsBulkUpdate( $hash, $t, ( $v == 1 ? 'true' : 'false' ) )
if ( $t eq 'batteryCharging' );
::readingsBulkUpdate( $hash, 'batteryState', ::readingsBulkUpdate( $hash, 'batteryState',
( ( $v eq 'true' or $v == 1 ) ? 'low' : 'ok' ) ) ( $v == 1 ? 'low' : 'ok' ) )
if ( $t eq 'batteryCritical' ); if ( $t eq 'batteryCritical' );
::readingsBulkUpdate( $hash, 'batteryPercent', $v )
if ( $t eq 'batteryChargeState' );
} }
::readingsEndUpdate( $hash, 1 ); ::readingsEndUpdate( $hash, 1 );