Stromverbrauch pro Monat wird nicht im SVG Plot angezeigt

Begonnen von axel.mohnen, 12 Oktober 2014, 20:01:58

Vorheriges Thema - Nächstes Thema

tupol


axel.mohnen

#16
Hallo Fhem´ler,

ich möchte euch mal meine Lösung zum Thema zeigen.

Ziel:
Ein Jahresplot (fixedrange=year) als Balkendiagramm um den monatlichen Stromverbrauch darzustellen. Dabei sollte der aktuelle Monat in "Echtzeit" angezeigt werden und nicht erst am Ende des Monats.

Lösung:
1) DB (SQLite installiert) mit "Current" und "History" Tabelle
2) Dummy Device angelegt ohne readings
3) Fake DBLOG angelegt das auf das Dummy Device (step 2) verweisst
4) myUtils geschrieben "upd_monthly_dblog()". Diese Methode wird mittels notify getriggert immer wenn sich die Readings meines Stromzählers (SMLUSB) ändern. Der Stromzähler besitzt zusätzliche readings aus dem Statistics Module. Die Methode schreibt einen Record pro Monat in die DB Tabelle "History". Sollte es für den aktuellen Monat bereits einen Eintrag geben, wird Dieser aktualisiert.
Als "Value" lesen ich mir das "statistics" reading (statZählerstand-Bezug-Total) aus dem Stromzähler (SMLUSB).
5) SVG Plot manuell angelegt, das auf den Fake DBLOG und dummy reading verweisst. Leider werden die Dummy readings nicht im SVG Editor (Drop-Down List) mitgegeben. Keine Ahnung warum?

Todo´s:
Das Balkendiagramm mittels logproxy Modul ausrichten (offset). Hat bist jetzt leider noch nicht gefunzt ;-)

Dummy Device:
#----------------------------------------------------------------------
# Monthly Consumption: Dummy Device - House Powermeter
#----------------------------------------------------------------------
define eg.hw.sz.haushalt.dum1 dummy


Fake DBLOG:
#----------------------------------------------------------------------
# Monthly Consumption: DBLog Definition - House Powermeter
#----------------------------------------------------------------------
define eg.hw.sz.haushalt.dblog1 DbLog ./db.conf eg.hw.sz.haushalt.dum1:Monatsverbrauch


Notify:
#----------------------------------------------------------------------
# Monthly Consumption: Notify Definition - House Powermeter
#----------------------------------------------------------------------
define eg.hw.sz.haushalt.not1 notify eg.hw.sz.haushalt { upd_monthly_dblog("eg.hw.sz.haushalt","statZählerstand-Bezug-Total","eg.hw.sz.haushalt.dum1","SMLUSB","Monatsverbrauch") }


SVG Plot:
#----------------------------------------------------------------------
# Monthly Consumption: SVG Definition - House Powermeter
#----------------------------------------------------------------------
define SVG_eg.hw.sz.haushalt.dblog1_1 SVG eg.hw.sz.haushalt.dblog1:SVG_eg.hw.sz.haushalt.dblog1_1:HISTORY
attr SVG_eg.hw.sz.haushalt.dblog1_1 fixedrange year
attr SVG_eg.hw.sz.haushalt.dblog1_1 label "Verbrauch min: $data{min1}, max: $data{max1}, last: $data{currval1}"
attr SVG_eg.hw.sz.haushalt.dblog1_1 plotfunction eg.hw.sz.haushalt.dum1:Monatsverbrauch
attr SVG_eg.hw.sz.haushalt.dblog1_1 room HWR


GPLOT File:
# Created by FHEM/98_SVG.pm, 2014-11-05 20:53:22
set terminal png transparent size <SIZE> crop
set output '<OUT>.png'
set xdata time
set timefmt "%Y-%m-%d_%H:%M:%S"
set xlabel " "
set title '<L1>'
set ytics
set y2tics
set grid
set ylabel "kw/h"
set y2label "kw/h"

