Gleitende Durchschnitte mit userReadings

Begonnen von Prof. Dr. Peter Henning, 27 Juli 2015, 17:43:59

Vorheriges Thema - Nächstes Thema

justme1968

das steht doch alles (und noch genauer) in dem von mir verlinkten perldoc artikel bzw. in den dort verlinkten details.   
hue, tradfri, alexa-fhem, homebridge-fhem, LightScene, readingsGroup, ...

https://github.com/sponsors/justme-1968

Prof. Dr. Peter Henning

Ach, wenn ich alle Artikel lesen wollte, die mir irgendjemand als Link schickt  8)

LG

pah

justme1968

da ist es natürlich effizienter stur alle hinweise zu ignorieren bis es nicht mehr geht und auf irgendeine eigene alte vorlesung hinzuweisen in der niemand nachsehen kann...

gruss
  andre
hue, tradfri, alexa-fhem, homebridge-fhem, LightScene, readingsGroup, ...

https://github.com/sponsors/justme-1968

Prof. Dr. Peter Henning

#18
Na, da gibt mir einerseits die Lebenserfahrung Recht, und andererseits kann man das sehr wohl nachsehen, das Skript ist öffentlich...

LG

pah

Prof. Dr. Peter Henning

Um das Ganze abzuschließen:

Die saubere Lösung, nämlich die letzten Einträge aufzuheben und darüber einen echten gleitenden Mittelwert zu berechnen, habe ich im Wiki eingestellt. Und zwar ohne Zugriff auf die Log-Dateien.

http://www.fhemwiki.de/wiki/Relative_Mittelwerte_berechnen_und_loggen

LG

pah

linuzer

#20
@pah
Vielen Dank für die tolle Routine, habe sie bei mir im Einsatz. Allerdings habe ich noch eine Frage dazu: Warum ist das Array auf 25 Werte limitiert?
Ich möchte bei meinen Temperatur-Sensoren einen 6-Stunden-Durchschnitt bilden. Da sind doch 25 History-Werte viel zu wenig (wenn die Temperatur alle 1-2 Minuten aktualisiert wird), oder verstehe ich da was grundlegendes noch nicht?

LG
linuzer

Prof. Dr. Peter Henning

Kein Grund - kann man beliebig hoch setzen.

LG

pah

linuzer

Für was ist diese Beschränkung eigentlich überhaupt drin? Wenn die gespeicherten History-Werte über den Betrachtungszeitraum ($avtime) geht, werden die Werte doch eh durchgetauscht. Somit ist das Array auch ohne Beschränkung immer genau so groß, wie es für den Betrachtungszeitraum in Abhängigkeit der Wertehäufigkeit sein muss. Oder liege ich falsch?

LG linuzer

linuzer

Also, nachdem ich diese Funktion jetzt mehrere Tage im Einsatz habe und sie inzwischen vollständig verstanden habe, muss ich mich doch nochmal zu Wort melden, denn die Funktion hat so massive Einschränkungen, dass sie in der Praxis nahezu unbrauchbar wird:

  • die oben erwähnte Beschränkung auf 25 History-Werte taugt bei den meisten Sensoren nur für Durchschnittsberechnungen über wenige Minuten. Gut, man kann sie sehr leicht entfernen, aber das macht es nicht besser...
  • jedes Mal, wenn FHEM neu startet (und das passiert schon beim bloßen Speichern der Konfiguration) vergisst die Funktion konzeptbedingt alle History-Werte. Der resultierende Durchschnitt ist dann mitnichten ein korrekter Durchschnitt über den gewünschten Zeitraum!
  • Apropos Durchschnitt: Die Berechnung liefert ausschließlich bei Sensorwerten, die in regelmäßigen zeitlichen Abständen auftreten einen korrekten Durchschnitt. In allen anderen Fällen (z.B. ein Temperatursensor, der unregelmäßig sendet) produziert sie genauso "gleitenden Unsinn", wie vom Herrn Professor weiter oben kritisiert wurde.
Um die Probleme zu beheben, müsste die Funktion natürlich beim ersten Aufruf erstmal die alten Werte aus dem Logfile lesen, um so von Beginn an eine vollständige History als Berechnungsgrundlage zu haben. Des weiteren muss die Durchschnittsberechnung zeitgewichtet erfolgen. Und natürlich muss die künstliche Beschränkung auf 25 Einträge raus.

