Stromzähler und 1-Wire, OWServer, OWDevice

Aus FHEMWiki

Dieser Artikel beschreibt eine Anbindung zweier Leistungsmesser (aka Stromzähler) mit S0 Ausgang über einen 1-wire-S0-Zähler an FHEM.

Das Ziel ist anstatt des monatlichen manuellen Ablesens des Einspeisezählers, eine granulare und automatisierte Aufzeichnung der Leistung einer Photovoltaikanlage zu erhalten.

Fhemwiki-grafik-s0counter-und-onewire.gif

Zwischen die beiden Wechselrichter und dem Einspeisezähler sind je ein digitaler Stromzähler mit S0-Ausgang geschaltet. Einer dieser Stromzähler liefert 2000 Impulse pro kWh, der andere 1000 Impulse pro kWh.

Diese beiden Stromzähler sind mit einem auf dem 1-Wire Baustein DS2423 basierenden Dual S0 Counter verbunden, der hier über einen USB-Busmaster als serielles Interface mit einer Instanz eines OWServers verbunden ist.

In FHEM ist dieser OWServer wie folgt definiert:

define myOWServer OWServer 192.168.1.12:4304

Es wird hier ein OWServer definiert, der über die IP-Adresse 192.168.1.12 erreichbar ist und dort auf dem Port 4304 auf Anfragen lauscht.

Ist in FHEM das "autocreate" Flag gesetzt, dann findet FHEM den S0-Counter und erzeugt beispielsweise die folgenden Einträge:

define DS2423_58E50F000000 OWDevice 1D.58E50F000000 60
attr DS2423_58E50F000000 model DS2423
attr DS2423_58E50F000000 room OWDevice 

Unter "DS2423_58E50F000000" kann man sich nicht direkt etwas vorstellen und anstatt alle 60 Sekunden ein Update zu holen, sollten auch 5 Minuten (=300 Sekunden) reichen. Daher wird das abgeändert zu:

define PVZaehler OWDevice 1D.58E50F000000 300
attr PVZaehler model DS2423
attr PVZaehler room OWDevice 

Dann definiert man noch ein Logfile dazu:

define FileLog_PVZaehler FileLog ./log/PVZaehler-%Y.log PVZaehler:.*
attr FileLog_PVZaehler logtype text

und findet dort Einträge der Form

...
2014-01-20_16:09:55 PVZaehler counters.A: 282478
2014-01-20_16:09:55 PVZaehler counters.B: 139840
2014-01-20_16:14:58 PVZaehler counters.A: 282478
2014-01-20_16:14:58 PVZaehler counters.B: 139841
...

Wie man sieht, erscheinen für jeden der beiden Counter in diesem 1-Wire Device eine eigene Zeile mit dem aktuellen Wert des Zählers. Da der erste S0-Zähler 2000 Impulse pro kWh liefert, ist der Wert für "counters.A" auch etwa das Doppelte von "counters.B", dessen zugehöriger S0-Zähler ja nur 1000 Impulse pro kWh liefert.

Eigentlich wäre es wünschenswert, nur eine Zeile mit beiden Werten zu loggen, diese mit der passenden Einheit Watt-Stunden (Wh) zu bezeichnen und auch noch die Summe der beiden Werte aufzuzeichnen.

Die "state" Zeile im Reading des Devices

Owdevice-pvzaehler-reading.png

beinhaltet die Werte beider Counter, aber das "state" Reading verursacht laut commandref/OWDevice keinen Event, den man in einer FileLog Definition einfach verwenden könnte.


Es muss also etwas mehr getan werden. Die Idee ist, ein Dummy-Device zu definieren und dazu ein passendes Notify, das auf die "PVZaehler" Events reagiert und den Wert an dieses Dummy-Device weitergibt, in dessen Filelog die Daten dann aufgezeichnet werden.

Im Einzelnen:

define PVErtrag dummy 

definiert das Dummy-Device, das die Erträge der Anlage erhalten soll.

Mit

1:  define notify_PVErtrag notify PVZaehler:counters\.A.*  { \
2:      my $a = ReadingsVal("PVZaehler","counters.A",0)/2 ;; \
3:      my $b = ReadingsVal("PVZaehler","counters.B",0) ;; \
4:      my $c = $a+$b ;; \
5:      my $text = "WR1: $a Wh, WR2: $b Wh, Gesamt: $c Wh" ;; \
6:      fhem ("set PVErtrag $text") ;; \
7:     } 

werden die Daten erzeugt und an das PVErtrag Device geschickt.