#DbLog eg.hw.sz.haushalt.dum1:Monatsverbrauch
plot "<IN>" using 1:2 axes x1y2 title 'Monatsverbrauch' ls l1fill lw 1 with bars


myUtils - upd_monthly_dblog()
sub upd_monthly_dblog($$$$$)
{
# ---------- Local data Definition -------------------------------------------------
my ($readingdev,$readingname,$logdev,$fake_devtype,$fake_readingname) = @_;
my $readingval;
my $readingstring;
my @readingarray;
my $dbh;
my $stmt;
my $sth;
my $rv;
my $row;
my $last_year;
my $last_month;
my $curr_year;
my $curr_month;
#my $curr_timestamp = strftime "%Y-%m-%d %H:%M:%S", localtime;
my $fake_timestamp = strftime "%Y-%m-15 00:00:00", localtime;

# ---------- Check import parameters -----------------------------------------------
if ((!$readingdev) || (!$readingname) || (!$logdev) || (!$fake_devtype) || (!$fake_readingname))
{
return "Import Parameter is missing";
}

# ---------- Retrieve reading from Powermeter --------------------------------------
$readingstring = ReadingsVal($readingdev,$readingname,"0");

if (!$readingstring)
{
return "Couldn´t retrieve Readings:$readingname from device $readingdev";
}
else
{

# ---------- Split reading string by spaces into array -----------------------------
@readingarray = split / /, "$readingstring";
# ---------- Set 5 field (statmonth) as reading value ------------------------------
$readingval = $readingarray[5];
}

# ---------- Build up DB connection ------------------------------------------------
$dbh = build_db_conn();

if (!$dbh)
{
return "DB connection failed";
}

# ---------- Build SQL select in order to retrieve the last record -----------------
$stmt = qq(select max(timestamp), timestamp, device, reading, value from history where device = "$logdev");
# ---------- Prepare SQL statement -------------------------------------------------
$sth = $dbh->prepare( $stmt ) or die "Can't prepare statement: $DBI::errstr";
# ---------- Execute SQL statement -------------------------------------------------
$rv = $sth->execute() or die "Can't execute statement: $DBI::errstr";

$row = $sth->fetchrow_hashref();
$sth->finish;

# ---------- Check DB result -------------------------------------------------------
if (!$row->{TIMESTAMP})
{
# ---------- No record exists, only for the first time -> INSERT record ------------
# ---------- Build SQL statement for insertion -------------------------------------
$stmt = qq(insert into history (timestamp,device,type,event,reading,value)
values ("$fake_timestamp", "$logdev", "$fake_devtype", "$fake_readingname", "$fake_readingname", "$readingval" ));
# ---------- Prepare SQL statement -------------------------------------------------
$sth = $dbh->prepare( $stmt ) or die "Can't prepare statement: $DBI::errstr";
# ---------- Execute SQL statement -------------------------------------------------
$rv = $sth->execute() or die "Can't execute statement: $DBI::errstr";
# ---------- Finish DB Statement ---------------------------------------------------
$sth->finish;

}
else
{
# ---------- At least one record exists --------------------------------------------
# ---------- Check if month has been switched since last record --------------------
$last_year = substr($row->{TIMESTAMP}, 0, 4);
$last_month = substr($row->{TIMESTAMP}, 5, 2);
$curr_year = strftime "%Y", localtime;
$curr_month = strftime "%m", localtime;

# ---------- Check first variables -------------------------------------------------
if (!$last_year)
{
return "Couldn´t retrieve year from timestamp: $row->{TIMESTAMP}";
}

if (!$last_month)
{
return "Couldn´t retrieve month from timestamp: $row->{TIMESTAMP}";
}

if (!$curr_year)
{
return "Couldn´t retieve actual year from localtime";
}

if (!$curr_month)
{
return "Couldn´t retieve actual month from localtime";
}

# ---------- Check if month or years has been changed ------------------------------
if (($last_year != $curr_year) || ($last_month != $curr_month))
{
# ---------- Month has been changed -> Insert record -------------------------------
# ---------- Build SQL statement for insertion -------------------------------------
$stmt = qq(insert into history (timestamp,device,type,event,reading,value)
values ("$fake_timestamp", "$logdev", "$fake_devtype", "$fake_readingname", "$fake_readingname", "$readingval"));
# ---------- Prepare SQL statement -------------------------------------------------
$sth = $dbh->prepare( $stmt ) or die "Can't prepare statement: $DBI::errstr";
# ---------- Execute SQL statement -------------------------------------------------
$rv = $sth->execute() or die "Can't execute statement: $DBI::errstr";
# ---------- Finish DB Statement ---------------------------------------------------
$sth->finish;

}
else
{
# ---------- Month hasn´t been changed -> Update last record -----------------------
# ---------- Build SQL statement for update ----------------------------------------
$stmt = qq(update history set value = "$readingval" where timestamp = "$row->{TIMESTAMP}" and device = "$row->{DEVICE}");
# ---------- Prepare SQL statement -------------------------------------------------  
$sth = $dbh->prepare( $stmt ) or die "Can't prepare statement: $DBI::errstr";
# ---------- Execute SQL statement -------------------------------------------------
$rv = $sth->execute() or die "Can't execute statement: $DBI::errstr";
# ---------- Finish DB Statement ---------------------------------------------------
$sth->finish;
}
}
# ---------- Done -> close DB connection -------------------------------------------
$dbh->disconnect();
}


