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

View File

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