mirror of
https://github.com/fhem/fhem-mirror.git
synced 2025-03-10 09:16:53 +00:00
30_HUEBridge.pm: suport for eventstream api (push events)
git-svn-id: https://svn.fhem.de/fhem/trunk@25506 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
parent
e8a5081096
commit
d71fe570b4
@ -1,5 +1,6 @@
|
|||||||
# Add changes at the top of the list. Keep it in ASCII, and 80-char wide.
|
# 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.
|
# Do not insert empty lines here, update check depends on it.
|
||||||
|
- feature: 30_HUEBridge: support eventstream api (push events)
|
||||||
- change: 70_DENON_AVR: added soundformat strings. patch by All-Ex
|
- change: 70_DENON_AVR: added soundformat strings. patch by All-Ex
|
||||||
- change: 50_Signalbot: link improved, reply command added, bugfixes
|
- change: 50_Signalbot: link improved, reply command added, bugfixes
|
||||||
- change: 93_DbRep: new design of sqlCmdHistory, minor fixes
|
- change: 93_DbRep: new design of sqlCmdHistory, minor fixes
|
||||||
|
@ -12,7 +12,8 @@ use warnings;
|
|||||||
|
|
||||||
use FHEM::Meta;
|
use FHEM::Meta;
|
||||||
|
|
||||||
use POSIX;
|
#use POSIX;
|
||||||
|
use Time::HiRes qw(gettimeofday);
|
||||||
use JSON;
|
use JSON;
|
||||||
use Data::Dumper;
|
use Data::Dumper;
|
||||||
|
|
||||||
@ -326,6 +327,9 @@ sub HUEBridge_Undefine($$)
|
|||||||
{
|
{
|
||||||
my ($hash,$arg) = @_;
|
my ($hash,$arg) = @_;
|
||||||
|
|
||||||
|
HUEBridge_closeWebsocket($hash);
|
||||||
|
HUEBridge_closeEventStream($hash);
|
||||||
|
|
||||||
RemoveInternalTimer($hash);
|
RemoveInternalTimer($hash);
|
||||||
return undef;
|
return undef;
|
||||||
}
|
}
|
||||||
@ -345,7 +349,8 @@ HUEBridge_hash2header($)
|
|||||||
|
|
||||||
return $header;
|
return $header;
|
||||||
}
|
}
|
||||||
sub HUEBridge_closeWebsocket($)
|
sub
|
||||||
|
HUEBridge_closeWebsocket($)
|
||||||
{
|
{
|
||||||
my ($hash) = @_;
|
my ($hash) = @_;
|
||||||
my $name = $hash->{NAME};
|
my $name = $hash->{NAME};
|
||||||
@ -361,7 +366,8 @@ sub HUEBridge_closeWebsocket($)
|
|||||||
|
|
||||||
delete($hash->{PORT});
|
delete($hash->{PORT});
|
||||||
}
|
}
|
||||||
sub HUEBridge_openWebsocket($)
|
sub
|
||||||
|
HUEBridge_openWebsocket($)
|
||||||
{
|
{
|
||||||
my ($hash) = @_;
|
my ($hash) = @_;
|
||||||
my $name = $hash->{NAME};
|
my $name = $hash->{NAME};
|
||||||
@ -403,7 +409,65 @@ sub HUEBridge_openWebsocket($)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sub HUEBridge_fillBridgeInfo($$)
|
sub
|
||||||
|
HUEBridge_closeEventStream($)
|
||||||
|
{
|
||||||
|
my ($hash) = @_;
|
||||||
|
my $name = $hash->{NAME};
|
||||||
|
|
||||||
|
RemoveInternalTimer($hash, "HUEBridge_openEventStream" );
|
||||||
|
|
||||||
|
return if( !defined($hash->{helper}{HTTP_CONNECTION}) );
|
||||||
|
|
||||||
|
$hash->{EventStream} = 'closing';
|
||||||
|
Log3 $name, 4, "name: EventStream: $hash->{EventStream}";
|
||||||
|
|
||||||
|
HttpUtils_Close( $hash->{helper}{HTTP_CONNECTION} );
|
||||||
|
delete $hash->{helper}{HTTP_CONNECTION};
|
||||||
|
|
||||||
|
delete $hash->{buf};
|
||||||
|
|
||||||
|
delete($hash->{EventStream});
|
||||||
|
Log3 $name, 4, "name: EventStream: closed";
|
||||||
|
}
|
||||||
|
sub
|
||||||
|
HUEBridge_openEventStream($)
|
||||||
|
{
|
||||||
|
my ($hash) = @_;
|
||||||
|
my $name = $hash->{NAME};
|
||||||
|
|
||||||
|
my $lastID;
|
||||||
|
$lastID = $hash->{helper}{HTTP_CONNECTION}{lastID} if( defined($hash->{helper}{HTTP_CONNECTION}) );
|
||||||
|
|
||||||
|
HUEBridge_closeEventStream($hash);
|
||||||
|
|
||||||
|
$hash->{EventStream} = 'connecting';
|
||||||
|
Log3 $name, 4, "name: EventStream: $hash->{EventStream}";
|
||||||
|
|
||||||
|
my $params = {
|
||||||
|
url => "https://$hash->{host}/eventstream/clip/v2",
|
||||||
|
httpversion => '1.1',
|
||||||
|
method => 'GET',
|
||||||
|
timeout => 60*60,
|
||||||
|
incrementalTimeout => 1,
|
||||||
|
noshutdown => 1,
|
||||||
|
keepalive => 1,
|
||||||
|
header => { Accept => 'text/event-stream',
|
||||||
|
'HUE-Application-Key' => $attr{$name}{key}, },
|
||||||
|
type => 'event',
|
||||||
|
hash => $hash,
|
||||||
|
callback => \&HUEBridge_dispatch,
|
||||||
|
};
|
||||||
|
$params->{header}{'Last-Event-ID'} = $lastID if( $lastID );
|
||||||
|
|
||||||
|
$hash->{helper}{HTTP_CONNECTION} = {};
|
||||||
|
map { $hash->{helper}{HTTP_CONNECTION}{$_} = $params->{$_} } keys %{$params};
|
||||||
|
|
||||||
|
HttpUtils_NonblockingGet( $hash->{helper}{HTTP_CONNECTION} );
|
||||||
|
}
|
||||||
|
|
||||||
|
sub
|
||||||
|
HUEBridge_fillBridgeInfo($$)
|
||||||
{
|
{
|
||||||
my ($hash,$config) = @_;
|
my ($hash,$config) = @_;
|
||||||
my $name = $hash->{NAME};
|
my $name = $hash->{NAME};
|
||||||
@ -413,10 +477,11 @@ sub HUEBridge_fillBridgeInfo($$)
|
|||||||
$hash->{bridgeid} = $config->{bridgeid};
|
$hash->{bridgeid} = $config->{bridgeid};
|
||||||
$hash->{swversion} = $config->{swversion};
|
$hash->{swversion} = $config->{swversion};
|
||||||
$hash->{apiversion} = $config->{apiversion};
|
$hash->{apiversion} = $config->{apiversion};
|
||||||
|
$hash->{zigbeechannel} = $config->{zigbeechannel};
|
||||||
|
|
||||||
if( defined($config->{websocketport}) ) {
|
delete $hash->{has_v2_api};
|
||||||
$hash->{websocketport} = $config->{websocketport};
|
if( $hash->{swversion} >= 1948086000 ) {
|
||||||
HUEBridge_openWebsocket($hash) if( !defined($hash->{CD}) );
|
$hash->{has_v2_api} = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if( $hash->{apiversion} ) {
|
if( $hash->{apiversion} ) {
|
||||||
@ -424,10 +489,22 @@ sub HUEBridge_fillBridgeInfo($$)
|
|||||||
$hash->{helper}{apiversion} = ($l[0] << 16) + ($l[1] << 8) + $l[2];
|
$hash->{helper}{apiversion} = ($l[0] << 16) + ($l[1] << 8) + $l[2];
|
||||||
}
|
}
|
||||||
|
|
||||||
if( !defined($config->{'linkbutton'})
|
|
||||||
|
if( defined($config->{websocketport}) ) {
|
||||||
|
$hash->{websocketport} = $config->{websocketport};
|
||||||
|
HUEBridge_openWebsocket($hash) if( !defined($hash->{CD}) );
|
||||||
|
|
||||||
|
} elsif( $hash->{has_v2_api} ) {
|
||||||
|
HUEBridge_openEventStream($hash) if( !defined($hash->{helper}{HTTP_CONNECTION}) );
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if( $hash->{modelid}
|
||||||
|
&& !defined($config->{'linkbutton'})
|
||||||
&& !defined($attr{$name}{icon}) ) {
|
&& !defined($attr{$name}{icon}) ) {
|
||||||
$attr{$name}{icon} = 'hue_filled_bridge_v1' if( $hash->{modelid} && $hash->{modelid} eq 'BSB001' );
|
$attr{$name}{icon} = 'hue_filled_bridge_v1' if( $hash->{modelid} eq 'BSB001' );
|
||||||
$attr{$name}{icon} = 'hue_filled_bridge_v2' if( $hash->{modelid} && $hash->{modelid} eq 'BSB002' );
|
$attr{$name}{icon} = 'hue_filled_bridge_v2' if( $hash->{modelid} eq 'BSB002' );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -447,12 +524,14 @@ HUEBridge_OpenDev($)
|
|||||||
|
|
||||||
if( defined($err) && $err ) {
|
if( defined($err) && $err ) {
|
||||||
Log3 $name, 2, "HUEBridge_OpenDev: error reading description: ". $err;
|
Log3 $name, 2, "HUEBridge_OpenDev: error reading description: ". $err;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
Log3 $name, 5, "HUEBridge_OpenDev: got description: $ret";
|
Log3 $name, 5, "HUEBridge_OpenDev: got description: $ret";
|
||||||
$ret =~ m/<modelName>([^<]*)/;
|
$ret =~ m/<modelName>([^<]*)/;
|
||||||
$hash->{modelName} = $1;
|
$hash->{modelName} = $1;
|
||||||
$ret =~ m/<manufacturer>([^<]*)/;
|
$ret =~ m/<manufacturer>([^<]*)/;
|
||||||
$hash->{manufacturer} = $1;
|
$hash->{manufacturer} = $1;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
my $result = HUEBridge_Call($hash, undef, 'config', undef);
|
my $result = HUEBridge_Call($hash, undef, 'config', undef);
|
||||||
@ -468,15 +547,29 @@ HUEBridge_OpenDev($)
|
|||||||
|
|
||||||
HUEBridge_Pair($hash);
|
HUEBridge_Pair($hash);
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
HUEBridge_fillBridgeInfo($hash, $result);
|
||||||
}
|
}
|
||||||
|
|
||||||
$hash->{mac} = $result->{mac};
|
$hash->{mac} = $result->{mac};
|
||||||
#$hash->{bridgeid} = $result->{bridgeid};
|
|
||||||
|
my $params = {
|
||||||
|
url => "https://$hash->{host}/auth/v1",
|
||||||
|
#httpversion => '1.1',
|
||||||
|
method => 'GET',
|
||||||
|
timeout => 5,
|
||||||
|
header => { 'HUE-Application-Key' => $attr{$name}{key}, },
|
||||||
|
type => 'application id',
|
||||||
|
hash => $hash,
|
||||||
|
callback => \&HUEBridge_dispatch,
|
||||||
|
};
|
||||||
|
HttpUtils_NonblockingGet( $params );
|
||||||
|
|
||||||
readingsSingleUpdate($hash, 'state', 'connected', 1 );
|
readingsSingleUpdate($hash, 'state', 'connected', 1 );
|
||||||
HUEBridge_GetUpdate($hash);
|
|
||||||
|
|
||||||
HUEBridge_Autocreate($hash);
|
HUEBridge_Autocreate($hash);
|
||||||
|
HUEBridge_GetUpdate($hash);
|
||||||
|
|
||||||
return undef;
|
return undef;
|
||||||
}
|
}
|
||||||
@ -619,7 +712,13 @@ HUEBridge_Set($@)
|
|||||||
#return "$name: not connected" if( $hash->{STATE} ne 'connected' );
|
#return "$name: not connected" if( $hash->{STATE} ne 'connected' );
|
||||||
|
|
||||||
# usage check
|
# usage check
|
||||||
if($cmd eq 'statusRequest') {
|
if($cmd eq 'reconnect') {
|
||||||
|
|
||||||
|
#HUEBridge_OpenDev( $hash );
|
||||||
|
HUEBridge_openEventStream( $hash );
|
||||||
|
return undef;
|
||||||
|
|
||||||
|
} elsif($cmd eq 'statusRequest') {
|
||||||
return "usage: statusRequest" if( @args != 0 );
|
return "usage: statusRequest" if( @args != 0 );
|
||||||
|
|
||||||
$hash->{LOCAL} = 1;
|
$hash->{LOCAL} = 1;
|
||||||
@ -997,6 +1096,81 @@ HUEBridge_Set($@)
|
|||||||
readingsSingleUpdate($hash, 'state', 'inactive', 1 );
|
readingsSingleUpdate($hash, 'state', 'inactive', 1 );
|
||||||
return undef;
|
return undef;
|
||||||
|
|
||||||
|
} elsif($cmd eq 'v2effect' ) {
|
||||||
|
my $params = {
|
||||||
|
url => "https://$hash->{host}/clip/v2/resource/light/$arg",
|
||||||
|
method => 'PUT',
|
||||||
|
timeout => 5,
|
||||||
|
header => { 'HUE-Application-Key' => $attr{$name}{key}, },
|
||||||
|
type => $cmd,
|
||||||
|
hash => $hash,
|
||||||
|
callback => \&HUEBridge_dispatch,
|
||||||
|
data => '{"on":{"on": true}}',
|
||||||
|
#data => '{"effect":{"effect": "candle"}}',
|
||||||
|
#data => '{"effect":{"effect": "breathe"}}',
|
||||||
|
};
|
||||||
|
|
||||||
|
my($err,$data) = HttpUtils_BlockingGet( $params );
|
||||||
|
|
||||||
|
if( !$data ) {
|
||||||
|
Log3 $name, 2, "$name: empty answer received for $cmd";
|
||||||
|
return undef;
|
||||||
|
} elsif( $data =~ m'HTTP/1.1 200 OK' ) {
|
||||||
|
Log3 $name, 4, "$name: empty answer received for $cmd";
|
||||||
|
return undef;
|
||||||
|
} elsif( $data !~ m/^[\[{].*[\]}]$/ ) {
|
||||||
|
#Log3 $name, 2, "$name: invalid json detected for $cmd: $data";
|
||||||
|
#return undef;
|
||||||
|
}
|
||||||
|
|
||||||
|
Log3 $name, 4, "$name: got: $data";
|
||||||
|
|
||||||
|
my $json = eval { JSON->new->utf8(0)->decode($data) };
|
||||||
|
Log3 $name, 2, "$name: json error: $@ in $data" if( $@ );
|
||||||
|
return undef if( !$json );
|
||||||
|
|
||||||
|
Log3 $name, 1, "$name: error: ". Dumper $json->{errors} if( scalar @{$json->{errors}} );
|
||||||
|
return Dumper $json if( scalar @{$json->{errors}} );
|
||||||
|
|
||||||
|
return;
|
||||||
|
|
||||||
|
} elsif($cmd eq 'v2scene' ) {
|
||||||
|
my $params = {
|
||||||
|
url => "https://$hash->{host}/clip/v2/resource/scene/$arg",
|
||||||
|
method => 'PUT',
|
||||||
|
timeout => 5,
|
||||||
|
header => { 'HUE-Application-Key' => $attr{$name}{key}, },
|
||||||
|
type => $cmd,
|
||||||
|
hash => $hash,
|
||||||
|
callback => \&HUEBridge_dispatch,
|
||||||
|
#data => '{ "recall": { "action": "active" } }',
|
||||||
|
data => '{ "recall": { "action": "dynamic_palette" } }',
|
||||||
|
};
|
||||||
|
|
||||||
|
my($err,$data) = HttpUtils_BlockingGet( $params );
|
||||||
|
|
||||||
|
if( !$data ) {
|
||||||
|
Log3 $name, 2, "$name: empty answer received for $cmd";
|
||||||
|
return undef;
|
||||||
|
} elsif( $data =~ m'HTTP/1.1 200 OK' ) {
|
||||||
|
Log3 $name, 4, "$name: empty answer received for $cmd";
|
||||||
|
return undef;
|
||||||
|
} elsif( $data !~ m/^[\[{].*[\]}]$/ ) {
|
||||||
|
#Log3 $name, 2, "$name: invalid json detected for $cmd: $data";
|
||||||
|
#return undef;
|
||||||
|
}
|
||||||
|
|
||||||
|
Log3 $name, 4, "$name: got: $data";
|
||||||
|
|
||||||
|
my $json = eval { JSON->new->utf8(0)->decode($data) };
|
||||||
|
Log3 $name, 2, "$name: json error: $@ in $data" if( $@ );
|
||||||
|
return undef if( !$json );
|
||||||
|
|
||||||
|
Log3 $name, 1, "$name: error: ". Dumper $json->{errors} if( scalar @{$json->{errors}} );
|
||||||
|
return Dumper $json if( scalar @{$json->{errors}} );
|
||||||
|
|
||||||
|
return;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
my $list = "active inactive delete creategroup deletegroup savescene deletescene modifyscene";
|
my $list = "active inactive delete creategroup deletegroup savescene deletescene modifyscene";
|
||||||
|
|
||||||
@ -1025,10 +1199,28 @@ HUEBridge_Set($@)
|
|||||||
$list .= " swupdate:noArg" if( defined($hash->{updatestate}) && $hash->{updatestate} =~ '^2' );
|
$list .= " swupdate:noArg" if( defined($hash->{updatestate}) && $hash->{updatestate} =~ '^2' );
|
||||||
$list .= " createrule updaterule updateschedule enableschedule disableschedule deleterule createsensor deletesensor configsensor setsensor updatesensor deletewhitelist touchlink:noArg checkforupdate:noArg autodetect:noArg autocreate:noArg statusRequest:noArg";
|
$list .= " createrule updaterule updateschedule enableschedule disableschedule deleterule createsensor deletesensor configsensor setsensor updatesensor deletewhitelist touchlink:noArg checkforupdate:noArg autodetect:noArg autocreate:noArg statusRequest:noArg";
|
||||||
|
|
||||||
|
if( $hash->{has_v2_api} ) {
|
||||||
|
$list .= " v2scene";
|
||||||
|
}
|
||||||
|
|
||||||
return "Unknown argument $cmd, choose one of $list";
|
return "Unknown argument $cmd, choose one of $list";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub
|
||||||
|
HUEBridge_nameOfResource($$)
|
||||||
|
{
|
||||||
|
my ($hash, $id) = @_;
|
||||||
|
my $name = $hash->{NAME};
|
||||||
|
return "$name: v2 api not supported" if( !$hash->{has_v2_api} );
|
||||||
|
|
||||||
|
if( my $resource = $hash->{helper}{resource}{by_id}{$id} ) {
|
||||||
|
return $resource->{metadata}{name};
|
||||||
|
}
|
||||||
|
|
||||||
|
return '<unknown>';
|
||||||
|
}
|
||||||
|
|
||||||
sub
|
sub
|
||||||
HUEBridge_Get($@)
|
HUEBridge_Get($@)
|
||||||
{
|
{
|
||||||
@ -1177,12 +1369,13 @@ HUEBridge_Get($@)
|
|||||||
$fhem_name = $modules{HUEDevice}{defptr}{$code}->{NAME} if( defined($modules{HUEDevice}{defptr}{$code}) );
|
$fhem_name = $modules{HUEDevice}{defptr}{$code}->{NAME} if( defined($modules{HUEDevice}{defptr}{$code}) );
|
||||||
$fhem_name = "" if( !$fhem_name );
|
$fhem_name = "" if( !$fhem_name );
|
||||||
$ret .= sprintf( "%2i: %-15s %-15s %-20s", $key, $result->{$key}{name}, $fhem_name, $result->{$key}{type} );
|
$ret .= sprintf( "%2i: %-15s %-15s %-20s", $key, $result->{$key}{name}, $fhem_name, $result->{$key}{type} );
|
||||||
$ret .= sprintf( " %s", encode_json($result->{$key}{state}) ) if( $arg && $arg eq 'detail' );
|
$ret .= sprintf( "\n%-56s %s", '', encode_json($result->{$key}{state}) ) if( $arg && $arg eq 'detail' );
|
||||||
$ret .= sprintf( "\n%-56s %s", '', encode_json($result->{$key}{config}) ) if( $arg && $arg eq 'detail' );
|
$ret .= sprintf( "\n%-56s %s", '', encode_json($result->{$key}{config}) ) if( $arg && $arg eq 'detail' );
|
||||||
|
$ret .= sprintf( "\n%-56s %s", '', encode_json($result->{$key}{capabilities}) ) if( $arg && $arg eq 'detail' );
|
||||||
$ret .= "\n";
|
$ret .= "\n";
|
||||||
}
|
}
|
||||||
if( $arg && $arg eq 'detail' ) {
|
if( $arg && $arg eq 'detail' ) {
|
||||||
$ret = sprintf( "%2s %-15s %-15s %-20s %s\n", "ID", "NAME", "FHEM", "TYPE", "STATE,CONFIG" ) .$ret if( $ret );
|
$ret = sprintf( "%2s %-15s %-15s %-20s %s\n", "ID", "NAME", "FHEM", "TYPE", "STATE,CONFIG,CAPABILITIES" ) .$ret if( $ret );
|
||||||
} else {
|
} else {
|
||||||
$ret = sprintf( "%2s %-15s %-15s %-20s\n", "ID", "NAME", "FHEM", "TYPE" ) .$ret if( $ret );
|
$ret = sprintf( "%2s %-15s %-15s %-20s\n", "ID", "NAME", "FHEM", "TYPE" ) .$ret if( $ret );
|
||||||
}
|
}
|
||||||
@ -1218,12 +1411,50 @@ HUEBridge_Get($@)
|
|||||||
$ret = sprintf( "%2s %-25s %-15s %s\t%s\n", "ID", "NAME", "FHEM", "MODE", "CONFIGURED" ) .$ret if( $ret );
|
$ret = sprintf( "%2s %-25s %-15s %s\t%s\n", "ID", "NAME", "FHEM", "MODE", "CONFIGURED" ) .$ret if( $ret );
|
||||||
return $ret;
|
return $ret;
|
||||||
|
|
||||||
|
} elsif($cmd eq 'v2resource' ) {
|
||||||
|
if( $arg ) {
|
||||||
|
my $ret;
|
||||||
|
foreach my $entry ( values %{$hash->{helper}{resource}{by_id}} ) {
|
||||||
|
$ret .= Dumper $entry if( $entry->{id_v1} =~ /$arg$/ );
|
||||||
|
}
|
||||||
|
foreach my $entry ( values %{$hash->{helper}{resource}{by_id}} ) {
|
||||||
|
$ret .= Dumper $entry if( $entry->{type} eq $arg );
|
||||||
|
}
|
||||||
|
return $ret if( $ret );
|
||||||
|
|
||||||
|
return Dumper $hash->{helper}{resource}{by_id}{$arg};
|
||||||
|
}
|
||||||
|
return Dumper $hash->{helper}{resource};
|
||||||
|
|
||||||
|
} elsif($cmd eq 'v2resourcetypes' ) {
|
||||||
|
my %result;
|
||||||
|
foreach my $entry ( values %{$hash->{helper}{resource}{by_id}} ) {
|
||||||
|
next if( $arg && $arg ne $entry->{type} );
|
||||||
|
$result{$entry->{id}} = 1 if( $arg );
|
||||||
|
$result{$entry->{type}} = 1 if( !$arg );
|
||||||
|
}
|
||||||
|
return join( "\n", keys %result );
|
||||||
|
|
||||||
|
} elsif($cmd eq 'v2scenes' ) {
|
||||||
|
my $ret;
|
||||||
|
foreach my $entry ( values %{$hash->{helper}{resource}{by_id}} ) {
|
||||||
|
next if( $entry->{type} ne 'scene' );
|
||||||
|
$ret .= sprintf( "%-36s %-25s %-10s", $entry->{id}, $entry->{metadata}{name}, HUEBridge_nameOfResource($hash,$entry->{group}{rid}) );
|
||||||
|
$ret .= "\n";
|
||||||
|
}
|
||||||
|
$ret = sprintf( "%-36s %-25s %-15s %s\t%s\n", "ID", "NAME", "ROOM", "", "" ) .$ret if( $ret );
|
||||||
|
return $ret;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
my $list = "lights:noArg groups:noArg scenes:noArg rule rules:noArg sensors:noArg schedules:noArg whitelist:noArg";
|
my $list = "lights:noArg groups:noArg scenes:noArg rule rules:noArg sensors:noArg schedules:noArg whitelist:noArg";
|
||||||
if( $hash->{helper}{apiversion} && $hash->{helper}{apiversion} >= (1<<16) + (26<<8) ) {
|
if( $hash->{helper}{apiversion} && $hash->{helper}{apiversion} >= (1<<16) + (26<<8) ) {
|
||||||
$list .= " startup:noArg";
|
$list .= " startup:noArg";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if( $hash->{has_v2_api} ) {
|
||||||
|
$list .= " v2resource v2resourcetypes v2scenes";
|
||||||
|
}
|
||||||
|
|
||||||
return "Unknown argument $cmd, choose one of $list";
|
return "Unknown argument $cmd, choose one of $list";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1241,20 +1472,28 @@ HUEBridge_GetUpdate($)
|
|||||||
|
|
||||||
if( $hash->{websocketport} && !$hash->{PORT} ) {
|
if( $hash->{websocketport} && !$hash->{PORT} ) {
|
||||||
HUEBridge_openWebsocket($hash);
|
HUEBridge_openWebsocket($hash);
|
||||||
|
|
||||||
|
} elsif( $hash->{has_v2_api} ) {
|
||||||
|
HUEBridge_openEventStream($hash) if( !defined($hash->{helper}{HTTP_CONNECTION}) );
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
my $type;
|
my $type;
|
||||||
my $result;
|
my $result;
|
||||||
my $poll_devices = AttrVal($name, "pollDevices", 1);
|
my $poll_devices = AttrVal($name, "pollDevices", 2);
|
||||||
if( $poll_devices ) {
|
if( $poll_devices ) {
|
||||||
my ($now) = gettimeofday();
|
my ($now) = gettimeofday();
|
||||||
if( $poll_devices > 1 || $hash->{LOCAL} || $now - $hash->{helper}{last_config_timestamp} > 300 ) {
|
if( $poll_devices > 1 || $hash->{LOCAL} || !$hash->{helper}{last_config_timestamp}
|
||||||
|
|| $now - $hash->{helper}{last_config_timestamp} > 300 ) {
|
||||||
$result = HUEBridge_Call($hash, $hash, undef, undef);
|
$result = HUEBridge_Call($hash, $hash, undef, undef);
|
||||||
$hash->{helper}{last_config_timestamp} = $now;
|
$hash->{helper}{last_config_timestamp} = $now;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
$type = 'lights';
|
$type = 'lights';
|
||||||
$result = HUEBridge_Call($hash, $hash, 'lights', undef);
|
$result = HUEBridge_Call($hash, $hash, 'lights', undef);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
$type = 'config';
|
$type = 'config';
|
||||||
$result = HUEBridge_Call($hash, $hash, 'config', undef);
|
$result = HUEBridge_Call($hash, $hash, 'config', undef);
|
||||||
@ -1313,6 +1552,8 @@ HUEBridge_updateGroups($$)
|
|||||||
foreach my $chash ( values %{$groups} ) {
|
foreach my $chash ( values %{$groups} ) {
|
||||||
my $count = 0;
|
my $count = 0;
|
||||||
my %readings;
|
my %readings;
|
||||||
|
$readings{all_on} = 1;
|
||||||
|
$readings{any_on} = 0;
|
||||||
my ($hue,$sat,$bri);
|
my ($hue,$sat,$bri);
|
||||||
foreach my $light ( split(',', $chash->{lights}) ) {
|
foreach my $light ( split(',', $chash->{lights}) ) {
|
||||||
next if( !$light );
|
next if( !$light );
|
||||||
@ -1371,6 +1612,9 @@ HUEBridge_updateGroups($$)
|
|||||||
|
|
||||||
$readings{on} |= ($current->{on}?'1':'0');
|
$readings{on} |= ($current->{on}?'1':'0');
|
||||||
|
|
||||||
|
$readings{all_on} = 0 if( !($current->{on}?'1':'0') );
|
||||||
|
$readings{any_on} |= ($current->{on}?'1':'0');
|
||||||
|
|
||||||
if( AttrVal($lhash->{NAME}, 'ignoreReachable', 0) ) {
|
if( AttrVal($lhash->{NAME}, 'ignoreReachable', 0) ) {
|
||||||
$readings{reachable} |= 1;
|
$readings{reachable} |= 1;
|
||||||
} else {
|
} else {
|
||||||
@ -1396,6 +1640,8 @@ HUEBridge_updateGroups($$)
|
|||||||
++$count;
|
++$count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$readings{all_on} = 0 if( !$count );
|
||||||
|
|
||||||
if( AttrVal($name, 'ignoreReachable', 0) ) {
|
if( AttrVal($name, 'ignoreReachable', 0) ) {
|
||||||
delete $readings{reachable};
|
delete $readings{reachable};
|
||||||
}
|
}
|
||||||
@ -1459,7 +1705,7 @@ HUEBridge_Parse($$)
|
|||||||
my($hash,$config) = @_;
|
my($hash,$config) = @_;
|
||||||
my $name = $hash->{NAME};
|
my $name = $hash->{NAME};
|
||||||
|
|
||||||
Log3 $name, 4, "parse status message for $name";
|
Log3 $name, 4, "$name: parse status message";
|
||||||
#Log3 $name, 5, Dumper $config;
|
#Log3 $name, 5, Dumper $config;
|
||||||
|
|
||||||
$hash->{helper}{lights} = $config->{lights} if( $config->{lights} );
|
$hash->{helper}{lights} = $config->{lights} if( $config->{lights} );
|
||||||
@ -1470,8 +1716,6 @@ HUEBridge_Parse($$)
|
|||||||
$config = $config->{config} if( defined($config->{config}) );
|
$config = $config->{config} if( defined($config->{config}) );
|
||||||
HUEBridge_fillBridgeInfo($hash, $config);
|
HUEBridge_fillBridgeInfo($hash, $config);
|
||||||
|
|
||||||
$hash->{zigbeechannel} = $config->{zigbeechannel};
|
|
||||||
|
|
||||||
if( my $utc = $config->{UTC} ) {
|
if( my $utc = $config->{UTC} ) {
|
||||||
substr( $utc, 10, 1, '_' );
|
substr( $utc, 10, 1, '_' );
|
||||||
|
|
||||||
@ -1526,11 +1770,10 @@ HUEBridge_Autocreate($;$$)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
my $autocreated = 0;
|
my @ignored = (0, 0, 0);
|
||||||
|
my @created = (0, 0, 0);
|
||||||
my $result = HUEBridge_Call($hash,undef, 'lights', undef);
|
my $result = HUEBridge_Call($hash,undef, 'lights', undef);
|
||||||
foreach my $key ( keys %{$result} ) {
|
foreach my $id ( sort {$a<=>$b} keys %{$result} ) {
|
||||||
my $id= $key;
|
|
||||||
|
|
||||||
my $code = $name ."-". $id;
|
my $code = $name ."-". $id;
|
||||||
if( defined($modules{HUEDevice}{defptr}{$code}) ) {
|
if( defined($modules{HUEDevice}{defptr}{$code}) ) {
|
||||||
Log3 $name, 5, "$name: id '$id' already defined as '$modules{HUEDevice}{defptr}{$code}->{NAME}'";
|
Log3 $name, 5, "$name: id '$id' already defined as '$modules{HUEDevice}{defptr}{$code}->{NAME}'";
|
||||||
@ -1555,14 +1798,18 @@ HUEBridge_Autocreate($;$$)
|
|||||||
HUEDeviceSetIcon($devname);
|
HUEDeviceSetIcon($devname);
|
||||||
$defs{$devname}{helper}{fromAutocreate} = 1 ;
|
$defs{$devname}{helper}{fromAutocreate} = 1 ;
|
||||||
|
|
||||||
$autocreated++;
|
$created[0]++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$result = HUEBridge_Call($hash,undef, 'groups', undef);
|
$result = HUEBridge_Call($hash,undef, 'groups', undef);
|
||||||
$result->{0} = { name => "Lightset 0", };
|
$result->{0} = { name => "Lightset 0", type => 'LightGroup' };
|
||||||
foreach my $key ( keys %{$result} ) {
|
foreach my $id ( sort {$a<=>$b} keys %{$result} ) {
|
||||||
my $id= $key;
|
if( $result->{$id}{type} eq 'Entertainment' ) {
|
||||||
|
Log3 $name, 4, "$name: ignoring group $id ($result->{$id}{name}) of type $result->{$id}{type} in autocreate";
|
||||||
|
$ignored[1]++;
|
||||||
|
next;
|
||||||
|
}
|
||||||
|
|
||||||
my $code = $name ."-G". $id;
|
my $code = $name ."-G". $id;
|
||||||
if( defined($modules{HUEDevice}{defptr}{$code}) ) {
|
if( defined($modules{HUEDevice}{defptr}{$code}) ) {
|
||||||
@ -1588,14 +1835,18 @@ HUEBridge_Autocreate($;$$)
|
|||||||
HUEDeviceSetIcon($devname);
|
HUEDeviceSetIcon($devname);
|
||||||
$defs{$devname}{helper}{fromAutocreate} = 1 ;
|
$defs{$devname}{helper}{fromAutocreate} = 1 ;
|
||||||
|
|
||||||
$autocreated++;
|
$created[1]++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if( $sensors || $hash->{websocket} ) {
|
if( $sensors || $hash->{websocket} || $hash->{has_v2_api} ) {
|
||||||
$result = HUEBridge_Call($hash,undef, 'sensors', undef);
|
$result = HUEBridge_Call($hash,undef, 'sensors', undef);
|
||||||
foreach my $key ( keys %{$result} ) {
|
foreach my $id ( sort {$a<=>$b} keys %{$result} ) {
|
||||||
my $id= $key;
|
if( $result->{$id}{type} eq 'CLIPGenericStatus' ) {
|
||||||
|
Log3 $name, 4, "$name: ignoring sensor $id ($result->{$id}{name}) of type $result->{$id}{type} in autocreate";
|
||||||
|
$ignored[2]++;
|
||||||
|
next;
|
||||||
|
}
|
||||||
|
|
||||||
my $code = $name ."-S". $id;
|
my $code = $name ."-S". $id;
|
||||||
if( defined($modules{HUEDevice}{defptr}{$code}) ) {
|
if( defined($modules{HUEDevice}{defptr}{$code}) ) {
|
||||||
@ -1621,17 +1872,21 @@ HUEBridge_Autocreate($;$$)
|
|||||||
HUEDeviceSetIcon($devname);
|
HUEDeviceSetIcon($devname);
|
||||||
$defs{$devname}{helper}{fromAutocreate} = 1 ;
|
$defs{$devname}{helper}{fromAutocreate} = 1 ;
|
||||||
|
|
||||||
$autocreated++;
|
$created[2]++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if( $autocreated ) {
|
sub sum { my $sum = 0; $sum += $_ for @_; return $sum }
|
||||||
Log3 $name, 2, "$name: autocreated $autocreated devices";
|
|
||||||
|
my $created = join( '/', @created );
|
||||||
|
my $ignored = join( '/', @ignored );
|
||||||
|
if( !$force || sum(@created) || sum(@ignored) ) {
|
||||||
|
Log3 $name, 2, "$name: autocreate: created $created devices (ignored $ignored)";
|
||||||
CommandSave(undef,undef) if( AttrVal( "autocreate", "autosave", 1 ) );
|
CommandSave(undef,undef) if( AttrVal( "autocreate", "autosave", 1 ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
return "created $autocreated devices";
|
return "created $created devices (ignored $ignored)";
|
||||||
}
|
}
|
||||||
|
|
||||||
sub
|
sub
|
||||||
@ -1879,6 +2134,7 @@ HUEBridge_HTTP_Call2($$$$;$)
|
|||||||
return HUEBridge_ProcessResponse($hash, $json);
|
return HUEBridge_ProcessResponse($hash, $json);
|
||||||
|
|
||||||
HUEBridge_dispatch( {hash=>$hash,chash=>$chash,type=>$path},$err,$data );
|
HUEBridge_dispatch( {hash=>$hash,chash=>$chash,type=>$path},$err,$data );
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
Log3 $name, 4, "using HttpUtils_NonblockingGet: $method ". ($path?$path:'');
|
Log3 $name, 4, "using HttpUtils_NonblockingGet: $method ". ($path?$path:'');
|
||||||
|
|
||||||
@ -1899,6 +2155,33 @@ HUEBridge_HTTP_Call2($$$$;$)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub
|
||||||
|
HUEBridge_getv2resources($;$)
|
||||||
|
{
|
||||||
|
my ($hash,$blocking) = @_;
|
||||||
|
my $name = $hash->{NAME};
|
||||||
|
|
||||||
|
my $params = {
|
||||||
|
url => "https://$hash->{host}/clip/v2/resource",
|
||||||
|
method => 'GET',
|
||||||
|
timeout => 5,
|
||||||
|
header => { 'HUE-Application-Key' => $attr{$name}{key}, },
|
||||||
|
type => 'resource',
|
||||||
|
hash => $hash,
|
||||||
|
callback => \&HUEBridge_dispatch,
|
||||||
|
};
|
||||||
|
|
||||||
|
if( $blocking ) {
|
||||||
|
my($err,$data) = HttpUtils_BlockingGet( $params );
|
||||||
|
|
||||||
|
HUEBridge_dispatch($params, $err, $data );
|
||||||
|
|
||||||
|
} else {
|
||||||
|
HttpUtils_NonblockingGet( $params );
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
sub
|
sub
|
||||||
HUEBridge_dispatch($$$;$)
|
HUEBridge_dispatch($$$;$)
|
||||||
{
|
{
|
||||||
@ -1906,10 +2189,246 @@ HUEBridge_dispatch($$$;$)
|
|||||||
my $hash = $param->{hash};
|
my $hash = $param->{hash};
|
||||||
my $name = $hash->{NAME};
|
my $name = $hash->{NAME};
|
||||||
|
|
||||||
#Log3 $name, 5, "HUEBridge_dispatch";
|
Log3 $name, 4, "$name: dispatch";
|
||||||
|
Log3 $name, 5, "HUEBridge_dispatch". ($param->{type}?": $param->{type}":"");
|
||||||
|
|
||||||
|
my $type = $param->{type};
|
||||||
|
|
||||||
if( $err ) {
|
if( $err ) {
|
||||||
Log3 $name, 2, "$name: http request failed: $err";
|
Log3 $name, 2, "$name: http request failed: $err";
|
||||||
|
|
||||||
|
if( $type eq 'event' ) {
|
||||||
|
if( defined($hash->{helper}{HTTP_CONNECTION}) && defined($hash->{helper}{HTTP_CONNECTION}{lastID}) ) {
|
||||||
|
$hash->{EventStream} = 'terminated';
|
||||||
|
Log3 $name, 2, "name: EventStream: $hash->{EventStream}";
|
||||||
|
HUEBridge_openEventStream( $hash );
|
||||||
|
|
||||||
|
} else {
|
||||||
|
$hash->{EventStream} = 'terminated; retrying later';
|
||||||
|
Log3 $name, 2, "name: EventStream: $hash->{EventStream}";
|
||||||
|
|
||||||
|
RemoveInternalTimer($hash, "HUEBridge_openEventStream" );
|
||||||
|
InternalTimer(gettimeofday()+2, "HUEBridge_openEventStream", $hash, 0);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return undef;
|
||||||
|
|
||||||
|
} elsif( defined($type) && $type eq 'resource' ) {
|
||||||
|
$json = eval { JSON->new->utf8(0)->decode($data) };
|
||||||
|
Log3 $name, 2, "$name: json error: $@ in $data" if( $@ );
|
||||||
|
|
||||||
|
delete $hash->{helper}{resource};
|
||||||
|
return undef if( !$json );
|
||||||
|
|
||||||
|
Log3 $name, 1, "$name: error: ". Dumper $json->{errors} if( scalar @{$json->{errors}} );
|
||||||
|
|
||||||
|
$hash->{helper}{resource} = $json;
|
||||||
|
Log3 $name, 5, "$name: received: ". Dumper $json;
|
||||||
|
|
||||||
|
foreach my $item (@{$hash->{helper}{resource}{data}}) {
|
||||||
|
$hash->{helper}{resource}{by_id}{$item->{id}} = $item;
|
||||||
|
}
|
||||||
|
return undef;
|
||||||
|
|
||||||
|
} elsif( defined($type) && $type eq 'event' ) {
|
||||||
|
$hash->{EventStream} = 'connected';
|
||||||
|
Log3 $name, 4, "name: EventStream: $hash->{EventStream}";
|
||||||
|
|
||||||
|
if( $hash->{INTERVAL} && $hash->{INTERVAL} < 60 ) {
|
||||||
|
$hash->{INTERVAL} = 60;
|
||||||
|
Log3 $name, 2, "$name: EventStream connected, changing interval to $hash->{INTERVAL}";
|
||||||
|
}
|
||||||
|
|
||||||
|
CommandDeleteAttr( undef, "$name pollDevices" ) if defined( AttrVal($name, 'pollDevices', undef) );
|
||||||
|
|
||||||
|
#Log3 $name, 5, "$name: EventStream: got: $data";
|
||||||
|
|
||||||
|
while($data =~ m/([^:]*):\s*(.+)(\r?\n)?(.*)/) {
|
||||||
|
my $key = $1;
|
||||||
|
my $value = $2;
|
||||||
|
$data = $4;
|
||||||
|
|
||||||
|
if( !$key ) {
|
||||||
|
Log3 $name, 5, "$name: ignoring: $value";
|
||||||
|
|
||||||
|
HUEBridge_getv2resources($hash);
|
||||||
|
next;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( $key eq 'id' ) {
|
||||||
|
Log3 $name, 5, "$name: EventStream: got id: $value";
|
||||||
|
$hash->{helper}{HTTP_CONNECTION}{lastID} = $value;
|
||||||
|
|
||||||
|
} elsif( $key eq 'data' ) {
|
||||||
|
Log3 $name, 5, "$name: EventStream: got data: $value";
|
||||||
|
|
||||||
|
if( $value && $value !~ m/^[\[{].*[\]}]$/ ) {
|
||||||
|
Log3 $name, 2, "$name: invalid json detected: $value";
|
||||||
|
return undef;
|
||||||
|
}
|
||||||
|
|
||||||
|
$json = eval { JSON->new->utf8(0)->decode($value) };
|
||||||
|
Log3 $name, 2, "$name: json error: $@ in $value" if( $@ );
|
||||||
|
|
||||||
|
return undef if( !$json );
|
||||||
|
Log3 $name, 4, "$name: received: ". Dumper $json;
|
||||||
|
|
||||||
|
for my $event ( @{$json} ) {
|
||||||
|
my $changed = "";
|
||||||
|
|
||||||
|
if( $event->{type} eq 'update' ) {
|
||||||
|
Log3 $name, 4, "$name: got $event->{type} event";
|
||||||
|
|
||||||
|
for my $data ( @{$event->{data}} ) {
|
||||||
|
my(undef, $t, $id) = split( '/', $data->{id_v1} );
|
||||||
|
if( !defined($t) || !defined($id) ) {
|
||||||
|
Log3 $name, 3, "$name: ignoring event type $data->{type}";
|
||||||
|
next;
|
||||||
|
}
|
||||||
|
|
||||||
|
my $code;
|
||||||
|
$code = $name ."-". $id if( $t eq 'lights' );
|
||||||
|
$code = $name ."-S". $id if( $t eq 'sensors' );
|
||||||
|
$code = $name ."-G". $id if( $t eq 'groups' );
|
||||||
|
if( !$code ) {
|
||||||
|
Log3 $name, 3, "$name: ignoring event for $t";
|
||||||
|
next;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( my $chash = $modules{HUEDevice}{defptr}{$code} ) {
|
||||||
|
my $creationtime = substr($event->{creationtime},0,19);
|
||||||
|
#substr( $creationtime, 10, 1, '_' );
|
||||||
|
#$creationtime = FmtDateTime( SVG_time_to_sec($creationtime) + $hash->{helper}{offsetUTC} ) if( defined($hash->{helper}{offsetUTC}) );
|
||||||
|
#substr( $creationtime, 10, 1, 'T' );
|
||||||
|
my $obj = { state => { lastupdated => $creationtime },
|
||||||
|
v2_id => $data->{owner}{rid},
|
||||||
|
v2_service => $data->{id} };
|
||||||
|
$obj->{v2_id} = $obj->{v2_service} if( $t eq 'groups' );
|
||||||
|
|
||||||
|
my $device = $hash->{helper}{resource}{by_id}{$obj->{v2_id}};
|
||||||
|
if( !$device ) {
|
||||||
|
Log3 $name, 2, "$name: event for unknown device received, tying to refresh resouces";
|
||||||
|
HUEBridge_getv2resources($hash, 1);
|
||||||
|
HUEBridge_Autocreate($hash);
|
||||||
|
$device = $hash->{helper}{resource}{by_id}{$obj->{v2_id}};
|
||||||
|
}
|
||||||
|
my $service = $hash->{helper}{resource}{by_id}{$obj->{v2_service}};
|
||||||
|
if( !$service ) {
|
||||||
|
Log3 $name, 2, "$name: event for unknown service received, tying to refresh resouces";
|
||||||
|
HUEBridge_getv2resources($hash, 1);
|
||||||
|
$service = $hash->{helper}{resource}{by_id}{$obj->{v2_service}};
|
||||||
|
}
|
||||||
|
#Log 1, Dumper $device;
|
||||||
|
#Log 1, Dumper $service;
|
||||||
|
|
||||||
|
if( $data->{type} eq 'motion' ) {
|
||||||
|
$obj->{state}{presence} = $data->{motion}{motion} if( defined($data->{motion}) );
|
||||||
|
|
||||||
|
} elsif( $data->{type} eq 'button' ) {
|
||||||
|
my $input = $service->{metadata}{control_id};
|
||||||
|
my $eventtype = $data->{button}{last_event};
|
||||||
|
#Log 1, "input: $input";
|
||||||
|
#Log 1, "eventtype: $eventtype";
|
||||||
|
|
||||||
|
my $buttonevent;
|
||||||
|
if( $input
|
||||||
|
&& defined($chash->{helper}{events})
|
||||||
|
&& defined($chash->{helper}{events}[$input-1])
|
||||||
|
&& defined($chash->{helper}{events}[$input-1]{$eventtype}) ) {
|
||||||
|
$buttonevent = $chash->{helper}{events}[$input-1]{$eventtype};
|
||||||
|
|
||||||
|
} elsif( $eventtype eq 'initial_press' ) {
|
||||||
|
$buttonevent = "${input}000";
|
||||||
|
|
||||||
|
} elsif( $eventtype eq 'repeat' ) {
|
||||||
|
$buttonevent = "${input}001";
|
||||||
|
|
||||||
|
} elsif( $eventtype eq 'short_release' ) {
|
||||||
|
$buttonevent = "${input}002";
|
||||||
|
|
||||||
|
} elsif( $eventtype eq 'long_release' ) {
|
||||||
|
$buttonevent = "${input}003";
|
||||||
|
}
|
||||||
|
|
||||||
|
#$obj->{state}{input} = $input;
|
||||||
|
#$obj->{state}{eventtype} = $eventtype;
|
||||||
|
$obj->{state}{buttonevent} = $buttonevent;
|
||||||
|
|
||||||
|
} elsif( $data->{type} eq 'temperature' ) {
|
||||||
|
$obj->{state}{temperature} = int($data->{temperature}{temperature}*100) if( defined($data->{temperature})
|
||||||
|
&& $data->{temperature}{temperature_valid} );
|
||||||
|
|
||||||
|
} elsif( $data->{type} eq 'light_level' ) {
|
||||||
|
$obj->{state}{lightlevel} = $data->{light}{light_level} if( defined($data->{light})
|
||||||
|
&& $data->{light}{light_level_valid} );
|
||||||
|
} elsif( $data->{type} eq 'zigbee_connectivity' ) {
|
||||||
|
$obj->{state}{reachable} = ($data->{status} eq 'connected') ? 1 : 0;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
$obj->{state}{on} = $data->{on}{on} if( defined($data->{on}) );
|
||||||
|
|
||||||
|
$obj->{state}{bri} = int($data->{dimming}{brightness} * 254 / 100) if( defined($data->{dimming}) );
|
||||||
|
|
||||||
|
if( defined($data->{color}) ) {
|
||||||
|
if( my $xy = $data->{color}{xy} ) {
|
||||||
|
$obj->{state}{colormode} = 'xy';
|
||||||
|
$obj->{state}{xy} = [$xy->{x}, $xy->{y}];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if( defined($data->{color_temperature}) && defined($data->{color_temperature}{mirek}) ) {
|
||||||
|
$obj->{state}{colormode} = 'ct';
|
||||||
|
$obj->{state}{ct} = $data->{color_temperature}{mirek};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if( defined($data->{dynamics}) ) {
|
||||||
|
$obj->{state}{dynamics_speed} = $data->{dynamics}{speed} if( $data->{dynamics}{speed_valid} );
|
||||||
|
$obj->{state}{dynamics_status} = $data->{dynamics}{status};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Log3 $name, 4, "$name: created from event: ". Dumper $obj;
|
||||||
|
|
||||||
|
if( HUEDevice_Parse($chash, $obj) && !$chash->{helper}{devtype} ) {
|
||||||
|
$changed .= "," if( $changed );
|
||||||
|
$changed .= $chash->{ID};
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
Log3 $name, 3, "$name: message for unknown device received: $code";
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} elsif( $event->{type} eq 'add' ) {
|
||||||
|
Log3 $name, 4, "$name: got $event->{type} event";
|
||||||
|
|
||||||
|
HUEBridge_getv2resources($hash, 1);
|
||||||
|
HUEBridge_Autocreate($hash);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
Log3 $name, 3, "$name: unknown event type $event->{type}: $data";
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
HUEBridge_updateGroups($hash, $changed) if( $changed ); # not needed ?
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
Log3 $name, 4, "$name: EventStream: unknown event: $key: $value";
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return undef;
|
||||||
|
|
||||||
|
} elsif( defined($type) && $type eq 'application id' ) {
|
||||||
|
if( $param->{httpheader} =~ m/hue-application-id:\s?([^\s;]*)/i ) {
|
||||||
|
$hash->{'application id'} = $1;
|
||||||
|
}
|
||||||
|
|
||||||
} elsif( $data || $json ) {
|
} elsif( $data || $json ) {
|
||||||
if( !$data && !$json ) {
|
if( !$data && !$json ) {
|
||||||
Log3 $name, 2, "$name: empty answer received";
|
Log3 $name, 2, "$name: empty answer received";
|
||||||
@ -1922,13 +2441,11 @@ HUEBridge_dispatch($$$;$)
|
|||||||
my $queryAfterSet = AttrVal( $name,'queryAfterSet', 1 );
|
my $queryAfterSet = AttrVal( $name,'queryAfterSet', 1 );
|
||||||
|
|
||||||
if( !$json ) {
|
if( !$json ) {
|
||||||
$json = eval { JSON->new->utf8(0)->decode($data) } if( !$json );
|
$json = eval { JSON->new->utf8(0)->decode($data) };
|
||||||
Log3 $name, 2, "$name: json error: $@ in $data" if( $@ );
|
Log3 $name, 2, "$name: json error: $@ in $data" if( $@ );
|
||||||
}
|
}
|
||||||
return undef if( !$json );
|
return undef if( !$json );
|
||||||
|
|
||||||
my $type = $param->{type};
|
|
||||||
|
|
||||||
if( ref($json) eq 'ARRAY' ) {
|
if( ref($json) eq 'ARRAY' ) {
|
||||||
HUEBridge_ProcessResponse($hash,$json) if( !$queryAfterSet );
|
HUEBridge_ProcessResponse($hash,$json) if( !$queryAfterSet );
|
||||||
|
|
||||||
@ -2195,6 +2712,8 @@ HUEBridge_Attr($$$)
|
|||||||
|
|
||||||
1;
|
1;
|
||||||
|
|
||||||
|
__END__
|
||||||
|
|
||||||
=pod
|
=pod
|
||||||
=item tag cloudfree
|
=item tag cloudfree
|
||||||
=item tag publicAPI
|
=item tag publicAPI
|
||||||
@ -2342,9 +2861,9 @@ HUEBridge_Attr($$$)
|
|||||||
1 -> use HttpUtils_NonblockingGet<br>
|
1 -> use HttpUtils_NonblockingGet<br>
|
||||||
not set -> use old module specific implementation</li>
|
not set -> use old module specific implementation</li>
|
||||||
<li>pollDevices<br>
|
<li>pollDevices<br>
|
||||||
1 -> the bridge will poll all lights in one go instead of each device polling itself independently<br>
|
1 -> the bridge will poll all lights in one go instead of each light polling itself independently<br>
|
||||||
2 -> the bridge will poll all devices in one go instead of each device polling itself independently<br>
|
2 -> the bridge will poll all devices in one go instead of each device polling itself independently<br>
|
||||||
default is 1.</li>
|
default is 2. will be deleted if v2 api is detected and eventstream connects.</li>
|
||||||
<li>createGroupReadings<br>
|
<li>createGroupReadings<br>
|
||||||
create 'artificial' readings for group devices.</li>
|
create 'artificial' readings for group devices.</li>
|
||||||
0 -> create readings only for group devices where createGroupReadings ist set to 1
|
0 -> create readings only for group devices where createGroupReadings ist set to 1
|
||||||
|
@ -492,8 +492,10 @@ HUEDevice_Define($$) {
|
|||||||
RemoveInternalTimer($hash);
|
RemoveInternalTimer($hash);
|
||||||
if( $init_done ) {
|
if( $init_done ) {
|
||||||
HUEDevice_GetUpdate($hash);
|
HUEDevice_GetUpdate($hash);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
InternalTimer(gettimeofday()+10, "HUEDevice_GetUpdate", $hash, 0);
|
InternalTimer(gettimeofday()+10, "HUEDevice_GetUpdate", $hash, 0);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return undef;
|
return undef;
|
||||||
@ -1457,6 +1459,27 @@ HUEDevice_Parse($$)
|
|||||||
my($hash,$result) = @_;
|
my($hash,$result) = @_;
|
||||||
my $name = $hash->{NAME};
|
my $name = $hash->{NAME};
|
||||||
|
|
||||||
|
if( !defined($hash->{has_v2_api}) && defined($hash->{IODev}) ) {
|
||||||
|
$hash->{has_v2_api} = $hash->{IODev}{has_v2_api} if( defined($hash->{IODev}{has_v2_api}) );
|
||||||
|
|
||||||
|
Log3 $name, 4, "$name: bridge has v2 api: $hash->{has_v2_api}";
|
||||||
|
|
||||||
|
if( $hash->{INTERVAL} && $hash->{has_v2_api} ) {
|
||||||
|
if( defined($hash->{IODev}{EventStream}) && $hash->{IODev}{EventStream} eq 'connected' ) {
|
||||||
|
delete $hash->{INTERVAL};
|
||||||
|
Log3 $name, 2, "$name: bridge has v2 api, EventStream connected, removing interval";
|
||||||
|
|
||||||
|
RemoveInternalTimer($hash);
|
||||||
|
InternalTimer(gettimeofday()+$hash->{INTERVAL}, "HUEDevice_GetUpdate", $hash, 0) if( $hash->{INTERVAL} );
|
||||||
|
|
||||||
|
} else {
|
||||||
|
delete $hash->{has_v2_api};
|
||||||
|
Log3 $name, 2, "$name: bridge has v2 api, EventStream not jet connected";
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if( ref($result) ne "HASH" ) {
|
if( ref($result) ne "HASH" ) {
|
||||||
if( ref($result) && $HUEDevice_hasDataDumper) {
|
if( ref($result) && $HUEDevice_hasDataDumper) {
|
||||||
#Log3 $name, 2, "$name: got wrong status message for $name: ". Dumper $result;
|
#Log3 $name, 2, "$name: got wrong status message for $name: ". Dumper $result;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user