In Zeile 1 wird das Notify definiert: "PVZaehler" ist das Device, auf dessen Events reagiert werden soll und "counters\.A.*" der reguläre Ausdruck, auf den das Notify reagieren soll. Der Punkt in "counters.A" ist mit einem Backslash maskiert, da er sonst als Metazeichen für den regulären Ausdruck interpretiert werden würde. Das ".*" bezeichnet x-beliebigen Text.

Wenn nun also das PVZaehler-Device einen Event verursacht, der die Zeichenkette "counters.A" enthält, dann wird der in geschweiften Klammern stehende Perl-Code ausgeführt. Dabei dienen die Backslashes jeweils am Zeilenende als Hinweis für FHEM, dass der Perl-Code noch nicht zu Ende ist, sondern in der nächsten Zeile fortgeführt wird. Ebenso sind entsprechend der commandref/perl die Semikolons in der fhem.cfg verdoppelt. Zeile 2 definiert eine Variable $a und weist ihr den Wert des "counters.A" geteilt durch 2 zu; in Zeile 3 erhält die Variable $b den Wert des "counters.B". In Zeile 4 werden die beiden Werte addiert und in Zeile 5 schliesslich zu einem Ausgabetext zusammengesetzt. Zeile 6 sendet dann diesen Ausgabetext an das PVErtrag Device.

Schliesslich wird mit

define FileLog_PVErtrag FileLog ./log/FileLog_PVErtrag-%Y-%m.log PVErtrag
attr FileLog_PVErtrag logtype text 

das Filelog erzeugt, in dem die Daten abgelegt werden. Hier ein Auszug aus diesem FileLog:

...
2014-01-21_13:15:22 PVErtrag WR1: 141666 Wh, WR2: 140271 Wh, Gesamt: 281937 Wh
2014-01-21_13:20:22 PVErtrag WR1: 141677 Wh, WR2: 140282 Wh, Gesamt: 281959 Wh
2014-01-21_13:25:22 PVErtrag WR1: 141687.5 Wh, WR2: 140293 Wh, Gesamt: 281980.5 Wh
2014-01-21_13:30:22 PVErtrag WR1: 141697 Wh, WR2: 140302 Wh, Gesamt: 281999 Wh
2014-01-21_13:35:22 PVErtrag WR1: 141705.5 Wh, WR2: 140311 Wh, Gesamt: 282016.5 Wh
... 


Berechnen, Loggen und Plotten von Strom- und Gasverbrauchswerten

Abweichend von der Verwendung als Ertragszähler hier ein Beispiel mit "userreadings":

Quellle für die userreadings und die Umrechnung: Beitrag

Eintrag für die benutzerdefinierten Readings und Attribute(CFG):

attr global userattr offset offsetA offsetB 

Die Def. des Zählers mit der Berechnung der Verbrauchswerte (CFG):

define OW_Strom_Gas_Zaehler OWDevice 1D.58E50F000000 300
attr OW_Strom_Gas_Zaehler IODev myOWServer
attr OW_Strom_Gas_Zaehler model DS2423
attr OW_Strom_Gas_Zaehler offsetA 25003.91
attr OW_Strom_Gas_Zaehler offsetB 12287.94
attr OW_Strom_Gas_Zaehler polls counters.A,counters.B
attr OW_Strom_Gas_Zaehler room 1_Energie
attr OW_Strom_Gas_Zaehler stateFormat { sprintf("%.3f kWh %.3f m³", ReadingsVal("OW_Strom_Gas_Zaehler","displayA","kWh"), ReadingsVal("OW_Strom_Gas_Zaehler","displayB","m³"));; }
attr OW_Strom_Gas_Zaehler userReadings displayA {ReadingsVal("OW_Strom_Gas_Zaehler","counters.A",0)/75.0+AttrVal("OW_Strom_Gas_Zaehler","offsetA",0);;}, consumption_power differential {ReadingsVal("OW_Strom_Gas_Zaehler","counters.A",0)*3600*0.01333333333333;;}, daily_power {ReadingsVal("OW_Strom_Gas_Zaehler","displayA",0)-Value("Dum_DP_Diff_D");;}, displayB {ReadingsVal("OW_Strom_Gas_Zaehler","counters.B",0)/100.0+AttrVal("OW_Strom_Gas_Zaehler","offsetB",0);;}, consumption_gas differential {ReadingsVal("OW_Strom_Gas_Zaehler","counters.B",0)*3600*0.01;;}, daily_gas {ReadingsVal("OW_Strom_Gas_Zaehler","displayB",0)-Value("Dum_DG_Diff_D");;} 