Gruß Axel

justme1968

eine andere idee das umzusetzen wäre logProxy zu verwenden und die 'alten' readings ganz normal aus FileLog oder DbLog zu plotten und zusätzlich den aktuellen wert 'life' auf dem reading zu holen und mit einer kleinen funktion in den gleichen plot zu setzen. ohne extra fake db.

mehr zu logProxy findest du in der commandref oder z.b. hier: http://www.fhemwiki.de/wiki/LogProxy.

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

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

netbus

Zitat von: justme1968 am 17 November 2014, 23:10:47
eine andere idee das umzusetzen wäre logProxy zu verwenden ... ohne extra fake db.
und diesen monatsgraphen wie axel.mohnen zeigt, kann man mit logproxy auch erzeugen?

justme1968

#19
den monats teil für die vergangenen monate erzeugst du wie sonst auch per average oder statistics oder hourcounter oder was auch immer. loggst sie ganz normal per filelog oder dblog.

nur den einen wert für den aktuellen monat baust du dann per logProxy ein. ohne irgendwelche dummys oder fake log tricks sondern in dem du momentan wert direkt verwendest.

logProxy erzeugt keine daten sondern erlaubt es nur daten aus unterschiedlichen quellen zusammen zu mischen und zu manipulieren.

gruß
  andre
hue, tradfri, alexa-fhem, homebridge-fhem, LightScene, readingsGroup, ...

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

netbus

Zitat von: justme1968 am 18 November 2014, 10:51:47
den monats teil für die vergangenen monate erzeugst du wie sonst auch per average oder statistics oder hourcounter oder was auch immer. loggst sie ganz normal per filelog oder dblog.

nur den einen wert für den aktuellen monat baust du dann per logProxy ein. ohne irgendwelche dummys oder fake log tricks sondern in dem du momentan wert direkt verwendest.

logProxy erzeugt keine daten sondern erlaubt es nur daten aus unterschiedlichen quellen zusammen zu mischen und zu manipulieren.

gruß
  andre
Hallo Andre,
Dein Modul ist leider alles andere als einfach zu verwenden.
Auch über die Wiki werde ich nicht wirklich schlau weil einfach die Beispiele fehlen.
Wie kann ich mein bestehenden Plot auf Monatsplott umstellen?
# Created by FHEM/98_SVG.pm, 2014-11-17 08:14:01
set terminal png transparent size <SIZE> crop
set output '<OUT>.png'
set xdata time
set timefmt "%Y-%m-%d_%H:%M:%S"
set xlabel " "
set title '<L1>'
set ytics
set y2tics
set grid
set ylabel "Leistung"
set y2label "Leistung"