So, lange Reder, kurzer Sinn, hier ist die vollständig überarbeitete Funktion, die so ziemlich in allen Lebenslagen einen korrekten gleitenden Durchschnitt berechnet:


package main;

use strict;
use warnings;
use Time::Piece;
use POSIX;

sub
myUtils_Initialize($$)
{
  my ($hash) = @_;
}

###############################################################################
#
#  Moving average
#
#  Aufruf: movingAverage(devicename,readingname,zeitspanne in s,logdevice)
#
###############################################################################
sub movingAverage($$$;$){
   my ($name,$reading,$avtime,$logfile) = @_;
   my $hash = $defs{$name};
   my @new = my ($val,$time) = ($hash->{READINGS}{$reading}{VAL},$hash->{READINGS}{$reading}{TIME});
   my $ctime = Time::Piece->strptime($time, "%Y-%m-%d %H:%M:%S");
   my $btime = $ctime - $avtime;
   my $num;
   my $arr;
   my $average;
   #-- initialize if requested
   if( ($avtime eq "-1") ){
     $hash->{READINGS}{$reading}{"history"}=undef;
     return "$name:$reading initialized.";
   }
   
   #-- test for existence
   if( !$hash->{READINGS}{$reading}{"history"}){
      #-- the first time build the array from the logfile
      if( defined $logfile) {
         my $lbtime = $btime->strftime("%Y-%m-%d_%H:%M:%S");
         (my $letime = $time)  =~ s/ /_/;
         my $oldverb = $attr{global}{verbose};
         $attr{global}{verbose} = 0;
         my @logdata = split("\n", fhem("get $logfile - - $lbtime $letime $name:$reading"));
         $attr{global}{verbose} = $oldverb;
         foreach (@logdata){
            my @line = split(" ", $_);
            if(defined $line[1] && "$line[1]" ne ""){
               $line[0] =~ s/_/ /;
               my @tmp = my ($v,$t) = ($line[1],$line[0]);
               push(@{$hash->{READINGS}{$reading}{"history"}},\@tmp);
            }
         }
         $num = int(@{$hash->{READINGS}{$reading}{"history"}});
         Log 3,"movingAverage array for $name:$reading read from log, $num entries";
      }
   } else {
      $arr=\@{$hash->{READINGS}{$reading}{"history"}};
      my $stime = Time::Piece->strptime($arr->[0][1], "%Y-%m-%d %H:%M:%S");
      if($stime>$btime){
         push(@{$hash->{READINGS}{$reading}{"history"}},\@new);
      } else {
         my $old = shift(@{$hash->{READINGS}{$reading}{"history"}});
         my $xtime = Time::Piece->strptime($old->[1], "%Y-%m-%d %H:%M:%S");
         until( $xtime >$btime ){
            $old = shift(@{$hash->{READINGS}{$reading}{"history"}});
            $xtime = Time::Piece->strptime($old->[1], "%Y-%m-%d %H:%M:%S");
         }
      }
   }
   push(@{$hash->{READINGS}{$reading}{"history"}},\@new);
   #-- output and time-weighted average
   $arr=\@{$hash->{READINGS}{$reading}{"history"}};
   $num = int(@{$hash->{READINGS}{$reading}{"history"}});
   if($num > 1){
      my $SumSec = 0;
      my $SumProd = 0;
      for(my $i=1;$i<$num;$i++){
         my $OldTime = Time::Piece->strptime($arr->[$i-1][1], "%Y-%m-%d %H:%M:%S");
         my $Sec = Time::Piece->strptime($arr->[$i][1], "%Y-%m-%d %H:%M:%S") - $OldTime;
         $SumProd += $arr->[$i][0] * $Sec;
         $SumSec += $Sec;
         #Log 3,"[$name moving average] Value = ".$arr->[$i][0]." Time = ".$arr->[$i][1];
      }
      $average=sprintf( "%5.3f", $SumProd/$SumSec);
   } else {
      $average=sprintf("%5.3f", $val);
   }
   Log 3,"[$name moving average] calculated over $num values is $average"; 
   return $average;
}

1;


Vielleicht kann sie jemand ins Wiki übernehmen, ich habe dazu nicht die Berechtigung.

