FHEM: Eule unter Strom

2015-06-13_fhem_owlSchon seit Weihnachten letzten Jahres wohnt eine Eule bei uns – genauer gesagt ein OWL +USB Energiemonitor. Einmal im Jahr kommt immer die große Energiekostenabrechnung und dann befragt man immer wieder die Glaskugel, wohin denn nur der ganze teure Strom fließt. Dieses tolle Gerät sollte helfen, unsere Energiekosten etwas besser – in Echtzeit – im Blick zu haben. Allerdings ist das Display der Eule nicht gerade der optische Bringer, der eine zentrale Ausstellung im Wohnzimmer rechtfertigt. Für uns Nerds schon, aber so manche Frau verfolgt ja ein „schöner wohnen“-Konzept. Doch zum Glück gibt es FHEM …

Die Hardware

Ich als Hobbybastler stelle mich unerschrocken einem jedem Klingeldraht, der mir in die Quere kommt. Bei Starkstromkabeln bin ich aber dann doch etwas zurückhaltender. Genau hier ist aber der einzig sinnvolle Punkt, den Stromverbrauch einer Wohnung zu messen. Damit ich meine Ängste nicht überwinden muss, entschied ich mich zum Kauf der Eule. Zum Messgerät führen drei Kabel, an deren Enden je eine Spule mit einem Clip befestigt ist. Jede Spule wird um jeweils eine der drei Phasen der Hauptstromleitung ge-clip-t – Das Wunder der Induktion macht die restliche Arbeit. Das Messgerät und die Anzeige sind drahtlos miteinander verbunden. Soweit der elektrotechnische Voodoo-Zauber, der leider seine Grenzen in der Optik der Anzeige fand. Diese hat jedoch eine USB-Schnittstelle …

Module (a.k.a. Treiber)

Ein nicht mehr ganz so aktuelles Projekt bei Sourceforge beschäftigte sich mit dieser USB-Schnittstelle. Es stellt ein Kernel-Modul zur Verfügung, über welches die OWL-Devices angesprochen werden können. Aus diesem Projekt schnappte ich mir den aktuellsten Download (v2.3_v0.3_linux_driver) und beförderte diesen auf meinen Banana Pi. Dort entpackte ich das tgz-File und fand ein paar vorgefertigte Module, Sourcen und Skripte. Von meinen längst vergangenen Kernel-Selbstbau-Zeiten steckte noch ein wenig Knoff-Hoff in meinem Kopf, so dass ich relativ schnell zu einem brauchbaren und passenden Treibermodul kam:

# Die Kernelversion herausfinden …
uname -a
> Linux bananapro2 3.4.108-bananian

# Kernel-Header in dieser Version und Compiler besorgen …
apt-get install linux-headers-3.4.108-bananian gcc make

# die passenden Sourcen für den Banana Pi nutzen
cp bananapi_cp210x_driver/cp210x.c.3.4.104 cp210x.c

# und das Modul bauen lassen
./clean.sh
./build.sh

# das Resultat „installieren“ und „automatisch starten lassen“
cp cp210x.ko /lib/modules/3.4.108-bananian/kernel/drivers/char/
echo cp210x >> /etc/modules

Wenn alles passt (und das tut es nach kleinen Änderungen am 20.11.2015) lässt sich das neue Modul kommentarlos via „modprobe cp210x“ laden und ein „dmesg“ wird so etwas berichten wie zum Beispiel:

USB Serial support registered for cp210x
cp210x 2-1.1:1.0: cp210x converter detected
usb 2-1.1: reset full-speed USB device number 3 using sw-ehci
usb 2-1.1: cp210x converter now attached to ttyUSB0

FHEM-Modul 60_CM160.pm

Einem Douglas-Adams-mäßig großartigen Gleichnis folgend hat somit die Banane die Eule gefunden ;-). Nun muss FHEM noch davon überzeugt werden, von diesen neuen Möglichkeiten Gebrauch zu machen. Darüber sinnierte einst ein Entwickler namens Dirk im FHEM-Forum. Das was er produzierte ist nach heutigen Gesichtspunkten nicht mehr ganz konform, produziert aber Ergebnisse mit denen man weiterarbeiten kann. Das meintechblog.de stellt das notwendige CM160-Modul zum Download zur Verfügung. Nach dem Auspacken ist die Moduldatei aus dem Pfad „CM160/60_CM160.pm“ in den Pfad „FHEM“ des FHEM-Verzeichnisbaumes zu befördern. Da die Rückmeldung des Modules nicht immer einwandfrei ist, habe ich mir einen Workaround mit erweitertem Feature-Set gebaut.

Aber fangen wir mit den Basics an:

# Das ist meine Eule. Die darf nicht loggen.
define OWL_stromzaehler CM160 /dev/ttyUSB1
attr OWL_stromzaehler DbLogExclude .*
attr OWL_stromzaehler loglevel 6

# Das ist die Vertretung meiner Eule. Hier wird geloggt.
define stromzaehler dummy
attr stromzaehler event-min-interval leistung:300
attr stromzaehler event-on-change-reading leistung

