From a220dacb66b0b3c870dc3cf80727654f4baed11e Mon Sep 17 00:00:00 2001
From: rudolfkoenig <>
Date: Wed, 19 Aug 2020 16:54:17 +0000
Subject: [PATCH] fhem.pl: better support for deviating reading/event
timestamps (Forum #113652)
git-svn-id: https://svn.fhem.de/fhem/trunk@22631 2b470e98-0d58-463d-a4d8-8e2adae1ed80
---
fhem/FHEM/01_FHEMWEB.pm | 4 ++-
fhem/docs/commandref_frame.html | 6 ++--
fhem/docs/commandref_frame_DE.html | 6 ++--
fhem/fhem.pl | 53 +++++++++++++++++++-----------
4 files changed, 45 insertions(+), 24 deletions(-)
diff --git a/fhem/FHEM/01_FHEMWEB.pm b/fhem/FHEM/01_FHEMWEB.pm
index 145c2fc70..ec6f50ce4 100644
--- a/fhem/FHEM/01_FHEMWEB.pm
+++ b/fhem/FHEM/01_FHEMWEB.pm
@@ -3112,6 +3112,7 @@ FW_Notify($$)
#Add READINGS
if($events) { # It gets deleted sometimes (?)
my $tn = TimeNow();
+ my $ct = $dev->{CHANGETIME};
my $max = int(@{$events});
for(my $i = 0; $i < $max; $i++) {
if($events->[$i] !~ /: /) {
@@ -3127,7 +3128,8 @@ FW_Notify($$)
next if($readingName !~ m/^[A-Za-z\d_\.\-\/:]+$/); # Forum #70608,70844
push @data, FW_longpollInfo($h->{fmt},
"$dn-$readingName", $readingVal,$readingVal);
- push @data, FW_longpollInfo($h->{fmt}, "$dn-$readingName-ts", $tn, $tn);
+ my $t = (($ct && $ct->[$i]) ? $ct->[$i] : $tn);
+ push @data, FW_longpollInfo($h->{fmt}, "$dn-$readingName-ts", $t, $t);
}
}
}
diff --git a/fhem/docs/commandref_frame.html b/fhem/docs/commandref_frame.html
index 605b36b3c..69b78ef7d 100644
--- a/fhem/docs/commandref_frame.html
+++ b/fhem/docs/commandref_frame.html
@@ -1279,12 +1279,14 @@ The following local attributes are used by a wider range of devices:
setreading
- setreading <devspec> <reading> <value>
+ setreading <devspec> [YYYY-MM-DD HH:MM:SS] <reading>
+ <value>
Set the reading <reading> for the device <name>
to
<value> without sending out commands to the device, but triggering
events and eventMap/stateFormat transformations as usual. See the set
- command documentation for replacement description.
+ command documentation for replacement description.
+ If the timespec is omitted (default) the current time will be used.
Examples:
diff --git a/fhem/docs/commandref_frame_DE.html b/fhem/docs/commandref_frame_DE.html
index 0286f12b7..45c533bd1 100644
--- a/fhem/docs/commandref_frame_DE.html
+++ b/fhem/docs/commandref_frame_DE.html
@@ -1361,12 +1361,14 @@ Die folgenden lokalen Attribute werden von mehreren Geräten verwendet:
setreading
- setreading <devspec> <reading> <value>
+ setreading <devspec> [YYYY-MM-DD HH:MM:SS] <reading>
+ <value>
Der Befehl setzt das Reading <reading> auf den Wert <value> ohne
Signale an das betroffene Gerät zu senden, generiert aber Ereignisse und
die übliche eventMap und stateFormat Umwandlung wird auch
- durchgeführt.
+ durchgeführt.
+ Falls keine Zeit spezifiziert wurde, wird die aktuelle Uhrzeit verwendet.
Siehe den Abschnitt über Geräte-Spezifikation
für Details der <devspec> und die Beschreibung des set Befehls
diff --git a/fhem/fhem.pl b/fhem/fhem.pl
index 7e140be5d..7647f7896 100755
--- a/fhem/fhem.pl
+++ b/fhem/fhem.pl
@@ -111,7 +111,7 @@ sub TimeNow();
sub Value($);
sub WriteStatefile();
sub XmlEscape($);
-sub addEvent($$);
+sub addEvent($$;$);
sub addToDevAttrList($$);
sub applyGlobalAttrFromEnv();
sub delFromDevAttrList($$);
@@ -150,7 +150,7 @@ sub perlSyntaxCheck($%);
sub readingsBeginUpdate($);
sub readingsBulkUpdate($$$@);
sub readingsEndUpdate($$);
-sub readingsSingleUpdate($$$$);
+sub readingsSingleUpdate($$$$;$);
sub readingsDelete($$);
sub redirectStdinStdErr();
sub rejectDuplicate($$$);
@@ -457,7 +457,8 @@ my %ra = (
"set" => { Fn=>"CommandSet",
Hlp=>" ,transmit code for " },
"setreading" => { Fn=>"CommandSetReading",
- Hlp=>" ,set reading for " },
+ Hlp=>" [YYYY-MM-DD HH:MM:SS] ,".
+ "set reading for " },
"setstate"=> { Fn=>"CommandSetstate",
Hlp=>" ,set the state shown in the command list" },
"setuuid" => { Fn=>"CommandSetuuid", Hlp=>"" },
@@ -2416,9 +2417,16 @@ sub
CommandSetReading($$)
{
my ($cl, $def) = @_;
+ my $timestamp;
+
+ if($def =~ m/^([^ ]+) +(\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d) +([^ ]+) +(.*)$/) {
+ $def = "$1 $3 $4";
+ $timestamp = $2;
+ }
my @a = split(" ", $def, 3);
- return "Usage: setreading \n$namedef" if(@a != 3);
+ return "Usage: setreading [YYYY-MM-DD HH:MM:SS] \n".
+ $namedef if(@a != 3);
my $err;
my @b = @a;
@@ -2443,7 +2451,7 @@ CommandSetReading($$)
Log 1, "'setreading $def' called form userReadings is prohibited";
return;
} else {
- readingsSingleUpdate($hash, $b1, $b[2], 1);
+ readingsSingleUpdate($hash, $b1, $b[2], 1, $timestamp);
}
}
return join("\n", @rets);
@@ -3732,6 +3740,12 @@ DoTrigger($$@)
################
# Inform
if($hash->{CHANGED}) { # It gets deleted sometimes (?)
+ my $tn = $now;
+ if($attr{global}{mseclog}) {
+ my ($seconds, $microseconds) = gettimeofday();
+ $tn .= sprintf(".%03d", $microseconds/1000);
+ }
+ my $ct = $hash->{CHANGETIME};
foreach my $c (keys %inform) {
my $dc = $defs{$c};
if(!$dc || $dc->{NR} != $inform{$c}{NR}) {
@@ -3739,18 +3753,14 @@ DoTrigger($$@)
next;
}
next if($inform{$c}{type} eq "raw");
- my $tn = $now;
- if($attr{global}{mseclog}) {
- my ($seconds, $microseconds) = gettimeofday();
- $tn .= sprintf(".%03d", $microseconds/1000);
- }
my $re = $inform{$c}{regexp};
my $events = deviceEvents($hash, $inform{$c}{type} =~ m/WithState/);
$max = int(@{$events});
for(my $i = 0; $i < $max; $i++) {
my $event = $events->[$i];
+ my $t = (($ct && $ct->[$i]) ? $ct->[$i] : $tn);
next if($re && !($dev =~ m/$re/ || "$dev:$event" =~ m/$re/));
- addToWritebuffer($dc,($inform{$c}{type} eq "timer" ? "$tn " : "").
+ addToWritebuffer($dc,($inform{$c}{type} eq "timer" ? "$t " : "").
"$hash->{TYPE} $dev $event\n");
}
}
@@ -4612,10 +4622,14 @@ setReadingsVal($$$$)
}
sub
-addEvent($$)
+addEvent($$;$)
{
- my ($hash,$event) = @_;
+ my ($hash,$event,$timestamp) = @_;
push(@{$hash->{CHANGED}}, $event);
+ if($timestamp) {
+ $hash->{CHANGETIME} = [] if(!defined($hash->{CHANGETIME}));
+ $hash->{CHANGETIME}->[@{$hash->{CHANGED}}-1] = $timestamp;
+ }
}
sub
@@ -4817,7 +4831,7 @@ readingsBulkUpdateIfChanged($$$@) # Forum #58797
sub
readingsBulkUpdate($$$@)
{
- my ($hash,$reading,$value,$changed)= @_;
+ my ($hash,$reading,$value,$changed,$timestamp)= @_;
my $name= $hash->{NAME};
return if(!defined($reading) || !defined($value));
@@ -4947,7 +4961,8 @@ readingsBulkUpdate($$$@)
}
- setReadingsVal($hash, $reading, $value, $hash->{".updateTimestamp"})
+ setReadingsVal($hash, $reading, $value,
+ $timestamp ? $timestamp : $hash->{".updateTimestamp"})
if($update_timestamp);
my $rv = "$reading: $value";
@@ -4956,7 +4971,7 @@ readingsBulkUpdate($$$@)
$rv = $value;
$hash->{CHANGEDWITHSTATE} = [];
}
- addEvent($hash, $rv);
+ addEvent($hash, $rv, $timestamp);
}
return $rv;
}
@@ -4965,11 +4980,11 @@ readingsBulkUpdate($$$@)
# this is a shorthand call
#
sub
-readingsSingleUpdate($$$$)
+readingsSingleUpdate($$$$;$)
{
- my ($hash,$reading,$value,$dotrigger)= @_;
+ my ($hash,$reading,$value,$dotrigger,$timestamp)= @_;
readingsBeginUpdate($hash);
- my $rv = readingsBulkUpdate($hash,$reading,$value);
+ my $rv = readingsBulkUpdate($hash, $reading, $value, undef, $timestamp);
readingsEndUpdate($hash,$dotrigger);
return $rv;
}