LG
linuzer

justme1968

das dein fhem beim config speichern neu startet ist ein nebeneffekt der (nicht mehr empfohlen) art wie die die config bearbeitest. aber das ist eine andere diskussion.

unabhängig davon ist der ansatz die alten readings aus dem log zu holen meiner meinung nach immer noch falsch. das funktioniert z.b. nicht wenn die werte log file übergreifend gebraucht werden (jahres oder monats wechsel) oder wenn man die originale gar nicht loggen will.

der von fhem hierfür vorgesehene mechanismus ist einen reading namen zu verwenden der mit einem . beginnt. der ist unsichtbar und wird aber trotzdem mit gespeichert. damit kann man entweder alle nötigen werte direkt in readings ablegen oder z.b. im global SAVE event und/oder global SHUTDOWN wie oben vorgeschlagen alles in ein einziges reading serialisieren. bei global INITIALIZED holt man sich die werte dann wieder.

auch auf das $hash->{READINGS} direkt zuzugreifen ist eigentlich unsauber.

den wiki zugang bekommst du ohne probleme.

gruss
  andre
hue, tradfri, alexa-fhem, homebridge-fhem, LightScene, readingsGroup, ...

https://github.com/sponsors/justme-1968

linuzer

Naja, mein Punkt war, wenn FHEM neu startet, ist die History weg, das ist einfach so. Aber off-topic würde mich interessieren, wie ist denn die empfohlene Art, die Config zu ändern?

Und wenn jemand die Werte gar nicht loggen will, mag das ja so sein, aber dann muss er halt damit leben, dass sich das System erst wieder "warmlaufen" muss. Aber dafür dann einen neuen, eigenen "Logging-Mechanismus" zu erfinden indem man irgend etwas serialisiert ist in meinen Augen widersinnig, dann kann ich die Werte, die ich brauche auch direkt ins Log schreiben. Und die Werte in ein wöchentlich oder monatlich wechselndes Log*file* zu schreiben, führt nicht nur hier zu Problemen beim Wechsel. Die Lösung ist halt eine Log*DB* zu verwenden, damit funktioniert es auch hier.

LG
linuzer

justme1968

es gibt keinen grund das config file von hand zu editieren. wenn du alles über das web frontend oder per telnet machst wird fhem niemals neu gestartet. ist aber wie gesagt off-topic. es gibt schon viele diskussionen darüber.

ich würde es nicht wiedersinnig nennen wenn es unterm strich mit deutlich mit weniger code zeilen auskommt und dabei auch noch die angesprochenen nachteile vermeidet. aber wie gesagt: es ist nur meine meinung.

gruss
  andre
hue, tradfri, alexa-fhem, homebridge-fhem, LightScene, readingsGroup, ...

https://github.com/sponsors/justme-1968

Prof. Dr. Peter Henning

Wir leben in einem freien Land, möge jeder verwenden, was er für besser hält. Darüber hinaus liefert diese neue Funktion natürlich ebenfalls gleitenden Unsinn für nicht äquidistante Messungen, weiil sie kein ordentliches Interpolationsmodell hat.
LG
pah

Rampler

Hallo zusammen,
ich weiß, der Thread ist schon etwas älter...
Habe mal beide Versionen vom "moving average" getestet.
Was ich nicht verstehe, warum ich pro Änderung des Reading vier Log-Zeilen bekomme..
Hier mein userreading:
brightness.av {movingAverage("Sonnensensor","brightness",900)}

und hier der logauszug:
2016.08.26 10:56:54 3: [Sonnensensor moving average] calculated over 46 values is 99177.841
2016.08.26 10:56:54 3: [Sonnensensor moving average] calculated over 39 values is 100096.628
2016.08.26 10:56:54 3: [Sonnensensor moving average] calculated over 41 values is 100096.628
2016.08.26 10:56:54 3: [Sonnensensor moving average] calculated over 43 values is 100096.628


Viele Grüße
   Klaus
3 HMUART (2 via ESP8266), 1 DUOFERN, 12 ESP8266, SolvisBen, GoodWE WR, RPI2 (Bullseye), ZWAVE, HM-Classic, und hoch zufrieden ...
Danke an alle, die was dazu beigetragen haben !!