From 51a00eca3085659f0a87ac3b63edc8f91554f2d0 Mon Sep 17 00:00:00 2001 From: justme-1968 Date: Fri, 21 Jan 2022 14:34:53 +0000 Subject: [PATCH] 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 --- fhem/FHEM/30_HUEBridge.pm | 69 +++++++++++++++++++++--------- fhem/FHEM/31_HUEDevice.pm | 90 ++++++++++++++++----------------------- 2 files changed, 87 insertions(+), 72 deletions(-) diff --git a/fhem/FHEM/30_HUEBridge.pm b/fhem/FHEM/30_HUEBridge.pm index daec718e3..cd6ef2610 100644 --- a/fhem/FHEM/30_HUEBridge.pm +++ b/fhem/FHEM/30_HUEBridge.pm @@ -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 ); diff --git a/fhem/FHEM/31_HUEDevice.pm b/fhem/FHEM/31_HUEDevice.pm index 76524ba92..fade7e825 100644 --- a/fhem/FHEM/31_HUEDevice.pm +++ b/fhem/FHEM/31_HUEDevice.pm @@ -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}; }