Hier müssen wie bereits oben beschrieben die Zählerkonstanten eingetragen werden - im Beispiel ist:

"counters.A" (Strom): "75.0" , was 75 Impulse für 1 KWH entspricht, "0.01333333333333" ist nichts weiter als "1 durch 75 (Imp.)"

"counters.B" (Gas)  : "100.0" , was 100 Impulse für 1 m³ entspricht, "0.01" ist nichts weiter als "1 durch 100 (Imp.)"


Autom. Berechnung der Offset- Werte für die realen Zählerstände "DisplayA/B" (CFG): Diese Funktion setzt automatisch die "Offset"- Werte, indem man den aktuellen Zählerstand in das entspr. Eingabefeld einträgt und abschickt. Man hat dann mit der Anzeige des "Sollzählerstandes" in FHEM einen Vergleichswert um zu prüfen, ob die Energiezählung richtig arbeitet oder ob es Ausfälle gab.

# Dummys zum Eingeben der realen Zählerstände:
define Offset_A dummy
attr Offset_A alias Berechn. Offset A
attr Offset_A comment Eingabefeld für Zählerstand Strom zur Berechnung von "offsetA"
attr Offset_A group OWDevice
attr Offset_A room 1_Energie
attr Offset_A setList Zählerstand_Strom:textField
attr Offset_A webCmd Zählerstand_Strom

define Offset_B dummy
attr Offset_B alias Berechn. Offset B
attr Offset_B comment Eingabefeld für Zählerstand Strom zur Berechnung von "offsetB"
attr Offset_B group OWDevice
attr Offset_B room 1_Energie
attr Offset_B setList Zählerstand_Gas:textField
attr Offset_B webCmd Zählerstand_Gas

# Funktion zum Berechnen und Setzen der Offset- Werte:
define Func_SetOffset_N notify Offset_A|Offset_B  { \
\
  my $aufrufer_SetOffset = "$NAME" ;;\
  my $valueA = (ReadingsVal("OW_Strom_Gas_Zaehler","counters.A",0)/75.0);;\
  my $valueB = (ReadingsVal("OW_Strom_Gas_Zaehler","counters.B",0)/100.0);;\
  my $setoffsetA = (ReadingsVal("Offset_A","Zählerstand_Strom",0) - $valueA);;\
  my $setoffsetB = (ReadingsVal("Offset_B","Zählerstand_Gas",0) - $valueB);;\
\
 if ($aufrufer_SetOffset eq "Offset_A") { \
     Log 1, "Setze OW_Strom_Gas_Zaehler.offsetA auf: $setoffsetA";; \
     fhem("attr OW_Strom_Gas_Zaehler offsetA $setoffsetA");;\
     fhem("set Offset_A $setoffsetA");;\
 } \
 if ($aufrufer_SetOffset eq "Offset_B") { \
     Log 1, "Setze OW_Strom_Gas_Zaehler.offsetB auf: $setoffsetB";; \
     fhem("attr OW_Strom_Gas_Zaehler offsetB $setoffsetB");;\
     fhem("set Offset_B $setoffsetB");;\
 }\
                                             { \
     fhem("save");;\
 }\
}
attr Func_SetOffset_N group Berechnungen
attr Func_SetOffset_N room 1_Energie 

Auch hier müssen die Zählerkonstanten noch einmal angepasst werden in den Variablen "$valueA" und "$valueB".

Dummies und Aufrufe zur Berechnung des Energieverbrauchs Tag / Monat (CFG):

# Dummys für Daily
define Dum_DP_Diff_D dummy
attr Dum_DP_Diff_D group Berechnungen
attr Dum_DP_Diff_D room 1_Energie

define Dum_DG_Diff_D dummy
attr Dum_DG_Diff_D group Berechnungen
attr Dum_DG_Diff_D room 1_Energie

define Dum_Daily_Power_D dummy
attr Dum_Daily_Power_D group Berechnungen
attr Dum_Daily_Power_D room 1_Energie

define Dum_Daily_Gas_D dummy
attr Dum_Daily_Gas_D group Berechnungen
attr Dum_Daily_Gas_D room 1_Energie

# Dummys für Monthly
define Dum_MP_Diff_D dummy
attr Dum_MP_Diff_D group Berechnungen
attr Dum_MP_Diff_D room 1_Energie

