add discovery and full automation define bridge with host and port from

fetch API information via /auth endpoint
This commit is contained in:
Marko Oldenburg 2021-11-27 18:08:27 +01:00
parent c21bd2479a
commit a8ea6814ab
4 changed files with 205 additions and 42 deletions

View File

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

View File

@ -1,4 +1,4 @@
UPD 2021-11-27_08:19:45 9207 FHEM/73_NUKIBridge.pm UPD 2021-11-27_18:02:39 9224 FHEM/73_NUKIBridge.pm
UPD 2021-11-27_08:19:45 7548 FHEM/74_NUKIDevice.pm UPD 2021-11-27_18:01:59 7548 FHEM/74_NUKIDevice.pm
UPD 2021-11-27_08:19:45 35495 lib/FHEM/Devices/Nuki/Bridge.pm UPD 2021-11-27_18:08:11 40444 lib/FHEM/Devices/Nuki/Bridge.pm
UPD 2021-11-27_09:51:08 15681 lib/FHEM/Devices/Nuki/Device.pm UPD 2021-11-27_15:28:59 15728 lib/FHEM/Devices/Nuki/Device.pm

View File

@ -145,28 +145,38 @@ 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>') # return ('too few parameters: define <name> NUKIBridge <HOST> <TOKEN>')
if ( !defined($host) # if ( !defined($host)
|| !defined($token) ); # || !defined($token) );
my $port = 8080; my $port = ::AttrVal($name, 'port', 8080);
my $infix = 'NUKIBridge'; my $infix = 'NUKIBridge';
$hash->{HOST} = $host; $hash->{HOST} = $host // 'discover';
$hash->{PORT} = $port; $hash->{PORT} = $port;
$hash->{TOKEN} = $token; $hash->{TOKEN} = $token // 'discover';
$hash->{NOTIFYDEV} = 'global,' . $name; $hash->{NOTIFYDEV} = 'global,' . $name;
$hash->{VERSION} = version->parse($VERSION)->normal; $hash->{VERSION} = version->parse($VERSION)->normal;
$hash->{BRIDGEAPI} = FHEM::Meta::Get( $hash, 'x_apiversion' ); $hash->{BRIDGEAPI} = FHEM::Meta::Get( $hash, 'x_apiversion' );
$hash->{helper}->{actionQueue} = []; $hash->{helper}->{actionQueue} = [];
$hash->{helper}->{iowrite} = 0; $hash->{helper}->{iowrite} = 0;
::CommandAttr( undef, $name . ' room NUKI' )
if ( ::AttrVal( $name, 'room', 'none' ) eq 'none' );
$hash->{WEBHOOK_REGISTER} = "unregistered";
::readingsSingleUpdate($hash, 'state', 'Initialized', 1);
::RemoveInternalTimer($hash);
return BridgeDiscover($hash,'discover')
if ( $hash->{HOST} 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"
); );
::CommandAttr( undef, $name . ' room NUKI' )
if ( ::AttrVal( $name, 'room', 'none' ) eq 'none' );
if ( if (
addExtension( addExtension(
$name, $name,
@ -178,12 +188,6 @@ sub Define {
$hash->{fhem}{infix} = $infix; $hash->{fhem}{infix} = $infix;
} }
$hash->{WEBHOOK_REGISTER} = "unregistered";
::readingsSingleUpdate( $hash, 'state', 'Initialized', 1 );
::RemoveInternalTimer($hash);
$::modules{NUKIBridge}{defptr}{ $hash->{HOST} } = $hash; $::modules{NUKIBridge}{defptr}{ $hash->{HOST} } = $hash;
return; return;
@ -225,6 +229,17 @@ sub Attr {
} }
} }
if ( $attrName eq 'port' ) {
if ( $cmd eq 'set' ) {
$hash->{PORT} = $attrVal;
::Log3( $name, 3, "NUKIBridge ($name) - change bridge port" );
}
elsif ( $cmd eq 'del' ) {
$hash->{PORT} = 8080;
::Log3( $name, 3, "NUKIBridge ($name) - set bridge port to default" );
}
}
if ( $attrName eq 'disabledForIntervals' ) { if ( $attrName eq 'disabledForIntervals' ) {
if ( $cmd eq 'set' ) { if ( $cmd eq 'set' ) {
::Log3( $name, 3, ::Log3( $name, 3,
@ -345,6 +360,8 @@ sub Notify {
or grep /^DEFINED.$name$/, or grep /^DEFINED.$name$/,
@{$events} @{$events}
) )
&& $hash->{HOST} ne 'discover'
&& $hash->{TOKEN} ne 'discover'
&& $devname eq 'global' && $devname eq 'global'
&& $::init_done && $::init_done
); );
@ -378,7 +395,10 @@ 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) );
delete $::data{FWEXT}{$url}; delete $::data{FWEXT}{$url};
return; return;
@ -636,7 +656,7 @@ sub Distribution {
my $json = shift; my $json = shift;
my $hash = $param->{hash}; my $hash = $param->{hash};
my $doTrigger = $param->{doTrigger}; # my $doTrigger = $param->{doTrigger};
my $name = $hash->{NAME}; my $name = $hash->{NAME};
my $host = $hash->{HOST}; my $host = $hash->{HOST};
@ -1251,4 +1271,144 @@ sub ParseJSON {
return ( $msg, $tail ); return ( $msg, $tail );
} }
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 );
if ( $endpoint eq 'discover' ) {
::Log3( $name, 3,
"NUKIBridge ($name) - Bridge device defined. run discover mode"
);
::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."
);
}
::HttpUtils_NonblockingGet(
{
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' );
return;
}
sub BridgeDiscoverRequest {
my $param = shift;
my $err = shift;
my $json = shift;
my $hash = $param->{hash};
my $name = $hash->{NAME};
if ( defined($err)
&& $err ne '' )
{
return ::Log3( $name, 3,
"NUKIBridge ($name) - Error: $err");
}
elsif ( exists( $param->{code})
&& $param->{code} != 200 )
{
return ::Log3( $name, 3,
"NUKIBridge ($name) - HTTP error Code present. Code: $param->{code}");
}
my $decode_json;
$decode_json = eval { decode_json($json) };
if ($@) {
::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
&& $decode_json->{errorCode} == 0 );
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)
if ( $decode_json->{success} == 1 );
return ::readingsSingleUpdate($hash, 'state', 'get api token failed', 1);
}
return;
}
sub BridgeDiscover_getAPIToken {
my $hash = shift;
my $decode_json = shift;
my $name = $hash->{NAME};
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',$_);
}
return;
}
sub ModefinedBridgeDevices {
my $hash = shift;
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} );
return;
}
1; 1;