# In diesem Device pflege ich eine History
define zusammenfassung_stromzaehler

# In dieser Funktion passiert Voodoo
define update_stromzaehler at +*00:00:05 {\
[ siehe folgender Absatz ]
}
attr update_stromzaehler DbLogExclude .*

[Update 02.04.2016] Die Eule selbst loggt die Daten so, wie ich sie nicht benutzen kann. Deswegen mache ich sie kurzer Hand mundtot, indem ich sie vom Datenbanklog komplett ausschließe. Das „loglevel 6“ sorgt dafür, dass auch im Hauptlog nicht alle paar Sekunden eine wertlose Zeile landet. Auch die Voodoo-Funktion sollte mit einem „DbLogExclude“ versehen werden, sonst verewigt sich diese alle 5 Sekunden in der Datenbank.

Die Voodoo-Funktion wird alle 5 Sekunden aufgerufen. Sie befüllt den Dummy „stromzaehler“ mit Werten, die sie von der Eule ausliest oder eigenständig berechnet. Wichtig ist, sich hier an die FHEM/Perl-Syntax zu halten: Es gibt keine Zeilenumbrüche, so dass jede Zeile mit „\“ abgeschlossen werden muss. Weiterhin sind Steuerzeichen zu verdoppeln – daher die „;;“. Auch sind Kommentare und Leerzeilen unzulässig.

Ausbügeln von Unzulänglichkeiten

Neben der Leistung (Momentansicht, in Watt) möchte ich auch den Energieverbrauch (die allseits bekannten kWh) sehen. Die Energie ist das Integral über die Leistung, berechnet sich also aus Leistung * Zeit. Da meine Funktion alle 5 Sekunden rennt und die Induktionsmessung der Eule vermutlich auch fehlerbehaftet ist, ist mir diese Annäherung gut genug.

[Update 02.04.2016] Wenn FHEM startet und die Eule findet, fühlt sich diese leider genötigt, erst mal alles an Daten was sie im Speicher hält – und das ist eine Menge – übermitteln zu müssen. Damit das nicht dazu führt, dass die Funktion unnötig oft aufgerufen wird und die Messwerte  extrem verfälscht, muss man sicherstellen, dass die Eule gerade „live data“ liefert. Man kann es vermutlich sportlicher machen, ich habe aber einfach ein if-Statement eingebaut:

my $mode=InternalVal(„OWL_stromzaehler“,“MODE“,0);;\
if ( $mode eq „live data“ ) {\
[ hier kommt alles weitere rein ]
\}

Der Trick ist nun, die vorherige Leistung gespeichert zu haben. Weiter unten wird dies mit der aktuellen Leistung der Eule – aus „OWL_stromzaehler“ – getan. Den später für den kommenden Durchlauf befüllten Wert „leistung“ im Hilfsdevice „stromzaehler“ lese ich vorab schon einmal aus:

my $leistung_vorher=\
ReadingsVal(„stromzaehler“,“leistung“,0)+0;;\
my $leistung_jetzt=\
ReadingsVal(„OWL_stromzaehler“,“W“,0);;\
$leistung_jetzt =~ s/[^0-9\.]//g;;\

my $timestamp_jetzt=time();;\
my $differenz=$timestamp_jetzt-\
ReadingsVal(„stromzaehler“,“zaehler_leistung_timestamp“,$timestamp_jetzt);;\

Da die Eule immer die Einheit ‚W‘ hinter die Leistung schreibt, bereinige ich die $leistung_jetzt in der dritten Zeile so, dass nur Zahlenwerte und der Trennpunkt überbleiben. Der Wert $differenz sollte eigentlich immer bei 5 liegen. Ich habe diese Zeitstempelabfragen eingebaut, weil ich ursprünglich die Eule bei Änderungen triggern lassen wollte. Das macht das Modul jedoch derzeit nicht zuverlässig. Vielleicht kommt es ja irgendwann noch, dann muss die Funktion nicht alle 5 Sekunden ausgeführt werden.

Aus den nun vorliegenden Werten kann der Energieverbrauch in der Zeitspanne $differenz errechnet werden. Diesen rechne ich gleich von Ws (Wattsekunden) auf kWh (Kilowattstunden) um. Zur Erzeugung aufschlussreicher Plots zähle ich neben dem Gesamtenergieverbrauch auch den des aktuellen Tages:

my $energie_diff_kwh=\
($leistung_vorher*$differenz)/3600/1000;;\
my $energie_tag=$energie_diff_kwh+\
ReadingsVal(„stromzaehler“,“energie_tag“,0);;\
my $energie_gesamt=$energie_diff_kwh+\
ReadingsVal(„stromzaehler“,“energie_gesamt“,0);;\

Nun muss man dem FHEM noch beibiegen, dass es einen Tageswechsel gibt. An diesem wird der Verbrauch des Vortages in mein zusammenfassung_stromzaehler-Device geschrieben, der Energieverbrauch des Tages ($energie_tag) genullt und die Hilfsvariable des letzten geloggten Tages (zaehler_tag) aktualisiert:

my ($s, $mi, $h, $tag_jetzt, $mo, $y, $w, $yd, $st)=\
localtime(time);;\
my $zaehler_tag=ReadingsVal(„stromzaehler“,“zaehler_tag“,0);;\
if ( $tag_jetzt != $zaehler_tag ) {\
fhem(„setreading stromzaehler zaehler_tag $tag_jetzt“);;\
fhem(„setreading zusammenfassung_stromzaehler tag $energie_tag“);;\
$energie_tag=0;;\
}\

Alle relevanten Werte werden zu guter Letzt in den stromzaehler geschrieben:

# Die aktuelle Leistung …
fhem(„setreading stromzaehler leistung $leistung_jetzt“);;\

# Der Gesamtenergieverbrauch und der des Tages …
fhem(„setreading stromzaehler energie_tag $energie_tag“);;\
fhem(„setreading stromzaehler energie_gesamt $energie_gesamt“);;\

# Die Hilfsvariable für die Zeitdifferenz …
fhem(„setreading stromzaehler zaehler_leistung_timestamp $timestamp_jetzt“);;\

# Und ’state‘ noch einmal in schön 😉 …
fhem(„setreading stromzaehler state „.\
sprintf(„%.3f“,$energie_gesamt).“ kWh“);;\

Stromverbrauch des Tages plotten

Nun ergibt sich ein kleines, aber feines Problem: Die obige Funktion schreibt den Tagesenergieverbrauch erst nach dem Tageswechsel ins Log. Er landet dort also mit dem Datum des Folgetages. Hier hilft des FHEM-Modul logProxy. Dieses wird in der  fhem.cfg möglichst weit oben (zum Beispiel unter der DBLog-Definition) durch folgende Zeile aktiviert …

define lp logProxy

… und kann von nun an in ganz FHEM genutzt werden. Ich definiere mein Log des Tagesverbrauches nun in der fhem.cfg wie folgt:

define strom_SVG SVG lp:my_strom:CURRENT
attr strom_SVG fixedrange month

Der große Unterschied zum bisherigen Loggen ist hier das „lp:“, welches den logProxy beim Bearbeiten des gnuplot-Files my_strom.gplot aufruft. In diesem sind die zu plottenden Werte dabei wie folgt zu definieren:

#logProxy DbLog:logdb,offset=-60*60*12:zusammenfassung_stromzaehler:tag

Hierbei darf nach dem Leerzeichen nach #logProxy kein weiteres mehr folgen. Die Zeile sagt im weiteren Verlauf aus, dass ich aus dem DbLog (Schlüsselwort) namens logdb (meine Definition) mit einem Zeitoffset von -12 Stunden (-60 Sekunden * 60 Minuten * 12 Stunden) vom Device zusammenfassung_stromzaehler den Wert tag plotten möchte. Durch die Verschiebung um 12 Stunden lande ich in der Mitte des Vortages und ein Plot als ‚bars‚ sieht schick aus.

Und so sieht das Ganze dann aus …

[Update 02.04.2016] Ich habe eine recht alte Wohnung, die zwei getrennte Stromkreise hat. Darum betreibe ich auch zwei Eulen und habe folglich alles oben beschriebene doppelt konfiguriert. Eine readingsGroup gibt mir erstmal den allgemeinen Überblick:

Überblick über den Energieverbrauch

In einem folgenden Graphen plotte ich den Verbrauch eines jeden Tages des aktuellen Monats …

Energieverbrauch an den Tagen eines Monats

… und letztendlich noch die Entwicklung am aktuellen Tag selbst:

Energieverbrauch und Leistung an einem Tag

Tipps und Hinweise

Wenn das CM160-Modul zum ersten Mal eingebunden wird, werden alle Werte seit dem letzten Kontakt ausgelesen. Dummerweise bekommen diese auch alle den Zeitstempel des Auslesemoments. Damit haben die Graphen eines Plots so ihre liebe Not. Es empfiehlt sich, diese Log-Leichen im MySQL-Log zu entfernen.

Der gemessene Energieverbrauch muss nicht mit dem tatsächlichen Verbrauch übereinstimmen. Hier empfiehlt es sich, die gemessenen Kilowattstunden eines angemessenen Zeitraumes mit dem des geeichten Zählers zu vergleichen. Die Differenz zwischen den Werten kann mit dem Attribut „attr OWL_stromzaehler voltage 195″ der Eule ausgeglichen werden. Hiermit wird der eigentliche Spannungswert von 220V etwas korrigiert.

Weiterführende Links

http://www.meintechblog.de/2014/01/update-smart-metering-mit-fhem-und-owl-usb/: Dieser Blog brachte mich auf die Idee, die Eule einzubinden. Außerdem gibt es viele helfende Hinweise und ist Quelle des Perl-Moduls.

http://sourceforge.net/projects/electricowl/: Das Sourceforge-Projekt, dem die USB-Module entstammen.

http://www.fhemwiki.de/wiki/RFXtrx: Man könnte die Werte der Sensoren auch über diesen 433MHz-Empfänger auslesen.

http://www.fhemwiki.de/wiki/LogProxy: Die ausführliche Beschreibung zum Modul logProxy im FHEM-Wiki.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.