define Dum_MG_Diff_D dummy
attr Dum_MG_Diff_D group Berechnungen
attr Dum_MG_Diff_D room 1_Energie

define Dum_Monthly_Power_D dummy
attr Dum_Monthly_Power_D group Berechnungen
attr Dum_Monthly_Power_D room 1_Energie

define Dum_Monthly_Gas_D dummy
attr Dum_Monthly_Gas_D group Berechnungen
attr Dum_Monthly_Gas_D room 1_Energie

# Berechnung Energieverbrauch Tag gesamt
define Func_Daily_Energy_A at *23:50:00 {prg_Daily_Stat()}
attr Func_Daily_Energy_A group Berechnungen
attr Func_Daily_Energy_A room 1_Energie

# Berechnung Energieverbrauch Monat gesamt
define Func_Monthly_Energy_A at *23:55:00 {prg_Monthly_Stat()}
attr Func_Monthly_Energy_A group Berechnungen
attr Func_Monthly_Energy_A room 1_Energie 


Funktionen zum Berechnen und Loggen der Tages- und Monatswerte (99_myUtils.pm)

# Energie Tagesverbräuche berechnen
###################################
sub
prg_Daily_Stat()
 {
 # Zunächst Tagesverbrauch (aktueller Wert - Wert von gestern) berechnen
   $data{DPval} = ReadingsVal("OW_Strom_Gas_Zaehler","displayA",0) - Value("Dum_DP_Diff_D");
   $data{DGval} = ReadingsVal("OW_Strom_Gas_Zaehler","displayB",0) - Value("Dum_DG_Diff_D");
 # Tagesverbrauch in DailyPower speichern
   fhem ("set Dum_Daily_Power_D $data{DPval}");
   fhem ("set Dum_Daily_Gas_D $data{DGval}");
 # Aktuellen Wert in Daily_Power_Diff speichern (entspricht Zählerstand am Tagesende)
   $data{DPval} = ReadingsVal("OW_Strom_Gas_Zaehler","displayA",0);
   $data{DGval} = ReadingsVal("OW_Strom_Gas_Zaehler","displayB",0);
   fhem ("set Dum_DP_Diff_D $data{DPval}");
   fhem ("set Dum_DG_Diff_D $data{DGval}");
   fhem("save");
}

# Energie Monatsverbräuche berechnen
####################################

 # Funktion wie bei Tagesverbrauch, wird jedoch nur zum Monats- und Jahresumbruch ausgeführt

sub
prg_Monthly_Stat()
 {
  my ($sec,$min,$hour,$mday,$month,$year,$wday,$yday,$isdst) = localtime;
  my ($sec2,$min2,$hour2,$mday2,$month2,$year2,$wday2,$yday2,$isdst2) = localtime(time + (60*60*24) );
  if ( $month2 > $month or $year2 > $year ){
   $data{MPval} = ReadingsVal("OW_Strom_Gas_Zaehler","displayA",0) - Value("Dum_MP_Diff_D");
   $data{MGval} = ReadingsVal("OW_Strom_Gas_Zaehler","displayB",0) - Value("Dum_MG_Diff_D");
   fhem ("set Dum_Monthly_Power_D $data{MPval}");
   fhem ("set Dum_Monthly_Gas_D $data{MGval}");
   $data{MPval} = ReadingsVal("OW_Strom_Gas_Zaehler","displayA",0);
   $data{MGval} = ReadingsVal("OW_Strom_Gas_Zaehler","displayB",0);
   fhem ("set Dum_MP_Diff_D $data{MPval}");
   fhem ("set Dum_MG_Diff_D $data{MGval}");
   fhem("save");
   Log 1, "Monatsber. Energie erfolgt: Monatsumbruch $month2 > $month oder Jahresumbruch $year2 > $year erfüllt";
   } 
} 


Log- und Plotzuweisungen (CFG):

define Log_Energy_M FileLog ./log/OW_Strom_Gas_Zaehler-%Y-%m.log OW_Strom_Gas_Zaehler:counters.A.*|OW_Strom_Gas_Zaehler:counters.B.*|OW_Strom_Gas_Zaehler:displayA.*|OW_Strom_Gas_Zaehler:displayB.*|OW_Strom_Gas_Zaehler:consumption_power.*|OW_Strom_Gas_Zaehler:consumption_gas.*|OW_Strom_Gas_Zaehler:daily_power.*|OW_Strom_Gas_Zaehler:daily_gas.*
attr Log_Energy_M logtype stromverbrauch:Strom,gasverbrauch:Gas,text:Text
attr Log_Energy_M room 4_Logdaten