View File

@ -219,7 +219,7 @@ sub Define {
$iodev = $hash->{IODev}->{NAME}; $iodev = $hash->{IODev}->{NAME};
$hash->{BRIDGEAPI} = $::defs{$iodev}->{BRIDGEAPI} $hash->{BRIDGEAPI} = $::defs{$iodev}->{BRIDGEAPI}
if ( defined(iodev) if ( defined($iodev)
&& $iodev ); && $iodev );
my $d = $::modules{NUKIDevice}{defptr}{$nukiId}; my $d = $::modules{NUKIDevice}{defptr}{$nukiId};
@ -447,22 +447,24 @@ sub Parse {
::Log3( $name, 4, ::Log3( $name, 4,
"NUKIDevice ($name) - find logical device: $hash->{NAME}" ); "NUKIDevice ($name) - find logical device: $hash->{NAME}" );
return $hash->{NAME};
################## ##################
## Zwischenlösung so für die Umstellung, kann später gelöscht werden ## Zwischenlösung so für die Umstellung, kann später gelöscht werden
if ( ::AttrVal( $name, 'model', '' ) eq '' ) { # if ( ::AttrVal( $name, 'model', '' ) eq '' ) {
::CommandDefMod( undef, # ::CommandDefMod( undef,
$name # $name
. ' NUKIDevice ' # . ' NUKIDevice '
. $hash->{NUKIID} . ' ' # . $hash->{NUKIID} . ' '
. $decode_json->{deviceType} ); # . $decode_json->{deviceType} );
::CommandAttr( undef, # ::CommandAttr( undef,
$name # $name
. ' model ' # . ' model '
. $deviceTypes{ $decode_json->{deviceType} } ); # . $deviceTypes{ $decode_json->{deviceType} } );
::Log3( $name, 2, "NUKIDevice ($name) - redefined Defmod" ); # ::Log3( $name, 2, "NUKIDevice ($name) - redefined Defmod" );
} # }
return $hash->{NAME};
} }
else { else {
::Log3( $name, 4, ::Log3( $name, 4,