30_HUEBridge.pm, 31_HUEDevice.pm: better timestamp handling for polling and events, better handling of ignored resource types in events

git-svn-id: https://svn.fhem.de/fhem/trunk@25534 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
justme-1968 2022-01-21 14:34:53 +00:00
parent 01408639ad
commit 51a00eca30
2 changed files with 87 additions and 72 deletions

View File

@ -735,6 +735,12 @@ HUEBridge_Set($@)
#HUEBridge_openEventStream( $hash );
return undef;
} elsif($cmd eq 'close') {
HUEBridge_closeWebsocket($hash);
HUEBridge_closeEventStream($hash);
return undef;
} elsif($cmd eq 'statusRequest') {
return "usage: statusRequest" if( @args != 0 );
@ -1287,6 +1293,7 @@ HUEBridge_Get($@)
$result->{$key}{class} = '' if( !defined($result->{$key}{class}) ); #deCONZ fix
$result->{$key}{lights} = [] if( !defined($result->{$key}{lights}) ); #deCONZ fix
$ret .= sprintf( "%2i: %-15s %-15s %-15s %-15s", $key, $result->{$key}{name}, $fhem_name, $result->{$key}{type}, $result->{$key}{class} );
$ret .= ' (ignored)' if( $hash->{helper}{ignored}{$code} );
if( !$arg && $hash->{helper}{lights} ) {
$ret .= sprintf( " %s\n", join( ",", map { my $l = $hash->{helper}{lights}{$_}{name}; $l?$l:$_;} @{$result->{$key}{lights}} ) );
} else {
@ -1385,16 +1392,17 @@ HUEBridge_Get($@)
my $fhem_name = '';
$fhem_name = $modules{HUEDevice}{defptr}{$code}->{NAME} if( defined($modules{HUEDevice}{defptr}{$code}) );
$fhem_name = "" if( !$fhem_name );
$ret .= sprintf( "%2i: %-15s %-15s %-20s", $key, $result->{$key}{name}, $fhem_name, $result->{$key}{type} );
$ret .= sprintf( "%2i: %-20s %-15s %-20s", $key, $result->{$key}{name}, $fhem_name, $result->{$key}{type} );
$ret .= ' (ignored)' if( $hash->{helper}{ignored}{$code} );
$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}{capabilities}) ) if( $arg && $arg eq 'detail' );
$ret .= "\n";
}
if( $arg && $arg eq 'detail' ) {
$ret = sprintf( "%2s %-15s %-15s %-20s %s\n", "ID", "NAME", "FHEM", "TYPE", "STATE,CONFIG,CAPABILITIES" ) .$ret if( $ret );
$ret = sprintf( "%2s %-20s %-15s %-20s %s\n", "ID", "NAME", "FHEM", "TYPE", "STATE,CONFIG,CAPABILITIES" ) .$ret if( $ret );
} else {
$ret = sprintf( "%2s %-15s %-15s %-20s\n", "ID", "NAME", "FHEM", "TYPE" ) .$ret if( $ret );
$ret = sprintf( "%2s %-20s %-15s %-20s\n", "ID", "NAME", "FHEM", "TYPE" ) .$ret if( $ret );
}
return $ret;
@ -1428,6 +1436,9 @@ HUEBridge_Get($@)
$ret = sprintf( "%2s %-25s %-15s %s\t%s\n", "ID", "NAME", "FHEM", "MODE", "CONFIGURED" ) .$ret if( $ret );
return $ret;
} elsif($cmd eq 'ignored' ) {
return join( "\n", keys %{$hash->{helper}{ignored}} );
} elsif($cmd eq 'v2resource' ) {
if( $arg ) {
my $ret;
@ -1766,11 +1777,13 @@ HUEBridge_Parse($$)
$hash->{updatestate} .= " [$devicetypes]" if( $devicetypes );
}
} elsif ( defined( $hash->{swupdate} ) ) {
delete( $hash->{updatestate} );
delete( $hash->{helper}{updatestate} );
}
#update state timestamp
readingsSingleUpdate($hash, 'state', $hash->{READINGS}{state}{VAL}, 0);
}
@ -1823,6 +1836,11 @@ HUEBridge_Autocreate($;$$)
$result->{0} = { name => "Lightset 0", type => 'LightGroup' };
foreach my $id ( sort {$a<=>$b} keys %{$result} ) {
my $code = $name ."-G". $id;
if( defined($modules{HUEDevice}{defptr}{$code}) ) {
Log3 $name, 5, "$name: id '$id' already defined as '$modules{HUEDevice}{defptr}{$code}->{NAME}'";
next;
}
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]++;
@ -1830,11 +1848,6 @@ HUEBridge_Autocreate($;$$)
next;
}
if( defined($modules{HUEDevice}{defptr}{$code}) ) {
Log3 $name, 5, "$name: id '$id' already defined as '$modules{HUEDevice}{defptr}{$code}->{NAME}'";
next;
}
my $devname= "HUEGroup" . $id;
$devname = $name ."_". $devname if( $hash->{helper}{count} );
my $define= "$devname HUEDevice group $id IODev=$name";
@ -1861,6 +1874,11 @@ HUEBridge_Autocreate($;$$)
$result = HUEBridge_Call($hash,undef, 'sensors', undef);
foreach my $id ( sort {$a<=>$b} keys %{$result} ) {
my $code = $name ."-S". $id;
if( defined($modules{HUEDevice}{defptr}{$code}) ) {
Log3 $name, 5, "$name: id '$id' already defined as '$modules{HUEDevice}{defptr}{$code}->{NAME}'";
next;
}
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]++;
@ -1868,11 +1886,6 @@ HUEBridge_Autocreate($;$$)
next;
}
if( defined($modules{HUEDevice}{defptr}{$code}) ) {
Log3 $name, 5, "$name: id '$id' already defined as '$modules{HUEDevice}{defptr}{$code}->{NAME}'";
next;
}
my $devname= "HUESensor" . $id;
$devname = $name ."_". $devname if( $hash->{helper}{count} );
my $define= "$devname HUEDevice sensor $id IODev=$name";
@ -2208,7 +2221,7 @@ HUEBridge_dispatch($$$;$)
my $hash = $param->{hash};
my $name = $hash->{NAME};
Log3 $name, 4, "$name: dispatch";
Log3 $name, 4, "$name: dispatch". ($param->{url}?": $param->{url}":"");
Log3 $name, 5, "HUEBridge_dispatch". ($param->{type}?": $param->{type}":"");
my $type = $param->{type};
@ -2216,7 +2229,7 @@ HUEBridge_dispatch($$$;$)
if( $err ) {
Log3 $name, 2, "$name: http request failed: $err";
if( $type eq 'event' ) {
if( $type && $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}";
@ -2298,7 +2311,7 @@ HUEBridge_dispatch($$$;$)
Log3 $name, 2, "$name: EventStream: json error: $@ in $value" if( $@ );
return undef if( !$json );
Log3 $name, 4, "$name: EventStream: received: ". Dumper $json;
Log3 $name, 5, "$name: EventStream: received: ". Dumper $json;
my $changed = "";
for my $event ( @{$json} ) {
@ -2306,7 +2319,7 @@ HUEBridge_dispatch($$$;$)
Log3 $name, 4, "$name: EventStream: got $event->{type} event";
for my $data ( @{$event->{data}} ) {
Log3 $name, 4, "$name: with part for resource type $data->{type}";
Log3 $name, 4, "$name: event part for resource type $data->{type}";
my(undef, $t, $id) = split( '/', $data->{id_v1} );
if( !defined($t) || !defined($id) ) {
@ -2319,7 +2332,8 @@ HUEBridge_dispatch($$$;$)
$code = $name ."-S". $id if( $t eq 'sensors' );
$code = $name ."-G". $id if( $t eq 'groups' );
if( !$code ) {
Log3 $name, 3, "$name: EventStream: ignoring event for $t";
# handle events for scenes ?
Log3 $name, 4, "$name: EventStream: ignoring event for $t";
next;
}
@ -2400,8 +2414,25 @@ HUEBridge_dispatch($$$;$)
}
} elsif( $data->{type} eq 'entertainment_configuration' ) {
Log3 $name, 4, "$name: ignoring resource type $data->{type}";
$handled = 0;
} elsif( $data->{type} eq 'bridge_home' ) {
HUEBridge_getv2resources($hash, 1);
Log3 $name, 4, "$name: ignoring resource type $data->{type}";
$handled = 0;
} elsif( $data->{type} eq 'room' ) {
Log3 $name, 4, "$name: ignoring resource type $data->{type}";
$handled = 0;
} elsif( $data->{type} eq 'zone' ) {
Log3 $name, 4, "$name: ignoring resource type $data->{type}";
$handled = 0;
} elsif( $data->{type} eq 'grouped_light' ) {
Log3 $name, 4, "$name: ignoring resource type $data->{type}";
$handled = 0;
} elsif( $data->{type} eq 'light'
|| $data->{type} eq 'grouped_light' ) {
@ -2433,7 +2464,7 @@ HUEBridge_dispatch($$$;$)
}
if( $handled ) {
Log3 $name, 4, "$name: created from event: ". Dumper $obj;
Log3 $name, 5, "$name: created from event: ". Dumper $obj;
if( HUEDevice_Parse($chash, $obj) && !$chash->{helper}{devtype} ) {
$changed .= "," if( $changed );

View File

@ -81,8 +81,16 @@ my %hueModels = (
icon => 'hue_filled_white_and_color_e27_b22', },
LWB014 => {name => 'Hue Lux' ,type => 'Dimmable light' ,subType => 'dimmer',
icon => 'hue_filled_white_and_color_e27_b22', },
LWO003 => {name => 'Hue White Filament Bulb G125' ,type => 'Dimmable light' ,subType => 'dimmer',
icon => 'hue_filled_filament', },
LWV001 => {name => 'Hue White Filament Bulb' ,type => 'Dimmable light' ,subType => 'dimmer',
icon => 'hue_filled_filament', },
LTO001 => {name => 'Hue Filament Bulb G93' ,type => 'Color temperature light' ,subType => 'ctdimmer',
icon => 'hue_filled_filament', },
LTO002 => {name => 'Hue Filament Bulb G125' ,type => 'Color temperature light' ,subType => 'ctdimmer',
icon => 'hue_filled_filament', },
LTO004 => {name => 'Hue Filament Bulb G25' ,type => 'Color temperature light' ,subType => 'ctdimmer',
icon => 'hue_filled_filament', },
LTW001 => {name => 'Hue A19 White Ambience' ,type => 'Color temperature light' ,subType => 'ctdimmer',
icon => 'hue_filled_white_and_color_e27_b22', },
LTW004 => {name => 'Hue A19 White Ambience' ,type => 'Color temperature light' ,subType => 'ctdimmer', },
@ -494,14 +502,15 @@ HUEDevice_Define($$) {
HUEDevice_GetUpdate($hash);
} else {
InternalTimer(gettimeofday()+10, "HUEDevice_GetUpdate", $hash, 0);
InternalTimer(gettimeofday()+10, "HUEDevice_GetUpdate", $hash, 0) if( $hash->{INTERVAL} );
}
return undef;
}
sub HUEDevice_Undefine($$)
sub
HUEDevice_Undefine($$)
{
my ($hash,$arg) = @_;
@ -1461,8 +1470,8 @@ HUEDevice_Parse($$)
if( ref($result) ne "HASH" ) {
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: $result";
Log3 $name, 2, "$name: got wrong status message for $name: ". Dumper $result;
#Log3 $name, 2, "$name: got wrong status message for $name: $result";
} else {
Log3 $name, 2, "$name: got wrong status message for $name: $result";
}
@ -1670,6 +1679,7 @@ HUEDevice_Parse($$)
$hash->{power} = $result->{power} if( defined($result->{power}) );
if( $hash->{helper}->{devtype} eq 'S' ) {
my %readings;
@ -1689,6 +1699,7 @@ HUEDevice_Parse($$)
$hash->{tholddark} = $config->{tholddark} if( defined($config->{tholddark}) );
$hash->{sensitivity} = $config->{sensitivity} if( defined($config->{sensitivity}) );
$readings{battery} = $config->{battery} if( defined($config->{battery}) );
$readings{batteryPercent} = $config->{battery} if( defined($config->{battery}) );
@ -1705,8 +1716,8 @@ HUEDevice_Parse($$)
$readings{mode} = $config->{mode} if( defined ($config->{mode}) );
}
my $lastupdated = '';
my $lastupdated_local = '';
my $lastupdated;
my $ts;
my $offset = 0;
if( my $state = $result->{state} ) {
$lastupdated = $state->{lastupdated};
@ -1714,30 +1725,25 @@ HUEDevice_Parse($$)
return undef if( !$lastupdated );
return undef if( $lastupdated eq 'none' );
substr( $lastupdated, 10, 1, ' ' ) if($lastupdated);
$ts = time_str2num($lastupdated);
if( my $iohash = $hash->{IODev} ) {
substr( $lastupdated, 10, 1, '_' );
my $sec = SVG_time_to_sec($lastupdated);
$lastupdated = FmtDateTime($sec);
if( my $offset_bridge = $iohash->{helper}{offsetUTC} ) {
$offset = $offset_bridge;
Log3 $name, 4, "$name: use offsetUTC $offset from bridge";
}else{
#we do not have received the offsetUTC from the bridge, use the system offsetUTC until we received it
Log3 $name, 5, "$name: using offsetUTC $offset from bridge";
} else {
# bridge has no offsetUTC configured, use the system offsetUTC for now
my @t = localtime(time);
$offset = timegm(@t) - timelocal(@t);
Log3 $name, 4, "$name: use offsetUTC $offset from system";
Log3 $name, 5, "$name: using offsetUTC $offset from system";
}
#add offset to UTC for displaying in fhem
$sec += $offset;
$lastupdated_local = FmtDateTime($sec);
$ts += $offset;
$lastupdated = FmtDateTime($ts);
}else{
$lastupdated_local = $lastupdated;
} else {
# what to do? can this happen?
Log3 $name, 1, "$name: HUEDevice_Parse called without hash->{IODev}";
}
@ -1799,25 +1805,8 @@ HUEDevice_Parse($$)
}
}
$hash->{lastupdated} = ReadingsVal( $name, '.lastupdated', '' ) if( !$hash->{lastupdated} );
$hash->{lastupdated_local} = ReadingsVal( $name, '.lastupdated_local', '' ) if( !$hash->{lastupdated_local} );
substr($hash->{lastupdated},10, 1, '_' ) if( $hash->{lastupdated} );
substr($lastupdated,10, 1, '_' ) if( $lastupdated );
my $hlu = SVG_time_to_sec( $hash->{lastupdated} );
my $lu = SVG_time_to_sec( $lastupdated );
return undef if( $hash->{lastupdated}
&& $hlu <= $lu
&& !defined($result->{v2_service})
#&& $hash->{lastupdated} eq $lastupdated
#&& (!defined($readings{state}) || $readings{state} eq ReadingsVal( $name, 'state', '' ))
);
Log3 $name, 4, "$name: lastupdated: $lastupdated, hash->{lastupdated}: $hash->{lastupdated}, lastupdated_local: $lastupdated_local, offsetUTC: $offset";
#Log3 $name, 5, "$name: ". Dumper $result if($HUEDevice_hasDataDumper);
$hash->{lastupdated} = $lastupdated;
$hash->{lastupdated_local} = $lastupdated_local;
CommandDeleteReading( undef, "$name .lastupdated" );
CommandDeleteReading( undef, "$name .lastupdated_local" );
if( scalar keys %readings ) {
readingsBeginUpdate($hash);
@ -1825,27 +1814,22 @@ HUEDevice_Parse($$)
my $i = 0;
foreach my $key ( keys %readings ) {
if( defined($readings{$key}) ) {
if( $lastupdated_local) {
$hash->{'.updateTimestamp'} = $lastupdated_local;
$hash->{CHANGETIME}[$i] = $lastupdated_local;
my $rut = ReadingsTimestamp($name,$key,undef);
if( !defined($result->{v2_service}) && defined($rut) && $ts <= time_str2num($rut) ) {
Log3 $name, 4, "$name: ignoring reading $key with timestamp $lastupdated, current reading timestamp is $rut";
next;
}
if( $lastupdated ) {
$hash->{'.updateTimestamp'} = $lastupdated;
$hash->{CHANGETIME}[$i] = $lastupdated;
}
readingsBulkUpdate($hash, $key, $readings{$key}, 1);
++$i;
}
}
if( $lastupdated_local ) {
$hash->{'.updateTimestamp'} = $lastupdated_local;
$hash->{CHANGETIME}[$i] = $lastupdated_local;
readingsBulkUpdate($hash, '.lastupdated_local', $lastupdated_local, 0);
}
if( $lastupdated ) {
readingsBulkUpdate($hash, '.lastupdated', $lastupdated, 0);
}
readingsEndUpdate($hash,1);
delete $hash->{CHANGETIME};
}