define Log_Energy_Y FileLog ./log/OW_Strom_Gas_Zaehler-%Y.log Dum_DP_Diff_D.*|Dum_DG_Diff_D.*|Dum_Daily_Power_D.*|Dum_Daily_Gas_D.*|Dum_MP_Diff_D.*|Dum_MG_Diff_D.*|Dum_Monthly_Power_D.*|Dum_Monthly_Gas_D.*
attr Log_Energy_Y logtype energie_monat:Monat,energie_jahr:Jahr,text:Text
attr Log_Energy_Y room 4_Logdaten

define 00_Stromverbrauch SVG Log_Energy_M:stromverbrauch:CURRENT
attr 00_Stromverbrauch label "Min $data{min1}, Max $data{max1}, Last $data{currval1}"
attr 00_Stromverbrauch room 1_Energie

define 01_Gasverbrauch SVG Log_Energy_M:gasverbrauch:CURRENT
attr 01_Gasverbrauch label "Min $data{min1}, Max $data{max1}, Last $data{currval1}"
attr 01_Gasverbrauch room 1_Energie

define 02_Energie_Monat SVG Log_Energy_Y:energie_monat:CURRENT
attr 02_Energie_Monat fixedrange month
attr 02_Energie_Monat label "Min $data{min1}, Max $data{max1}, Last $data{currval1}"
attr 02_Energie_Monat room 1_Energie

define 03_Energie_Jahr SVG Log_Energy_Y:energie_jahr:CURRENT
attr 03_Energie_Jahr fixedrange year
attr 03_Energie_Jahr label "Min $data{min1}, Max $data{max1}, Last $data{currval1}"
attr 03_Energie_Jahr room 1_Energie 

Plotfile Strom (stromverbrauch.gplot):

# Created by FHEMWEB, 2013-05-16 21:45:00
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 'Stromverbrauch Tag'
set ytics 
set y2tics 
set grid ytics
set ylabel "Momentan kW"
set y2label "Summiert kWh"

#FileLog 4:OW_Strom_Gas_Zaehler.consumption_power\x3a::
#FileLog 4:OW_Strom_Gas_Zaehler.daily_power\x3a:: 

Plotfile Gas (gasverbrauch.gplot):

# Created by FHEMWEB, 2013-05-16 21:45:00
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 'Gasverbrauch Tag'
set ytics 
set y2tics 
set grid ytics
set ylabel "Momentan m³"
set y2label "Summiert m³"

#FileLog 4:OW_Strom_Gas_Zaehler.consumption_gas\x3a::
#FileLog 4:OW_Strom_Gas_Zaehler.daily_gas\x3a::

plot "<IN>" using 1:2 axes x1y1 title 'Momentan m³' ls l2fill lw 0.5 with lines,\
     "<IN>" using 1:2 axes x1y2 title 'Summiert m³' ls l1 lw 1 with lines,\
 

Plotfile Monat (energie_monat.gplot):

# Created by FHEM/98_SVG.pm, 2014-02-15 18:52:52
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 'Energieverbrauch Monat'
set ytics 
set y2tics nomirror
set grid ytics
set ylabel "Stromverbrauch kWh"
set y2label "Gasverbrauch m³"

#FileLog 3:Dum_Daily_Power_D.*:0:
#FileLog 3:Dum_Daily_Gas_D.*:0:
#FileLog 3:Dum_Daily_Gas_D.*:0:

plot "<IN>" using 1:2 axes x1y1 title 'Strom kWh' ls l3fill lw 1 with bars,\
     "<IN>" using 1:2 axes x1y2 title 'Gas m³' ls l2fill lw 1 with points
     "<IN>" using 1:2 axes x1y2 title '' ls l2 lw 1 with lines 

Plotfile Jahr (energie_jahr.gplot):

# Created by FHEM/98_SVG.pm, 2014-05-02 10:04:37
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 'Energieverbrauch Jahr'
set ytics 
set y2tics nomirror
set grid ytics
set ylabel "Stromverbrauch kWh"
set y2label "Gasverbrauch m³"

#FileLog 3:Dum_Monthly_Power_D.*:0:
#FileLog 3:Dum_Monthly_Gas_D.*:0:

plot "<IN>" using 1:2 axes x1y1 title 'Strom kWh' ls l3fill lw 1 with bars,\
     "<IN>" using 1:2 axes x1y2 title 'Gas m³' ls l2fill lw 1 with fsteps