diff --git a/fhem/FHEM/98_statistics.pm b/fhem/FHEM/98_statistics.pm
index dae29d0b0..49a8b7893 100644
--- a/fhem/FHEM/98_statistics.pm
+++ b/fhem/FHEM/98_statistics.pm
@@ -42,9 +42,10 @@ sub statistics_DoStatistics ($$$);
sub statistics_doStatisticMinMax ($$$$$);
sub statistics_doStatisticMinMaxSingle ($$$$$$);
sub statistics_doStatisticDelta ($$$$$);
+sub statistics_getStoredDevices($);
# Modul Version for remote debugging
- my $modulVersion = "2014-05-10";
+ my $modulVersion = "2014-05-13";
##############################################################
# Syntax: deviceType, readingName, statisticType, decimalPlaces
@@ -69,6 +70,29 @@ sub statistics_doStatisticDelta ($$$$$);
,["OREGON", "rain", 2, 1]
,["OREGON", "temperature", 1, 1]
);
+ my @knownDevices = ( "CUL_WS"
+ , "dummy"
+ , "FBDECT"
+ , "FRM_IN"
+ , "FS20"
+ , "KS300"
+ , "OREGON"
+ );
+ my @knownReadings = ( ["brightness", 1, 0]
+ ,["count", 2, 0]
+ ,["current", 1, 3]
+ ,["energy", 2, 0]
+ ,["humidity", 1, 0]
+ ,["power", 1, 1]
+ ,["rain", 2, 1]
+ ,["rain_total", 2, 1]
+ ,["state", 3, 1]
+ ,["temperature", 1, 1]
+ ,["total", 2, 2]
+ ,["voltage", 1, 1]
+ ,["wind", 1, 0]
+ ,["windSpeed", 1, 0]
+ );
##############################################################
sub ##########################################
@@ -80,11 +104,14 @@ statistics_Initialize($)
$hash->{UndefFn} = "statistics_Undefine";
$hash->{NotifyFn} = "statistics_Notify";
$hash->{NOTIFYDEV} = "global";
+ $hash->{SetFn} = "statistics_Set";
$hash->{NotifyOrderPrefix} = "10-"; # Want to be called before the rest
$hash->{AttrList} = "disable:0,1 "
- ."DayChangeTime "
+ ."dayChangeTime "
."excludedReadings "
+ ."periodChangePreset "
+ ."singleReadings "
.$readingFnAttributes;
}
@@ -112,8 +139,8 @@ statistics_Define($$)
RemoveInternalTimer($hash);
- # Run period change procedure each full hour.
- my $periodEndTime = 3600 * (int( gettimeofday() / 3600 ) + 1) ;
+ # Run period change procedure each full hour (1 second before).
+ my $periodEndTime = 3600 * ( int(gettimeofday()/3600) + 1 ) - 10 ;
InternalTimer( $periodEndTime, "statistics_PeriodChange", $hash, 0);
return undef;
@@ -129,6 +156,38 @@ statistics_Undefine($$)
return undef;
}
+sub ########################################
+statistics_Set($$@)
+{
+ my ($hash, $name, $cmd, $val) = @_;
+ my $resultStr = "";
+
+if ($cmd eq 'resetStatistics') {
+ if ($val ne "") {
+ my $regExp;
+ if ($val eq "all") { $regExp = ""; }
+ else { $regExp = $val.":.*"; }
+ foreach (sort keys %{ $hash->{READINGS} }) {
+ if ($_ =~ /^\.$regExp/ && $_ ne "state") {
+ delete $hash->{READINGS}{$_};
+ $resultStr .= "\n * " . substr $_, 1;
+ }
+ }
+ }
+ if ( $resultStr eq "" ) {
+ $resultStr = "$name: No statistics to reset";
+ } else {
+ $resultStr = "$name: Statistic value(s) reset:" . $resultStr;
+ readingsSingleUpdate($hash,"state","Statistic value(s) reset: $val",1);
+ }
+ # Log3 $hash, 3, $resultStr;
+ return $resultStr;
+ }
+
+ my $list = "resetStatistics:all" . statistics_getStoredDevices($hash);
+
+ return "Unknown argument $cmd, choose one of $list";
+}
sub ########################################
statistics_Notify($$)
@@ -151,8 +210,12 @@ statistics_Notify($$)
}
my $val="";
foreach my $device (sort (keys(%unknownDevices))) {
- if ($val ne "") { $val.=","; }
- $val .= $device;
+ if (exists ($defs{$device})) {
+ statistics_DoStatistics $hash, $defs{$device}, 0;
+ } else {
+ if ($val ne "") { $val.=","; }
+ $val .= $device;
+ }
}
if ($val ne "") {
Log3 $name,4,"$name: Initialization - Found hidden readings for device(s) '$val'.";
@@ -200,17 +263,23 @@ statistics_Notify($$)
sub ########################################
statistics_PeriodChange($)
{
- my ($hash) = @_;
- my $name = $hash->{NAME};
- my $dummy;
-
- RemoveInternalTimer($hash);
- #Run period change procedure each hour.
- my $periodEndTime = 3600 * (int( gettimeofday() / 3600 ) + 1 );
- InternalTimer( $periodEndTime, "statistics_PeriodChange", $hash, 0);
- Log3 $name,5,"$name: Next period change will be calculated at ".strftime ("%H:%M:%S", localtime($periodEndTime));
+ my ($hash) = @_;
+ my $name = $hash->{NAME};
+ my $dummy;
+ my $periodChangePreset = AttrVal($name, "periodChangePreset", 10);
- return if( AttrVal($name, "disable", 0 ) == 1 );
+ my $dayChangeTime = 0;
+ if (AttrVal($name, "dayChangeTime", "00:00") =~ /(\d+):(\d+)/ && $1<24 && $1 >=0 && $2<60 && $2>=0) {
+ $dayChangeTime = $1 * 3600 + $2 * 60;
+ }
+
+ RemoveInternalTimer($hash);
+ # Run period change procedure each full hour (1 second before).
+ my $periodEndTime = 3600 * ( int(gettimeofday()/3600) + 1 ) - $periodChangePreset ;
+ InternalTimer( $periodEndTime, "statistics_PeriodChange", $hash, 0);
+ Log3 $name,5,"$name: Next period change will be calculated at ".strftime ("%H:%M:%S", localtime($periodEndTime));
+
+ return if( AttrVal($name, "disable", 0 ) == 1 );
# Determine if time period switched (day, month, year)
# Get deltaValue and Tariff of previous call
@@ -223,8 +292,8 @@ statistics_PeriodChange($)
my $monthNow;
my $yearNow;
- ($dummy, $dummy, $dummy, $dayLast, $monthLast, $yearLast) = localtime (gettimeofday()-1800);
- ($dummy, $dummy, $dummy, $dayNow, $monthNow, $yearNow) = localtime (gettimeofday());
+ ($dummy, $dummy, $dummy, $dayLast, $monthLast, $yearLast) = localtime (gettimeofday() - 1800);
+ ($dummy, $dummy, $dummy, $dayNow, $monthNow, $yearNow) = localtime (gettimeofday() + 10 + $periodChangePreset);
if ($yearNow != $yearLast) { $periodSwitch = 4; }
elsif ($monthNow != $monthLast) { $periodSwitch = 3; }
@@ -283,18 +352,16 @@ statistics_DoStatistics($$$)
readingsBeginUpdate($dev);
# Loop through all known device types and readings
- foreach my $f (@knownDeviceReadings)
+ foreach my $f (@knownReadings)
{
- $readingName = $$f[1];
-
- # notifing device type is known and the device has also the known reading
- # No statistic for excluded Readings
+ # notifing device has known reading, no statistic for excluded readings
+ $readingName = $$f[0];
my $completeReadingName = $devName.":".$readingName;
- if ($$f[0] eq $devType && $completeReadingName !~ m/^($exclReadings)$/ ) {
- $statisticDone = 1;
- if ($$f[2] == 1) { statistics_doStatisticMinMax ($hash, $dev, $readingName, $$f[3], $periodSwitch);}
- if ($$f[2] == 2) { statistics_doStatisticDelta ($hash, $dev, $readingName, $$f[3], $periodSwitch);}
- }
+ next if ($completeReadingName =~ m/^($exclReadings)$/ );
+ next if not exists ($dev->{READINGS}{$readingName});
+ $statisticDone = 1;
+ if ($$f[1] == 1) { statistics_doStatisticMinMax ($hash, $dev, $readingName, $$f[2], $periodSwitch);}
+ if ($$f[1] == 2) { statistics_doStatisticDelta ($hash, $dev, $readingName, $$f[2], $periodSwitch);}
}
if ($periodSwitch >0) {readingsEndUpdate($dev,1);}
else {readingsEndUpdate($dev,0);}
@@ -336,13 +403,14 @@ sub ########################################
statistics_doStatisticMinMax ($$$$$)
{
my ($hash, $dev, $readingName, $decPlaces, $periodSwitch) = @_;
-
+ my $name = $hash->{NAME};
return if not exists ($dev->{READINGS}{$readingName});
# Get reading, cut out first number without units
my $value = $dev->{READINGS}{$readingName}{VAL};
- $value =~ s/^([\d.]*).*/$1/eg;
+ $value =~ s/^[\D]*([\d.]*).*/$1/eg;
+ Log3 $name, 5, "Calculating min/avg/max statistics for '$readingName=$value'";
# statistics_doStatisticMinMaxSingle: $hash, $readingName, $value, $saveLast, decPlaces
# Daily Statistic
statistics_doStatisticMinMaxSingle $hash, $dev, $readingName."Day", $value, ($periodSwitch >= 2), $decPlaces;
@@ -362,6 +430,7 @@ statistics_doStatisticMinMaxSingle ($$$$$$)
my ($hash, $dev, $readingName, $value, $saveLast, $decPlaces) = @_;
my $result;
my $hiddenReadingName = ".".$dev->{NAME}.":".$readingName;
+ my $name=$hash->{NAME};
my $statReadingName = $hash->{PREFIX};
$statReadingName .= ucfirst($readingName);
@@ -393,16 +462,19 @@ statistics_doStatisticMinMaxSingle ($$$$$$)
# Store current reading as last reading, Reset current reading
if ($saveLast) {
readingsBulkUpdate($dev, $statReadingName . "Last", $result, 1);
+ Log3 $name, 5, "Set '".$statReadingName . "Last'='$result'";
$hidden[1] = 0; $hidden[3] = 0; $hidden[9] = 0; # No since value anymore
$result = "Min: $value Avg: $value Max: $value";
}
# Store current reading
readingsBulkUpdate($dev, $statReadingName, $result, 0);
+ Log3 $name, 5, "Set '$statReadingName'='$result'";
# Store hidden reading
$result = "Sum: $hidden[1] Time: $hidden[3] LastValue: ".$value." LastTime: ".int(gettimeofday())." ShowDate: $hidden[9]";
readingsSingleUpdate($hash, $hiddenReadingName, $result, 0);
+ Log3 $name, 5, "Set '$hiddenReadingName'='$result'";
return;
}
@@ -422,7 +494,8 @@ statistics_doStatisticDelta ($$$$$)
# Get reading, cut out first number without units
my $value = $dev->{READINGS}{$readingName}{VAL};
- $value =~ s/^([\d.]*).*/$1/eg;
+ $value =~ s/^[\D]*([\d.]*).*/$1/eg;
+ Log3 $name, 5, "Calculating delta statistics for '$readingName=$value'";
my $hiddenReadingName = ".".$dev->{NAME}.":".$readingName;
@@ -432,7 +505,6 @@ statistics_doStatisticDelta ($$$$$)
my @hidden; my @stat; my @last;
my $firstRun = not exists($hash->{READINGS}{$hiddenReadingName});
-
if ( $firstRun ) {
# Show since-Value and initialize all readings
$showDate = 8;
@@ -504,24 +576,41 @@ statistics_doStatisticDelta ($$$$$)
$result = "Hour: $stat[1] Day: $stat[3] Month: $stat[5] Year: $stat[7]";
if ( $showDate >=2 ) { $result .= " (since: $stat[9] )"; }
readingsBulkUpdate($dev,$statReadingName,$result, 1);
- Log3 $name,5,"$name: Store '$result' in '$statReadingName'";
+ Log3 $name,5,"$name: Set '$statReadingName'='$result'";
# if changed, store previous visible statistic (delta) values
if ($periodSwitch >= 1) {
$result = "Hour: $last[1] Day: $last[3] Month: $last[5] Year: $last[7]";
if ( $showDate =~ /1|3|5|7/ ) { $result .= " (since: $last[9] )"; }
readingsBulkUpdate($dev,$statReadingName."Last",$result, 1);
- Log3 $name,4,"$name: Store '$result' in '".$statReadingName."Last'.";
+ Log3 $name,4,"$name: Set '".$statReadingName."Last'='$result'";
}
# Store hidden reading
$result = "LastValue: $value ShowDate: $showDate ";
readingsSingleUpdate($hash, $hiddenReadingName, $result, 0);
- Log3 $name,5,"$name: Store '$result' in '$hiddenReadingName'.";
+ Log3 $name,5,"$name: Set '$hiddenReadingName'='$result'";
return ;
}
+sub ####################
+statistics_getStoredDevices ($)
+{
+ my ($hash) = @_;
+ my $result="";
+ foreach my $r (sort keys %{$hash->{READINGS}}) {
+ if ($r =~ /^\.(.*):.*/) {
+ my $device = $1;
+ my $regExp = '^'.$1.'$|^'.$1.',|,'.$1.'$|,'.$1.',';
+ if ($result !~ /$regExp/) {
+ $result.="," . $device;
+ }
+ }
+ }
+ return $result;
+}
+
1;
=pod
@@ -606,36 +695,64 @@ statistics_doStatisticDelta ($$$$$)
Regularer Ausdruck für den Gerätenamen. !!! Nicht die Gerätewerte !!!
- Derzeit werden folgende Gerätetypen und Gerätewerte ausgewertet:
-
resetStatistics <All|Gerätename>
+ resetStatistics <All|Gerätename>
+ excludedReadings <GeräteName:WertRegExp>
+ dayChangeTime <Zeit>
excludedReadings <RegExpGerätename:RegExpGerätewert>
+ periodChangePreset <Sekunden>
+ singleReadings <Gerätename:Gerätewert>:Statistiktypen(Min|Avg|Max|Delta):ZeitPeriode(Hour|Day|Month|Year)
+