#logProxy  FileLog 3:AktuellerVerbrauch.*::

plot "<IN>" using 1:2 axes x1y2 title 'Watt' ls l1fill lw 0.5 with lines

define FileLog_AktuellerVerbrauch FileLog ./log/AktuellerVerbrauch-%Y.log AktuellerVerbrauch
attr FileLog_AktuellerVerbrauch custom_graph 3:AktuellerVerbrauch@Watt@Leistung
attr FileLog_AktuellerVerbrauch logtype text
attr FileLog_AktuellerVerbrauch room Admin


justme1968

#21
im wiki gibt es eine ganze reihe beispiele. von der einfachsten linie bis zum plotten eines thermostat wochenprofils oder der sonnen auf und untergangszeiten.

was du tun musst:

- log proxy device definieren
- im define deines SVG plots das FileLog device gegen das log proxy device austauschen
- die zugehörige zeile im plot muss so aussehen: #logProxy  FileLog:FileLog_AktuellerVerbrauch:3:AktuellerVerbrauch.*::

damit solltest erst mal wieder einen normalen plot für die vergangenen monate haben.

wenn das geht fügst du die folgende routine in dein 99_myUtils ein:sub
plotCurrent($$$$)
{
  my($from,$to,$device,$reading) = @_;
  my $fromsec = SVG_time_to_sec($from);
  my $tosec   = SVG_time_to_sec($to);

  my $ret = "";

  my $rt = ReadingsTimestamp( $device, $reading, undef );
  return $ret if( !$rt );

  my $sec = time_str2num($rt);
  return $ret if ( $sec < $fromsec || $sec > $tosec );

  my $val = ReadingsVal( $device, $reading, undef );

  my @t = localtime($sec);
  my $timestamp = sprintf("%04d-%02d-%02d_%02d:%02d:%02d", $t[5]+1900, $t[4]+1, $t[3], $t[2], $t[1], $t[0]);

  $ret .= "$timestamp $val\n";
  $ret .= "#plotCurrent: $device:$reading\n";

  return $ret;
}

die schaut on das letzte $reading aus $device im aktuell angezeigten plot zeitraum liegt und bereitet es zum plotten auf. wenn der wert ausserhalb des gerade angezeigten bereiches liegt wird nichts zurückgegeben. du kannst hier noch mit dem alignment des wertes auf ganze minuten, stunden oder tage spielen und auch beim vergleich sekunden, minuten, usw. unberücksichtigt lassen. das hängt alles davon ab wie weit du in den plot hinein zoomen willst.

du kannst das auf der fhem kommandozeile prüfen in dem du die sub direkt aufrufst:{plotCurrent("2014-11-19 00:00:00", "2014-11-19:17:00:00", "AktuellerVerbrauch", "state")}

als letztes baust du noch die folgende zeile für den aktuellen wert in dein plotfile ein:
#logProxy Func:plotCurrent($from,$to,"AktuellerVerbrauch","state")

eine plot... zeile brauchst du natürlich auch:
plot "<IN>" using 1:2 axes x1y2 title 'Watt Aktuell' ls l0fill lw 0.5 with bars

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

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

netbus

Zitat von: justme1968 am 19 November 2014, 11:38:25
- die zugehörige zeile im plot muss so aussehen: #logProxy  FileLog:FileLog_AktuellerVerbrauch:3:AktuellerVerbrauch.*::
damit solltest erst mal wieder einen normalen plot für die vergangenen monate haben.
Leider bekomme ich da einen Fehler
XML-Verarbeitungsfehler: Kein Element gefunden
Adresse: http://192.168.1.50:8083/fhem/SVG_showLog?dev=SVG_FileLog_AktuellerVerbrauch_1&logdev=FileLog_AktuellerVerbrauch&gplotfile=SVG_FileLog_AktuellerVerbrauch_1&logfile=CURRENT&pos=
Zeile Nr. 2, Spalte 1:

justme1968

zeig mir bitte mal eine rein FileLog basierte konfiguration die mit deinem log geht.

gruß
  andre
hue, tradfri, alexa-fhem, homebridge-fhem, LightScene, readingsGroup, ...

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

netbus

#24
define FileLog_AktuellerVerbrauch FileLog ./log/AktuellerVerbrauch-%Y.log AktuellerVerbrauch
attr FileLog_AktuellerVerbrauch custom_graph 3:AktuellerVerbrauch@Watt@Leistung
attr FileLog_AktuellerVerbrauch logtype text
attr FileLog_AktuellerVerbrauch room Admin
define SVG_FileLog_AktuellerVerbrauch_1 SVG FileLog_AktuellerVerbrauch:SVG_FileLog_AktuellerVerbrauch_1:CURRENT
attr SVG_FileLog_AktuellerVerbrauch_1 label "min: $data{min1}, max: $data{max1}, avg: $data{avg1}, Last $data{currval1}"
attr SVG_FileLog_AktuellerVerbrauch_1 room Haus


Jetzt sehe ich einen Graphen,
Ich musste im Plot Editor einmal "write .gplot file" drücken
Wenn ich aber
{plotCurrent("2014-11-19 00:00:00", "2014-11-19:17:00:00", "AktuellerVerbrauch", "state")}
eingebe kommt nichts

justme1968

in welchem reading steht der wert? wenn es nicht state ist musst du natürlich etwas anderes eintragen.
hue, tradfri, alexa-fhem, homebridge-fhem, LightScene, readingsGroup, ...

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

axel.mohnen

Hi Andre und Netbus,

ich denke mal das zur Darstellung eines Balkendiagramms nur ein Log pro Monat weggeschrieben wird?
Zusätzlich kommt noch ein log aus der "plotCurrent" dazu, korrekt?

"./log/AktuellerVerbrauch-%Y.log"

Gruss
Axel

justme1968

du brauchst ein log für die historischen monats werte. plotCurrent wird nicht geloggt sondern holt den aktuellen wert im augenblick des plottens live aus dem device in dem die werte aufsummiert werden.

logProxy kombiniert die daten aus beiden quellen in einen einzigen SVG plot.

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

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

netbus

Zitat von: axel.mohnen am 17 November 2014, 21:49:45
Der Stromzähler besitzt zusätzliche readings aus dem Statistics Module. Die Methode schreibt einen Record pro Monat in die DB Tabelle "History".
Axel,
die Methode mit Statistics hatte ich auch probiert aber ich hatte nur Nullen in den Readings. Der Entwickler des Moduls hat geschrieben dass das Modul anzeigt wie lange der jeweilige state existiert. Mein Stromzähler liefert ab Watt Werte und kein On Off. Was für Werte spuckt dein Stromzähler aus?

axel.mohnen

Hi Netbus,

Hier sind meine Definitionen. Im Anhang findest du ein screenshot von den Stromzähler readings + statistics.
Bis auf ein Rundungsproblem, das bereits vom Entwickler behoben wurde funzt die Statistics sehr gut bei mir.

Definition Stromzähler (SMLUSB)

#----------------------------------------------------------------------
# Powermeter Definition:
#----------------------------------------------------------------------
define eg.hw.sz.haushalt SMLUSB /dev/ttyUSB0
attr eg.hw.sz.haushalt alias Stromzähler Haushalt
attr eg.hw.sz.haushalt event-min-interval *:1800
attr eg.hw.sz.haushalt room HWR


Definition Statistics für Stromzähler:

#----------------------------------------------------------------------
# Statistics Definition: House Powermeter
#----------------------------------------------------------------------
define eg.hw.sz.haushalt.stat statistics eg.hw.sz.haushalt
attr eg.hw.sz.haushalt.stat deltaReadings Zählerstand-Bezug-Total


Gruß
Axel