CSV-Datei als FHEM-Reading einlesen

Immer mehr Hersteller implementieren in ihren Geräten eine Funktion zum Auslesen von Messwerten. Sei es eine Solaranlage oder eine Heizung. Einige Geräte besitzen dafür eine hübsche Weboberfläche. Andere dagegen erlauben das Downloaden der Messwerte als CSV-Datei. Um solche Geräte soll es in diesem Blogbeitrag gehen. Ich werde euch zeigen, wie ihr eine CSV-Datei in FHEM einliest und die Werte als Reading abspeichern könnt. 

Vorbereitung

Ich habe mich dazu entschieden, die Perl-Erweiterung "Text::CSV:Slurp" zu verwenden. Es gibt sicherlich Möglichkeiten, die ohne zusätzliche Erweiterungen auskommen. Sollen jedoch große CSV-Dateien eingelsen werden, kann es oft zu Problemen oder Leistungseinbußen kommen. 

Bevor man also mit dem eigentlichen Einlesen in FHEM beginnen kann, muss die Erweiterung "Text::CSV::Slurp" installiert werden. Dazu einfach per SSH auf euren Raspberry einloggen und folgenden Befehl ausführen.

More...

perl -MCPAN -e'install Text::CSV::Slurp'

Die Installation dauert ein wenig, also nicht wundern. Nachdem die Erweiterung installiert wurde, kann mit der Einrichtung in FHEM begonnen werden.

Einrichtung in FHEM

Damit später aus jedem Device das Einlesen einer CSV-Datei gestartet werden kann, wird die Code dafür in eine Routine ausgelagert. Wie ihr eine Routine anlegt könnt ihr in meinem Blogbeitrag nachlesen. 

##################################################################
# Routine zum Einlesen einer CSV-Datei

#!/usr/bin/perl

package main;

use strict;
use warnings;
use POSIX;
use strict;
use warnings;
use Text::CSV::Slurp;

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

####################################################################
sub readCSV ($$)
{

my ($csvDatei, $Spalte) = @_;
my $slurp = Text::CSV::Slurp->new();

my $Wert;
my $Datum;

# CSV Options - see Text::CSV
my %csv_options = (
sep_char => ';',
binary => '1',
);

# Reference to an array of hashes
my $data = $slurp->load( file => $csvDatei, %csv_options );

foreach my $row (@$data) {
 $Wert = $row->{$Spalte};
}

return $Wert
}
1;

Die Routine "readCSV" verlangt zwei Übergabewerte. Zum einen den Pfad zur CSV-Datei und zum anderen die gewünschte Spalte. Die Routine gibt dann den letzten Wert der gewünschten Spalte als Rückgabewert zurück.

Beispielanwendung - Tagesertrag vom Kaco Wechselrichter

Im folgenden werde ich euch ein Anwendungsfall als Beispiel zeigen. Zum Beispiel ein Wechselrichter der Marke Kaco erlaubt das Auslesen der Tageserträge (in Wh) als CSV-Datei. Die Datei hat folgenden Inhalt:

Hier erkennt man ab Zeile 4 die Tageserträge. Getrennt werden die Einträge in der Datei durch ein ";". In der Routine ist dies ebenfalls angegeben " sep_char => ';' " Sollte eure CSV zum Beispiel ein Komma als Trennungszeichen verwenden, könnt ihr dies in der Routine anpassen. 

Man kann nun vermuten, dass die Spalte mit den Werten den Namen "E[Wh]" hat. Durch den Aufbau der CSV-Datei ist dies jedoch nicht der Fall. Die Spalte mit den Datum hat den Namen "WR-Type" und die Spalte mit den Werten "Seriennummer". Möchte man nun also den Ertragswert vom letzten Eintrag haben, so ruft man die Routine wie folgt auf:

{readCSV("/opt/fhem/201710.csv","Seriennummer")}

Es wird nun die CSV-Datei für den Monat Oktober geladen und der letzte Eintrag der Spalte "Seriennummer" als Rückgabewert zurückgegeben. In diesem Fall als der Ertragswert in Wh vom 19.10.2017. 

Info: Die CSV-Datei muss vorher auf den Raspberry geladen werden. Dies kann entweder manuell erfolgen oder automatisch durch ein entsprechendes Script.

Damit nun alles automatisch gemacht wird, kann man sich nun noch ein at-Device einrichten. Zunächst erstmal die Grunddefinition des at-Devices:

define readCSVat at *00:05:00 a

Das at-Device wird nun jedesmal um 5 Minuten nach Mitternacht aufgerufen. Damit ist sichergegangen, dass das gewünschte CSV-File bereits mit dem Ertragswert der Vortages aktualisiert wird. Die Definition des at-Device wird nun über den DEF-Editor angepasst:

*00:05:00 {
my $CSVstring;;
my $Wert;;
my $csvMonth;;

if ($mday == "1") {
$csvMonth = $month-1;;
if ($csvMonth < "10") {$csvMonth = "0".$csvMonth;;}}
else {
 $csvMonth = $month;
  if ($csvMonth < "10") {$csvMonth = "0".$csvMonth;;}}

$CSVstring = "http://192.168.1.12/2017".$csvMonth.".csv";;
$Wert = readCSV($CSVstring,"Seriennummer");;
fhem("setreading WechselrichterErtrag Ertrag $Wert");;

}

Innerhalb des at-Devices wird zunächst überprüft ob aktuell der 1. eines Monats ist. Ist dies der Fall, wird die  Monatsvariable - 1 genommen, damit der letzte Wert aus der Vormonats-CSV genommen wird. Ist nicht der 1. eines Monats wird der aktueller Monat verwendet. Danach wird der Dateinamen abhängig vom gewünschten Monat generiert. 

Durch die Routine erhält man nun in der Variable ($Wert) den Ertrag des Vortages. Diese kann nun verwendet werden um zum Beispiel ein Reading eines Dummys zu setzen. Wie in diesem Fall das Dummy "WechselrichterErtrag".

Durch ein Logdevice (zum Beispiel dbLog) kann man nun den Wert des Dummys jeden Tag in eine Datenbank schreiben und kann zum Beispiel durch SVGPlots seinen Ertrag visuell darstellen.

Achtung: Das oben beschriebene atDevice verwendet immer das Jahr 2017. Entweder ihr passt das Device im nächsten Jahr an oder ihr bastelt euch innerhalb des atDevice eine Abfrage.

Ich hoffe ich konnte euch verständlich erklären, wie man in FHEM eine CSV-Datei einliest und euch inspirieren dies selber auf eure Wünsche anzupassen und umzusetzen. Mit ein paar Anpassungen kann man natürlich relativ einfach die Routine nach seinen Wünschen anpassen. Zum Beispiel das Auslesen von mehreren Spalten oder das gleichzeitige Abspeichern des Datums.

Hinterlasse einen Kommentar

1 Kommentar auf "CSV-Datei als FHEM-Reading einlesen"

Benachrichtige mich zu:
avatar
Fotos und Bilder
 
 
 
sonstiges Dateiformat
 
 
 
Sortiert nach:   neuste | älteste | beste Bewertung
Norman
Gast
Norman

Hi, Bei mir wird aller 30sec ein eintrag in eine Tageslog CSV geschrieben. wie kann ich denn alle Einträge der Datei mit dem namen des aktuellen Datums mit einmal einlesen?

wpDiscuz