diff --git a/fhem/CHANGED b/fhem/CHANGED
index 1571d8ae0..3bab4acd2 100644
--- a/fhem/CHANGED
+++ b/fhem/CHANGED
@@ -55,6 +55,7 @@
OWFS
- feature: stateFormat (readingsFn modules) and showInternalValues attributes
- feature: new readingsFn modules: FS20 CUL_WS HMS CUL_EM CUL_TX EnOcean ZWave
+ - change: BS, USF1000, ECMDDevice, Weather, dummy migrated to readingsFN (Boris)
- feature: telnet client mode
- bugfix: FHEMWEB longpoll misses initial state change (HM: set_on vs. on)
- change: 20_OWFS.pm, 21_OWTEMP modules flagged as "deprecated". These
@@ -66,8 +67,8 @@
- feature: added new command 'notice'. (M. Fischer)
- change: update supports the display and confirmation of system messages
via the new notice command (M. Fischer)
- - change: BS, USF1000, dummy migrated to readingsFN (Boris)
- feature: added new set commands and basicauth to 49_IPCAM.pm (M. Fischer)
+ - feature: userReadings
- feature: average supports more than one value in combined readings (T:x H:y)
diff --git a/fhem/docs/commandref_frame.html b/fhem/docs/commandref_frame.html
index 4801074a1..feb89688f 100644
--- a/fhem/docs/commandref_frame.html
+++ b/fhem/docs/commandref_frame.html
@@ -384,18 +384,30 @@ A line ending with \ will be concatenated with the next one, so long lines
userReadings
- A comma-separated list of user-defined readings. Each definition has the form
- <reading> { <perl code> }
. After a single or bulk
+ A comma-separated list of definitions of user-defined readings. Each definition has the form
+ <reading> [<modifier>] { <perl code> }
. After a single or bulk
readings update, the user-defined readings are set by evaluating the
perl code
{ <perl code> }
for all definitions and setting the value of
the respective user-defined reading <reading>
to the result.
Examples:
- attr myPowerMeter userReadings power { ReadingsVal("myPowerMeter","count.A",0)/1250.0;; }
- attr myMultiMeter userReadings power1 { ReadingsVal("myMultiMeter","count.A",0)/1250.0;; },
- power2 { ReadingsVal("myMultiMeter","count.B",0)/1250.0;; }
+ attr myEnergyMeter userReadings energy { ReadingsVal("myEnergyMeter","counters.A",0)/1250.0;; }
+ attr myMultiMeter userReadings energy1 { ReadingsVal("myMultiMeter","counters.A",0)/1250.0;; },
+ energy2 { ReadingsVal("myMultiMeter","counters.B",0)/1250.0;; }
+
+ <modifier>
can take one of these values:
+
+ - none: the same as it would not have been given at all.
+ - difference: the reading is set to the difference between the current and the previously evaluated value.
+ - differential: the reading is set to the difference between the current and the previously evaluated value divided
+ by the time in seconds between the current and the previous evaluation. Granularity of time is one second. No
+ value is calculated if the time past is below one second. Useful to calculate rates.
+
+ Example:
+ attr myPowerMeter userReadings power differential { ReadingsVal("myPowerMeter","counters.A",0)/1250.0;; }
+ Note: user readings with modifiers difference and differential store the calculated values internally. The user reading is
+ set earliest at the second evaluation. Beware of stale values when changing definitions!
-
diff --git a/fhem/docs/commandref_frame_DE.html b/fhem/docs/commandref_frame_DE.html
index f5986fef1..c5b070058 100644
--- a/fhem/docs/commandref_frame_DE.html
+++ b/fhem/docs/commandref_frame_DE.html
@@ -394,16 +394,29 @@ Zeilen erstreckende Befehle, indem man keine \ am Zeilenende eingeben muss.
userReadings
- A comma-separated list of user-defined readings. Each definition has the form
- <reading> { <perl code> }
. After a single or bulk
+ A comma-separated list of definitions of user-defined readings. Each definition has the form
+ <reading> [<modifier>] { <perl code> }
. After a single or bulk
readings update, the user-defined readings are set by evaluating the
perl code
{ <perl code> }
for all definitions and setting the value of
the respective user-defined reading <reading>
to the result.
Examples:
- attr myPowerMeter userReadings power { ReadingsVal("myPowerMeter","count.A",0)/1250.0;; }
- attr myMultiMeter userReadings power1 { ReadingsVal("myMultiMeter","count.A",0)/1250.0;; },
- power2 { ReadingsVal("myMultiMeter","count.B",0)/1250.0;; }
+ attr myEnergyMeter userReadings energy { ReadingsVal("myEnergyMeter","counters.A",0)/1250.0;; }
+ attr myMultiMeter userReadings energy1 { ReadingsVal("myMultiMeter","counters.A",0)/1250.0;; },
+ energy2 { ReadingsVal("myMultiMeter","counters.B",0)/1250.0;; }
+
+ <modifier>
can take one of these values:
+
+ - none: the same as it would not have been given at all.
+ - difference: the reading is set to the difference between the current and the previously evaluated value.
+ - differential: the reading is set to the difference between the current and the previously evaluated value divided
+ by the time in seconds between the current and the previous evaluation. Granularity of time is one second. No
+ value is calculated if the time past is below one second. Useful to calculate rates.
+
+ Example:
+ attr myPowerMeter userReadings power differential { ReadingsVal("myPowerMeter","counters.A",0)/1250.0;; }
+ Note: user readings with modifiers difference and differential store the calculated values internally. The user reading is
+ set earliest at the second evaluation. Beware of stale values when changing definitions!
diff --git a/fhem/fhem.pl b/fhem/fhem.pl
index 0a1eefa4a..2da250eb5 100755
--- a/fhem/fhem.pl
+++ b/fhem/fhem.pl
@@ -1809,24 +1809,28 @@ CommandAttr($$)
if($a[1] eq "userReadings") {
my %userReadings;
-
- # myReading1 { codecodecode1 }, myReading2 { codecodecode2 }, ...
+ # myReading1 [modifier1] { codecodecode1 }, myReading2 [modifier2] { codecodecode2 }, ...
my $arg= $a[2];
- my $regexi= '\s*(\w+)\s+({.*?})\s*'; # matches myReading1 { codecode1 }
+ my $regexi= '\s*(\w+)\s+((\w+)\s+)?({.*?})\s*'; # matches myReading1 { codecode1 }
my $regexo= '^(' . $regexi . ')(,\s*(.*))*$';
#Debug "arg is $arg";
while($arg =~ /$regexo/) {
my $userReading= $2;
- my $perlCode= $3;
- #Debug sprintf("userReading %s has perlCode %s",$userReading,$perlCode);
- $userReadings{$userReading}= $perlCode;
- $arg= defined($5) ? $5 : "";
+ my $modifier= $4 ? $4 : "none";
+ my $perlCode= $5;
+ #Debug sprintf("userReading %s has perlCode %s with modifier %s",$userReading,$perlCode,$modifier);
+ if(grep { /$modifier/ } qw(none difference differential)) {
+ $userReadings{$userReading}{modifier}= $modifier;
+ $userReadings{$userReading}{perlCode}= $perlCode;
+ } else {
+ push @rets, "$sdev: unknown modifier $modifier for userReading $userReading, this userReading will be ignored";
+ }
+ $arg= defined($7) ? $7 : "";
}
$hash->{fhem}{'.userReadings'}= \%userReadings;
-
}
if($a[1] eq "IODev" && (!$a[2] || !defined($defs{$a[2]}))) {
@@ -2990,6 +2994,7 @@ readingsBeginUpdate($)
# get timestamp
my $now = TimeNow();
+ $hash->{".updateTime"} = time(); # in seconds since the epoch
$hash->{".updateTimestamp"} = $now;
my $attreocr= AttrVal($name, "event-on-change-reading", undef);
@@ -3053,19 +3058,42 @@ readingsEndUpdate($$)
if(defined($hash->{fhem}{'.userReadings'})) {
my %userReadings= %{$hash->{fhem}{'.userReadings'}};
foreach my $userReading (keys %userReadings) {
+ my $modifier= $userReadings{$userReading}{modifier};
+ my $perlCode= $userReadings{$userReading}{perlCode};
+ my $oldvalue= $userReadings{$userReading}{value};
+ my $oldt= $userReadings{$userReading}{t};
#Debug "Evaluating " . $userReadings{$userReading};
- my $value= eval $userReadings{$userReading};
+ # evaluate perl code
+ my $value= eval $perlCode;
+ my $result;
+ # store result
if($@) {
$value = "Error evaluating $name userReading $userReading: $@";
Log 1, $value;
- }
- readingsBulkUpdate($hash,$userReading,$value,1);
+ $result= $value;
+ } elsif($modifier eq "none") {
+ $result= $value;
+ } elsif($modifier eq "difference") {
+ $result= $value - $oldvalue if(defined($oldvalue));
+ } elsif($modifier eq "differential") {
+ my $deltav= $value - $oldvalue if(defined($oldvalue));
+ my $deltat= $hash->{".updateTime"} - $oldt if(defined($oldt));
+ if(defined($deltav) && defined($deltat) && ($deltat>= 1.0)) {
+ $result= $deltav/$deltat;
+ }
+ }
+ readingsBulkUpdate($hash,$userReading,$result,1) if(defined($result));
+ # store value
+ $hash->{fhem}{'.userReadings'}{$userReading}{TIME}= $hash->{".updateTimestamp"};
+ $hash->{fhem}{'.userReadings'}{$userReading}{t}= $hash->{".updateTime"};
+ $hash->{fhem}{'.userReadings'}{$userReading}{value}= $value;
}
}
evalStateFormat($hash);
# turn off updating mode
delete $hash->{".updateTimestamp"};
+ delete $hash->{".updateTime"};
delete $hash->{".attreour"};
delete $hash->{".attreocr"};