From c83e6103e562b9867035aadea8a910479c1d466f Mon Sep 17 00:00:00 2001 From: tpoitzsch Date: Mon, 19 May 2014 06:12:06 +0000 Subject: [PATCH] statistics: duration of Window and state git-svn-id: https://svn.fhem.de/fhem/trunk@5891 2b470e98-0d58-463d-a4d8-8e2adae1ed80 --- fhem/FHEM/98_statistics.pm | 242 ++++++++++++++++++++++++++++--------- 1 file changed, 182 insertions(+), 60 deletions(-) diff --git a/fhem/FHEM/98_statistics.pm b/fhem/FHEM/98_statistics.pm index 50c2018ed..0a281b061 100644 --- a/fhem/FHEM/98_statistics.pm +++ b/fhem/FHEM/98_statistics.pm @@ -38,47 +38,24 @@ use warnings; use Time::Local; sub statistics_PeriodChange($); +sub statistics_DoStatisticsAll($); sub statistics_DoStatistics ($$$); sub statistics_doStatisticMinMax ($$$$$); sub statistics_doStatisticMinMaxSingle ($$$$$$$); -sub statistics_storeSingleReadings ($$$$$$$$$$); sub statistics_doStatisticDelta ($$$$$); +sub statistics_doStatisticDuration ($$$$); +sub statistics_doStatisticDurationSingle ($$$$$$); +sub statistics_storeSingularReadings ($$$$$$$$$$); sub statistics_getStoredDevices($); +sub statistics_FormatDuration($); # Modul Version for remote debugging my $modulVersion = "2014-05-13"; ############################################################## # Syntax: deviceType, readingName, statisticType, decimalPlaces -# statisticType: 0=noStatistic | 1=minMaxAvg | 2=deltaTime | 3=stateTimeCount +# statisticType: 0=noStatistic | 1=minMaxAvg | 2=delta | 3=stateDuration ############################################################## - my @knownDeviceReadings = ( - ["CUL_WS", "humidity", 1, 0] - ,["CUL_WS", "temperature", 1, 1] - ,["dummy", "state", 3, 1] - ,["FBDECT", "current", 1, 3] - ,["FBDECT", "energy", 2, 0] - ,["FBDECT", "power", 1, 1] - ,["FBDECT", "voltage", 1, 1] - ,["FRM_IN", "count", 2, 0] - ,["FRM_IN", "power", 1, 2] - ,["FS20", "state", 3, 1] - ,["KS300", "humidity", 1, 0] - ,["KS300", "temperature", 1, 1] - ,["KS300", "wind", 1, 0] - ,["KS300", "rain", 2, 1] - ,["OREGON", "humidity", 1, 1] - ,["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] @@ -87,11 +64,11 @@ sub statistics_getStoredDevices($); ,["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] + ,["Window", 3, 1] ,["windSpeed", 1, 0] ); ############################################################## @@ -163,7 +140,7 @@ statistics_Set($$@) my ($hash, $name, $cmd, $val) = @_; my $resultStr = ""; -if ($cmd eq 'resetStatistics') { + if ($cmd eq 'resetStatistics') { if ($val ne "") { my $regExp; if ($val eq "all") { $regExp = ""; } @@ -183,10 +160,13 @@ if ($cmd eq 'resetStatistics') { } # Log3 $hash, 3, $resultStr; return $resultStr; + + } elsif ($cmd eq 'doStatistics') { + statistics_DoStatisticsAll($hash); + return undef; } - my $list = "resetStatistics:all" . statistics_getStoredDevices($hash); - + $list .= " doStatistics:noArg"; return "Unknown argument $cmd, choose one of $list"; } @@ -199,21 +179,20 @@ statistics_Notify($$) # At startup: delete old Readings of monitored devices and rebuild from hidden readings if ($devName eq "global" && grep (m/^INITIALIZED|REREADCFG$/,@{$dev->{CHANGED}})) { - my %unknownDevices; - foreach my $r (keys %{$hash->{READINGS}}) { - if ($r =~ /^\.(.*):.*/) { $unknownDevices{$1}++; } - } foreach my $r (keys %{$hash->{READINGS}}) { if ($r =~ /^monitoredDevices.*/) { Log3 $name,5,"$name: Initialization - Delete old reading '$r'."; delete($hash->{READINGS}{$r}); } - } + } + statistics_DoStatisticsAll $hash; + my %unknownDevices; + foreach my $r (keys %{$hash->{READINGS}}) { + if ($r =~ /^\.(.*):.*/) { $unknownDevices{$1}++; } + } my $val=""; foreach my $device (sort (keys(%unknownDevices))) { - if (exists ($defs{$device})) { - statistics_DoStatistics $hash, $defs{$device}, 0; - } else { + if (not exists ($defs{$device})) { if ($val ne "") { $val.=","; } $val .= $device; } @@ -309,7 +288,7 @@ statistics_PeriodChange($) my $yearNow; ($dummy, $dummy, $dummy, $dayLast, $monthLast, $yearLast) = localtime (gettimeofday() - 1800); - ($dummy, $minuteNow, $hourNow, $dayNow, $monthNow, $yearNow) = localtime (gettimeofday() + 10 + $periodChangePreset); + ($dummy, $minuteNow, $hourNow, $dayNow, $monthNow, $yearNow) = localtime (gettimeofday() + 1800); if ($yearNow != $yearLast) { $periodSwitch = 4; } elsif ($monthNow != $monthLast) { $periodSwitch = 3; } @@ -323,7 +302,7 @@ statistics_PeriodChange($) my @devNameArray = split /,/, $hash->{READINGS}{$r}{VAL}; foreach my $devName (@devNameArray) { Log3 $name,4,"$name: Doing period change statistics for device '$devName'"; - statistics_DoStatistics($hash, $defs{$devName}, $periodSwitch); + statistics_DoStatistics $hash, $defs{$devName}, $periodSwitch; } } } @@ -332,6 +311,23 @@ statistics_PeriodChange($) return undef; } +########################## +sub +statistics_DoStatisticsAll($) +{ + my ($hash) = @_; + my $name = $hash->{NAME}; + return "" if(!defined($hash->{DEV_REGEXP})); + my $regexp = $hash->{DEV_REGEXP}; + foreach my $devName (sort keys %defs) { + if($devName =~ m/^($regexp)$/) { + Log3 $name,4,"$name: Doing statistics for device '$devName'"; + statistics_DoStatistics($hash, $defs{$devName}, 0); + } + } +} + + ########################## sub statistics_DoStatistics($$$) @@ -378,7 +374,15 @@ statistics_DoStatistics($$$) $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 ($$f[1] == 3) { statistics_doStatisticDuration ($hash, $dev, $readingName, $periodSwitch);} } + if ($statisticDone != 1) { + if (exists ($dev->{READINGS}{state})) { + statistics_doStatisticDuration $hash, $dev, "state", $periodSwitch; + $statisticDone = 1; + } + } + if ($periodSwitch >0) {readingsEndUpdate($dev,1);} else {readingsEndUpdate($dev,0);} @@ -492,13 +496,13 @@ statistics_doStatisticMinMaxSingle ($$$$$$$) # Store single readings my $singularReadings = AttrVal($name, "singularReadings", ""); if ($singularReadings ne "") { - # statistics_storeSingleReadings $hashName,$singleReading,$dev,$statReadingName,$readingName,$statType,$period,$statValue,$value,$saveLast + # statistics_storeSingularReadings $hashName,$singleReading,$dev,$statReadingName,$readingName,$statType,$period,$statValue,$value,$saveLast my $statValue = sprintf "%.".$decPlaces."f", $stat[1]; - statistics_storeSingleReadings ($name,$singularReadings,$dev,$statReadingName,$readingName,"Min",$period,$statValue,$value,$saveLast); + statistics_storeSingularReadings ($name,$singularReadings,$dev,$statReadingName,$readingName,"Min",$period,$statValue,$value,$saveLast); $statValue = sprintf "%.".$decPlaces."f", $stat[3]; - statistics_storeSingleReadings ($name,$singularReadings,$dev,$statReadingName,$readingName,"Avg",$period,$statValue,$value,$saveLast); + statistics_storeSingularReadings ($name,$singularReadings,$dev,$statReadingName,$readingName,"Avg",$period,$statValue,$value,$saveLast); $statValue = sprintf "%.".$decPlaces."f", $stat[5]; - statistics_storeSingleReadings ($name,$singularReadings,$dev,$statReadingName,$readingName,"Max",$period,$statValue,$value,$saveLast); + statistics_storeSingularReadings ($name,$singularReadings,$dev,$statReadingName,$readingName,"Max",$period,$statValue,$value,$saveLast); } # Store hidden reading @@ -618,11 +622,11 @@ statistics_doStatisticDelta ($$$$$) # Store single readings my $singularReadings = AttrVal($name, "singularReadings", ""); if ($singularReadings ne "") { - # statistics_storeSingleReadings $hashName,$singularReadings,$dev,$statReadingName,$readingName,$statType,$period,$statValue,$value,$saveLast - statistics_storeSingleReadings ($name,$singularReadings,$dev,$statReadingName,$readingName,"Delta","Hour",$stat[1],$last[1],$periodSwitch >= 1); - statistics_storeSingleReadings ($name,$singularReadings,$dev,$statReadingName,$readingName,"Delta","Day",$stat[1],$last[1],$periodSwitch >= 2); - statistics_storeSingleReadings ($name,$singularReadings,$dev,$statReadingName,$readingName,"Delta","Month",$stat[1],$last[1],$periodSwitch >= 3); - statistics_storeSingleReadings ($name,$singularReadings,$dev,$statReadingName,$readingName,"Delta","Year",$stat[1],$last[1],$periodSwitch >= 4); + # statistics_storeSingularReadings $hashName,$singularReadings,$dev,$statReadingName,$readingName,$statType,$period,$statValue,$value,$saveLast + statistics_storeSingularReadings ($name,$singularReadings,$dev,$statReadingName,$readingName,"Delta","Hour",$stat[1],$last[1],$periodSwitch >= 1); + statistics_storeSingularReadings ($name,$singularReadings,$dev,$statReadingName,$readingName,"Delta","Day",$stat[1],$last[1],$periodSwitch >= 2); + statistics_storeSingularReadings ($name,$singularReadings,$dev,$statReadingName,$readingName,"Delta","Month",$stat[1],$last[1],$periodSwitch >= 3); + statistics_storeSingularReadings ($name,$singularReadings,$dev,$statReadingName,$readingName,"Delta","Year",$stat[1],$last[1],$periodSwitch >= 4); } # Store hidden reading @@ -633,9 +637,108 @@ statistics_doStatisticDelta ($$$$$) return ; } +# Calculates single Duration Values and informs about end of day and month +sub ######################################## +statistics_doStatisticDuration ($$$$) +{ + my ($hash, $dev, $readingName, $periodSwitch) = @_; + my $name = $hash->{NAME}; + my $devName = $dev->{NAME}; + return if not exists ($dev->{READINGS}{$readingName}); + + # Get reading, cut out first number without units + my $state = $dev->{READINGS}{$readingName}{VAL}; + + Log3 $name, 4, "Calculating duration statistics for '".$dev->{NAME}.":$readingName = $state'"; + # Daily Statistic + statistics_doStatisticDurationSingle $hash, $dev, $readingName, "Day", $state, ($periodSwitch >= 2); + # Monthly Statistic + statistics_doStatisticDurationSingle $hash, $dev, $readingName, "Month", $state, ($periodSwitch >= 3); + + return ; + +} + +# Calculates single duration values +sub ######################################## +statistics_doStatisticDurationSingle ($$$$$$) +{ + my ($hash, $dev, $readingName, $period, $state, $saveLast) = @_; + my $result; + my $hiddenReadingName = ".".$dev->{NAME}.":".$readingName.$period; + my $name=$hash->{NAME}; + my $devName = $dev->{NAME}; + + my $statReadingName = $hash->{PREFIX}; + $statReadingName .= ucfirst($readingName).$period; + my %hidden; + my $firstRun = not exists($hash->{READINGS}{$hiddenReadingName}); + my $lastState = $state; + my $statValue = "00:00:00"; + + if ( $firstRun ) { + # Show since-Value + $hidden{"showDate:"} = 1; + $saveLast = 0; + # $stat[7] = strftime ("%Y-%m-%d_%H:%M:%S",localtime() ); + } else { + # Do calculations if hidden reading exists + %hidden = split / /, $hash->{READINGS}{$hiddenReadingName}{VAL}; # Internal values + $lastState = $hidden{"lastState:"}; + my $timeDiff = int(gettimeofday())-$hidden{"lastTime:"}; + $hidden{$lastState.":"} += $timeDiff; + $statValue = statistics_FormatDuration ($hidden{$lastState.":"}); + } + $hidden{"lastState:"} = $state; + $hidden{"lastTime:"} = int(gettimeofday()); + + + # Prepare new current reading, delete hidden reading if it is used again + $result = ""; + while (my ($key, $duration) = each(%hidden)){ + if ($key !~ /lastState:|lastTime:|showDate:/) { + if ($result ne "") {$result .= " ";} + $result .= "$key ".statistics_FormatDuration($duration); + if ($saveLast) { delete $hidden{$key}; } + } + } + if ($result eq "") {$result = "$state: 0";} + # if ($hidden[9] == 1) { $result .= " (since: $stat[7] )"; } + + # Store current reading as last reading, Reset current reading + if ($saveLast) { + readingsBulkUpdate($dev, $statReadingName . "Last", $result, 1); + Log3 $name, 5, "Set '".$statReadingName . "Last'='$result'"; + $result = "$state: 00:00:00"; + $hidden{"showDate:"} = 0; + } + + # Store current reading + readingsBulkUpdate($dev, $statReadingName, $result, 0); + Log3 $name, 5, "Set '$statReadingName'='$result'"; + + # Store single readings + my $singularReadings = AttrVal($name, "singularReadings", ""); + if ($singularReadings ne "") { + # statistics_storeSingularReadings $hashName,$singularReadings,$dev,$statReadingName,$readingName,$statType,$period,$statValue,$value,$saveLast + statistics_storeSingularReadings ($name,$singularReadings,$dev,$statReadingName,$readingName,ucfirst($lastState),$period,$statValue,0,$saveLast); + } + + # Store hidden reading + $result = ""; + while (my ($key, $duration) = each(%hidden)){ + if ($result ne "") {$result .= " ";} + $result .= "$key $duration"; + } + readingsSingleUpdate($hash, $hiddenReadingName, $result, 0); + Log3 $name, 5, "Set '$hiddenReadingName = $result'"; + + return; +} + sub #################### -statistics_storeSingleReadings ($$$$$$$$$$) +statistics_storeSingularReadings ($$$$$$$$$$) { my ($hashName,$singularReadings,$dev,$statReadingName,$readingName,$statType,$period,$statValue,$value,$saveLast) = @_; return if $singularReadings eq ""; @@ -674,6 +777,24 @@ statistics_getStoredDevices ($) return $result; } +sub ######################################## +statistics_FormatDuration($) +{ + my ($value) = @_; + #Tage + my $returnstr =""; + if ($value > 86400) { sprintf "%d\d ", int($value/86400); } + # Stunden + $value %= 86400; + $returnstr .= sprintf "%02d:", int($value/3600); + $value %= 3600; + $returnstr .= sprintf "%02d:", int($value/60); + $value %= 60; + $returnstr .= sprintf "%02d", $value; + + return $returnstr; +} + 1; =pod @@ -681,13 +802,14 @@ statistics_getStoredDevices ($)

statistics

-