Inhaltsverzeichnis

SMA Wechselrichter

Der Hersteller SMA bietet Solarwechselrichter http://www.sma.de/produkte/solarwechselrichter.html an, deren Daten über verschiedenen Schnittstellen ausgelesen werden können. SMA bietet zum Auslesen die Software „Sunny Explorer“ an, die unter Windows läuft. Für die Nutzung mit der Volkszähler Lösung ist die Quelloffene Software SMASpot besser geeignet, die platttformübergreifend genutzt werden kann, und sich in die Volkszaehler Infrastruktur auf unterschiedliche Weise einbinden lässt. Auf dieser Seite sind einige Beispiele gezeigt.

SMAspot

Die Software SMAspot ist bei https://smaspot.codeplex.com/ zu finden.

SMASpot wurde eingestellt, das Projekt wird unter dem Namen SBFspot weitergeführt. https://github.com/SBFspot/SBFspot/

Kompilieren von SMAspot unter Linux

Die folgenden Punkte wurden unter Ubuntu 12.04 LTS getestet sollten aber auch in anderen Debian-kompatiblen Distributionen funktionieren:

  1. Herunterladen des Sourcecodes z.B. als SMAspot_SRC_244_Linux_Win32.tar.gz z.B. /usr/local/src
  2. Anlegen eines Verzeichnisses z.b. „smaspot“
  3. Wechseln in dieses Verzeichnis
  4. Auspacken mit ../tar xvfz SMAspot_SRC_244_Linux_Win32.tar.gz
  5. Installieren der nötigen Libraries sudo apt-get install libbluetooth-dev libcurl4-openssl-dev libboost-all-dev
  6. Übersetzen mit sudo make
  7. Kopieren des Ergebnisses sudo cp bin/Release/SMASpot /usr/local/bin

Anpassen der Konfiguration für Bluetooth

Bluetooth Adapter prüfen

Beispiel: Cambridge Silicon Radio Bluetooth Adapter

lsusb | grep -i blue
Bus 002 Device 003: ID 0a12:0001 Cambridge Silicon Radio, Ltd Bluetooth Dongle (HCI mode)
SMA Geräte mit Bluetooth suchen

XXXX verdeckt die Angaben des konkreten Beispiels

hcitool scan | grep -i SMA
	00:80:25:24:XX:XX	SMA002d SN: 200221XXXX SN200221XXXX
	00:80:25:29:XX:XX	SMA002d SN: 213010XXXX SN213010XXXX
Ergebnisse in SMASpot.cfg eintragen

Wenn mehrere Inverter vorliegen einfach mehrere cfg Dateien erstellen z.B. 4000Watt.cfg, 1300Watt.cfg

SMASpot testen
SMASpot -v 

oder

SMASpot -v -cfg4000Watt.cfg

Beispiel STP 9000 TL-20 mit SMASpot per Ethernet / Perl->Middleware

Ich besitze den Wechselrichter STP 9000TL-20 http://www.sma.de/produkte/solar-wechselrichter-ohne-transformator/sunny-tripower-5000tl-6000tl-7000tl-8000tl-9000tl.html. Dieser hat direkt einen Ethernetanschluss, der mit dem Tool SMAspot angesprochen werden kann. Das Tool ist zu kompilieren. Sobald es läuft, findet es den Wechselrichter und gibt ein paar Infos aus: SMAspot -sp0 -v

Ich möchte den Gesamtertrag, sowie den Ertrag je String in den Volkszaehler übernehmen: Hierzu habe ich für den Gesamtertrag einen Kanal El. Energie (Zählerstände) und für den Ertrag je String je einen Kanal El. Energie (Leistungswerte) angelegt. Die UUID sind später im Script einzutragen. Anbei das Script:

sma.pl
#!/usr/bin/perl
use LWP::UserAgent;
open STATUS, "/home/markus/hack/sma/smaspot/bin/Release/SMAspot -sp0 -v |"
or die "can't fork: $!";
while (<STATUS>) {
	if (/ETotal:[ ]*(.*)kWh/){
		&submitt("e63106b0-dd25-11e3-9cd5-27aa144849cd",  $1);
 
    	}
    	if (/String 1 Pdc:[ ]*(.*)kW/){
    		$value = $1;
    		$value =~ s/\.//g;
    		&submitt("103ec300-dd27-11e3-84b2-a98f0b16e92d",  $value);
 
 
    	}
    	if (/String 2 Pdc:[ ]*(.*)kW/){
    		$value = $1;
    		$value =~ s/\.//g;
    		&submitt("2d130060-dd27-11e3-b78b-738251a19608",  $value);
    	}
} 
close STATUS or die "bad netstat: $! $?";
 
#------
sub submitt
{
	    $uuid =  $_[0] ;
    $val =  $_[1] ;
    print $uuid . " : " . $val . "\n";
 
 
    my $server_endpoint = "http://localhost/volkszaehler.org/middleware.php/data/${uuid}.json?value=" . $val;
#    get("http://localhost/volkszaehler.org/middleware.php/data/2d130060-dd27-11e3-b78b-738251a19608.json?value=" . $1)."\n";
    #print "serverget = " .  $server_endpoint . "\n";
 
    # set custom HTTP request header fields
    my $req = HTTP::Request->new(POST => $server_endpoint);
    $req->header('content-type' => 'application/json');
    $req->header('x-auth-token' => 'kfksj48sdfj4jd9d');
 
    # add POST data to HTTP request body
    $req->content(" ");
 
    my $ua = LWP::UserAgent->new;
    my $resp = $ua->request($req);
    if ($resp->is_success) {
      my $message = $resp->decoded_content;
      print "Received reply: $message\n";
    } else {
      print "HTTP GET error code: ", $resp->code, "\n";
      print "HTTP GET error message: ", $resp->message, "\n";
    }
 
}

Das Script wird dabei einmal pro Minute mit cron aufgerufen.

*  * * * *    /usr/bin/perl /home/markus/hack/Volksz/sma.pl

Um den Eigenverbrauch zu bestimmen bilde ich die Differenz zwischen der Einspeisung, gemessen am Stromzähler. ( mit vzlogger) und der erzeugten Strommenge des SMA. Da der SMA immer etwas verzögert die Werte anzeigt, lege ich nur einen Messpunkt alle 15 Minuten ab: Die Kanäle sind zuvor in der Weboberfläche anzulegen, und mit mysql die channel_id zu selektieren. Änderung 04.06.2014: Die Berechnung des Gesamtverbrauchs ist aktualisiert.

Originalscript von Markus
eigenverbrauch.sh
#!/bin/bash
echo '
delete from `data` where `timestamp` = (select * from (select max(timestamp) from data where channel_id = 7) x) and channel_id = 7
' | mysql --user=vz --password=fdfdfdfdf volkszaehler -T
echo '
INSERT INTO `data`( `channel_id`, `timestamp`, `value`) select 7,  timestamp_2,   data_4.value - data_2.value /1000    from (
SELECT 
max(case when `channel_id`  = 2 then timestamp else 0 end) timestamp_2,
max(case when `channel_id`  = 4 then timestamp else 0 end) timestamp_4
  FROM `data` where channel_id in (2,4) 
and timestamp > (select max(timestamp) from data where `channel_id` = 7)
group by floor(`timestamp`/60/1000/15) ) a inner join data data_2 on  timestamp_2  = data_2.timestamp and data_2.channel_id = 2
inner join data data_4 on  timestamp_4  = data_4.timestamp and data_4.channel_id = 4
' | mysql --user=vz --password=fdfdfdfdf volkszaehler
 
echo '
delete from `data` where channel_id = 8
' | mysql --user=vz --password=fdfdfdfdf volkszaehler -T
#and timestamp > (select max(timestamp) from data where `channel_id` = 8)
 
echo '
INSERT INTO `data`( `channel_id`, `timestamp`, `value`) select 8,  timestamp_1,   (select data.value from data where channel_id = 7 and  data.timestamp <= data_1.timestamp order by timestamp desc limit 1) + data_1.value/1000     from 
(
SELECT 
max(case when `channel_id`  = 1 then timestamp else 0 end) timestamp_1,
max(case when `channel_id`  = 7 then timestamp else 0 end) timestamp_7
  FROM `data` where channel_id in (1,7) 
group by floor(`timestamp`/60/1000/15) 
) a inner join data data_1 on  timestamp_1  = data_1.timestamp and data_1.channel_id = 1
' | mysql --user=vz --password=dsdsdsdsds volkszaehler

auch diese Script wird mit cron gestartet, aber nur ein mal die Stunde.

1  * * * *    bash /home/markus/hack/Volksz/eigenverbrauch.sh
Modifiziertes Script (2. Versuch von Markus)

Läuft Fix, und berechnet den Eigenverbrauch sehr gut, insbesondere bei Lücken in der AUfzeichnung.

eigenverbrauch.sh
#!/usr/bin/perl
 
use Time::Piece;
use LWP::UserAgent;
use DBI;
#Gesamtverbrauch einbauen
$debug = 0;
#$debug = 1;
 
my $dbh = DBI->connect("DBI:mysql:database=volkszaehler;", "vz", "Geheimagent",{AutoCommit => 0} ) or die $DBI::errstr;
my $ideigen = "7";
#step_clean();
#step_get_SMA();
step_copy_SMA();
step_max_10();
step_lin();
add_missig_dates(1);
step_gesamtverbrauch();
 
sub step_get_SMA { # ad300 -> 300 tage
system("/home/markus/hack/sma/SBFspot-3.7.0/SBFspot/mariadb/bin/SBFspot -finq -nocsv -am2 -ad300 -v");
}
 
sub step_copy_SMA {
my $sth =  $dbh->prepare ("insert into volkszaehler.data (channel_id, timestamp, value) SELECT  4 , TimeStamp * 1000,  TotalYield/1000 FROM SMA.DayData where cast(TimeStamp/60/10 as int) not in (select cast(timestamp/1000/60/10 as int) from volkszaehler.data where channel_id = 4) and Serial = 304951132");
my $numrows = $sth->execute();
$dbh->commit();
 
print "step_copy_SMA done -  $numrows copy\n";
}
 
sub step_clean {
my $sth =  $dbh->prepare ("delete FROM volkszaehler.`data` where channel_id in (1029, 1030, 1031, 8, ".$ideigen.")");
my $numrows = $sth->execute();
$dbh->commit();
print "step_clean done -  $numrows delted\n";
}
 
sub step_max_10 {xRe9AnfH7b9uNPS8
my $inserted = 0;
# Neue Idee zuerst die 10 Minuten lücken füllen ...
# Danach Linear zur Einspeisung ...
my $sth_4_solar = $dbh->prepare ("SELECT id, channel_id, `timestamp`, value FROM volkszaehler.`data` left outer join (select timestamp as ts1029 from volkszaehler.`data` where channel_id = ".$ideigen." ) x on ts1029 = timestamp where channel_id = 4 and ts1029 is null order by timestamp");
my $sth_2_einsp = $dbh->prepare ("SELECT id, channel_id, `timestamp`, value FROM volkszaehler.`data` where channel_id = 2 order by timestamp");
my $sth =  $dbh->prepare ("INSERT into   volkszaehler.`data` (channel_id, `timestamp`, value)  VALUES (?,?, ?) ");
 
$sth_4_solar->execute ();
$sth_2_einsp->execute ();
my $timestamp3  = 0;
 
while (my ($id, $channel_id, $timestamp, $value) = $sth_4_solar->fetchrow_array()) {
    while ( $timestamp3  < $timestamp and   my ($id2, $channel_id2, $timestamp2, $value2) = $sth_2_einsp->fetchrow_array()){
$timestamp3_old  = $timestamp3;
$value3_old = $value3;
$timestamp3  = $timestamp2;
$value3 = $value2/1000;
#print  "                  " .$timestamp3 ."\n";
}
my $time_diff_tot = $timestamp3  -   $timestamp3_old;
my $einsp_MS = ($value3  -   $value3_old)/ $time_diff_tot;
my $time_diff = $timestamp   -   $timestamp3_old;
my $einsp_estim = $value3_old + ( ($time_diff   ) * $einsp_MS );
my $value_eigenv = $value - $einsp_estim ;
 
if ($debug > 0) {
 
print "-------------------------";
print localtime($timestamp/1000)->strftime('%Y-%m-%d-%H:%M:%S'."\n");
print "Zaehlerstand WR: $value \n";
print "Einspeisung:\n";
print "    " .localtime($timestamp3_old/1000)->strftime('%Y-%m-%d-%H:%M:%S'.": $value3_old \n");
print " NEW" .localtime($timestamp     /1000)->strftime('%Y-%m-%d-%H:%M:%S'.": $einsp_estim \n");
print "    " .localtime($timestamp3    /1000)->strftime('%Y-%m-%d-%H:%M:%S'.": $value3 \n");
print  "Zeitdifferenz    Einspeisung:".  (  $timestamp3  -   $timestamp3_old )  ."         \n";
print  "Zaehlerdifferenz Einspeisung:".  (  $value3  -   $value3_old )  ."         \n";
print  "einspeisung MS:".    $einsp_MS   ."         \n";
print  "eigenverbrauch:". $value_eigenv."         \n";
}
if ($time_diff_tot< (1000*60*10)){ # nur bis 10 Minuten pausen
$inserted += 1;
$sth->execute($ideigen, $timestamp, $value_eigenv);
$sth->execute(2, $timestamp, $einsp_estim*1000);
}
}
$dbh->commit();
print "step_max_10 done $inserted affected\n";
}
 
sub step_lin{
my $inserted = 0;
# Neue Idee zuerst die 10 Minuten lücken füllen ...
# Danach Linear zur Einspeisung ...
my $sth_4_solar = $dbh->prepare ("SELECT id, channel_id, `timestamp`, value FROM volkszaehler.`data` left outer join (select timestamp as ts1029 from volkszaehler.`data` where channel_id = ".$ideigen." ) x on ts1029 = timestamp where channel_id = 4 and ts1029 is null order by timestamp");
my $sth_2_einsp = $dbh->prepare ("select timestamp4, value1029, value4 FROM 
(SELECT              `timestamp` as timestamp4, value as value4 FROM volkszaehler.`data` where channel_id = 4 ) as z4 inner join 
(SELECT  channel_id, `timestamp` as timestamp1029, value as value1029 FROM volkszaehler.`data` where channel_id = ".$ideigen." ) as z2 on timestamp4 = timestamp1029
order by timestamp4");
my $sth =  $dbh->prepare ("INSERT into   volkszaehler.`data` (channel_id, `timestamp`, value)  VALUES (?,?, ?) ");
 
$sth_4_solar->execute ();
 $sth_2_einsp->execute ();
my $timestamp3  = 0;
while (my ($id, $channel_id, $timestamp, $value) = $sth_4_solar->fetchrow_array()) {
#print localtime($timestamp     /1000)->strftime('%Y-%m-%d-%H:%M:%S'."\n");
    while ( $timestamp3  < $timestamp and   my ($timestamp2, $value1029, $value4, ) = $sth_2_einsp->fetchrow_array()){
$timestamp3_old  = $timestamp3;
$value1029_old = $value1029_new;
   $value4_old = $value4_new;
$timestamp3  = $timestamp2;
$value1029_new = $value1029;
   $value4_new = $value4;
#print  "                  " .$timestamp3 ."\n";
}
 
#my $einsp_MS = ($value3  -   $value3_old)/ ( $timestamp3  -   $timestamp3_old);
#my $time_diff = $timestamp   -   $timestamp3_old;
#my $einsp_estim = $value3_old + ( ($time_diff   ) * $einsp_MS );
#my $value_eigenv = $value - $einsp_estim ;
my $diffeinspeisung = $value1029_new - $value1029_old;
my $differz         = $value4_new - $value4_old;
my $differz_daz     = $value - $value4_old;
if ($differz > 0){
 
 
my $anteil  =  $diffeinspeisung /$differz  ;
my $diff_ber = $differz_daz *  $anteil;
my $value_ber = $diff_ber + $value1029_old;
my $value_ber_einsp = $value- $value_ber; 
if ($debug > 0) {
 
print "-----------------------------------\n";
 
print "Zeit:\n";
print " ALT:" .localtime($timestamp3_old/1000)->strftime('%Y-%m-%d-%H:%M:%S'." \n");
print " DAZ:" .localtime($timestamp     /1000)->strftime('%Y-%m-%d-%H:%M:%S'." \n");
print " NEU:" .localtime($timestamp3    /1000)->strftime('%Y-%m-%d-%H:%M:%S'." \n");
print "\n";
print "Zaehlerdifferenz EIGENVERB\n";
print " NEU: $value1029_new\n" ;
print " ALT: $value1029_old\n" ;
print "      --------------\n" ;
print "      $diffeinspeisung\n";
print "\n";
print "Zaehlerdifferenz Erzeugung\n";
print " NEU: $value4_new\n" ;
print " ALT: $value4_old\n" ;
print "      --------------\n" ;
print "      $differz\n";
print "\n";
print "ANTEIL: $anteil\n";
print "\n";
print "Zaehlerdifferenz Erzeugung2\n";
print " DAZ: $value\n" ;
print " ALT: $value4_old\n" ;
print "      --------------\n" ;
print "      $differz_daz \n";
print "\n";
print "Differenz Berechnet: $diff_ber\n";
print "Zähler NEU: $value_ber \n";
print "Zähler Einspeisung: $value_ber_einsp \n";
print "\n";
}
$inserted += 1;
$sth->execute($ideigen, $timestamp, $value_ber);
$sth->execute(2, $timestamp, $value_ber_einsp*1000);
 
}
}
$dbh->commit();
print "step_lin done  $inserted affected\n";
}
 
 
sub step_gesamtverbrauch {
#my $sth =  $dbh->prepare ("delete FROM volkszaehler.`data` where channel_id in ( 8)");
#my $numrows = $sth->execute();
 
my $inserted = 0;
# Neue Idee zuerst die 10 Minuten lücken füllen ...
# Danach Linear zur Einspeisung ...
my $sth_4_solar = $dbh->prepare ("SELECT id, channel_id, `timestamp`, value FROM volkszaehler.`data` left outer join (select timestamp as ts1029 from volkszaehler.`data` where channel_id = 8 ) x on ts1029 = timestamp where channel_id = 1 and ts1029 is null and timestamp < (SELECT max(`timestamp`) FROM volkszaehler.`data` where channel_id = 7 )  order by timestamp");
my $sth_2_einsp = $dbh->prepare ("SELECT id, channel_id, `timestamp`, value FROM volkszaehler.`data` where channel_id = 7 order by timestamp");
my $sth =  $dbh->prepare ("INSERT into   volkszaehler.`data` (channel_id, `timestamp`, value)  VALUES (?,?, ?) ");
 
$sth_4_solar->execute ();
$sth_2_einsp->execute ();
my $timestamp3  = 0;
 
while (my ($id, $channel_id, $timestamp, $value) = $sth_4_solar->fetchrow_array()) {
    while ( $timestamp3  < $timestamp and   my ($id2, $channel_id2, $timestamp2, $value2) = $sth_2_einsp->fetchrow_array()){
$timestamp3_old  = $timestamp3;
$value3_old = $value3;
$timestamp3  = $timestamp2;
$value3 = $value2;
#print  "                  " .$timestamp3 ."\n";
}
my $time_diff_tot = $timestamp3  -   $timestamp3_old;
my $einsp_MS = ($value3  -   $value3_old)/ $time_diff_tot;
my $time_diff = $timestamp   -   $timestamp3_old;
my $einsp_estim = $value3_old + ( ($time_diff   ) * $einsp_MS );
my $value_eigenv = $value/1000 + $einsp_estim ;
 
if ($debug > 0) {
 
print "-------------------------";
print localtime($timestamp/1000)->strftime('%Y-%m-%d-%H:%M:%S'."\n");
print "Zaehlerstand BEZUG: $value \n";
print "Eigenverbrauch:\n";
print "    " .localtime($timestamp3_old/1000)->strftime('%Y-%m-%d-%H:%M:%S'.": $value3_old \n");
print " NEW" .localtime($timestamp     /1000)->strftime('%Y-%m-%d-%H:%M:%S'.": $einsp_estim \n");
print "    " .localtime($timestamp3    /1000)->strftime('%Y-%m-%d-%H:%M:%S'.": $value3 \n");
print  "Zeitdifferenz    Einspeisung:".  (  $timestamp3  -   $timestamp3_old )  ."         \n";
print  "Zaehlerdifferenz Einspeisung:".  (  $value3  -   $value3_old )  ."         \n";
print  "einspeisung MS:".    $einsp_MS   ."         \n";
print  "Gesamtverbauch:". $value_eigenv."         \n";
}
#if ($time_diff_tot< (1000*60*10)){ # nur bis 10 Minuten pausen
$inserted += 1;
$sth->execute(8, $timestamp, $value_eigenv);
#$sth->execute(1031, $timestamp, $einsp_estim*1000);
#}
}
$dbh->commit();
print "step_max_10 done $inserted affected\n";
}
 
 
sub add_missig_dates {
  my ($id) = @_;
$inserted = 0;
#my $id = 1;
my $timestamp3  = 0;
 
my $sth = $dbh->prepare ("SELECT id, channel_id, `timestamp`, value FROM volkszaehler.`data` where channel_id = $id order by timestamp");
$sth->execute ();
my $sth2 =  $dbh->prepare ("INSERT into   volkszaehler.`data` (channel_id, `timestamp`, value)  VALUES (?,?, ?) ");
 
my $t = Time::Piece->strptime("2014-06-01", "%Y-%m-%d");
print $t->epoch*1000;
for (my $i= $t->epoch - 60*60*2-1; $i  <= localtime; $i = $i + (24*60*60)) {
#   print "$i\n";
my $timestamp = $i*1000;
while ( $timestamp3  < $timestamp and  my ($id2, $channel_id2, $timestamp2, $value2) = $sth->fetchrow_array()){
$timestamp3_old  = $timestamp3;
$value3_old = $value3;
$timestamp3  = $timestamp2;
$value3 = $value2;
#print  "                  " .$timestamp3 ."\n";
}
my $time_diff_tot = $timestamp3  -   $timestamp3_old;
my $einsp_MS  = ($value3  -   $value3_old)/ ( $time_diff_tot);
my $time_diff = $timestamp   -   $timestamp3_old;
my $value     = ($value3_old + ( ($time_diff   ) * $einsp_MS )) ;
 
if ($time_diff_tot> (1000*60*60*5)){ # nur bis 10 Minuten pausen
if ($debug > 0) {
print "-------------------------\n$id\n";
print localtime($timestamp/1000)->strftime('%Y-%m-%d-%H:%M:%S'."\n");
 
print "    " .localtime($timestamp3_old/1000)->strftime('%Y-%m-%d-%H:%M:%S'.": $value3_old \n");
print "NEW " .localtime($timestamp     /1000)->strftime('%Y-%m-%d-%H:%M:%S'.": $value \n");
print "    " .localtime($timestamp3    /1000)->strftime('%Y-%m-%d-%H:%M:%S'.": $value3 \n");
print  "Zeitdifferenz    Einspeisung:".  (  $timestamp3  -   $timestamp3_old )  ."         \n";
}
$inserted += 1;
$sth2->execute($id, $timestamp, $value);
}
}
$dbh->commit();
print "step_max_10 done $inserted affected - $id\n";
}
Modifiziertes Script (Versuch von Wolfgang)
eigenverbrauch.sh
#!/bin/bash
# calculate own PV usage 
# Author: Markus 2014-05-20
# latest version:
# $Header: /home/wf/smaspot/RCS/eigenverbrauch.bash,v 1.5 2014/05/29 10:17:11 wf Exp wf $
 
#
# get the delete query for the given channel
#
deletequery() {
  local l_channel=$1
cat << EOF
  DELETE 
   FROM `data` 
   WHERE `timestamp` = 
     (SELECT MAX(timestamp) FROM data WHERE channel_id = $l_channel)  
   AND channel_id = $l_channel;
EOF
}
 
#
# get the insert query for the given channels
#  l_chanel the channel to insert into
#    l_c2 - the channel to substract 
#    l_c3 - the channel to substract the value from
#
insertquery() {
  local l_channel=$1
  local l_c2=$2
  local l_c3=$3
cat << EOF
  INSERT INTO `data`( `channel_id`, `timestamp`, `value`) 
    SELECT $l_chanel,  timestamp_2,   data_3.value - data_2.value /1000    
    FROM (
      SELECT 
        MAX(CASE WHEN `channel_id` = $l_c2 then timestamp else 0 end) timestamp_2,
        MAX(CASE WHEN `channel_id` = $l_c3 then timestamp else 0 end) timestamp_3
        FROM `data` 
        WHERE channel_id in ($l_c2,$l_c3) 
          AND timestamp > (
            SELECT MAX(timestamp) FROM data WHERE `channel_id` = $l_channel 
          )
        GROUP by floor(`timestamp`/60/1000/15) ) a 
        INNER JOIN data data_2 
          ON  timestamp_2  = data_2.timestamp 
          AND data_2.channel_id = $l_c2 
        INNER JOIN data data_3 
          ON  timestamp_3  = data_3.timestamp 
          AND data_3.channel_id = $l_c3 
EOF
}
 
# modify according to your volkszaehler mysql database settings
user=vz
password=fdfdfdfdf 
db=volkszaehler 
 
# debug setting
debug=
# uncomment to debug sql
#debug=-T
 
# modify to suite your channel settings
 
# channel 7 is calculated from as channel 4 - channel 2 / 1000 
deletequery 7     | egrep -v "^#" | mysql --user=$user --password=$password $db $debug
insertquery 7 2 4 | egrep -v "^#" | mysql --user=$user --password=$password $db $debug
 
# channel 8 is calculated from as channel 7 - channel 1 / 1000 
echo "DELETE FROM `data` WHERE channel_id = 8" |  mysql --user=$user --password=$password $db $debug
insertquery 8 1 7 | egrep -v "^#" | mysql --user=$user --password=$password $db $debug

Und so sieht es aus:

Beispiel SB 4000 TL-21 + SB 1300TL-10 mit SMASpot und PHP

The solution consists of three parts:

  1. the bash script sma2vz
  2. the php script sma2vz.php
  3. the php helper script vzapihelper.php (same as in the youless page)

the bash script sma2vz has a function configure - this needs to run once you might want to configure the here-document part in the function inverters of this script

there is a help screen available:

sma2vz
./sma2vz --help
   usage: ./sma2vz --vzurl=vzurl --cuuid_pwr=x --cuuid_kwh=y 
     [ --daytimeonly --lat=lattitude --lon=longitude]
     [--loop --delay=delay] 
    | [--help] 
    | [--configure] 
 
     --vzurl=<url> 
        volkszaehler middleware url  
 
     --cuuid_pwr=<uuid> 
        channel uuid for power (watt) PV output 
 
     --cuuid_kwh=<uuid>  
       channel uuid for energy (kwH) PV total 
 
     --daytimeonly
       do not post data at night (e.g. if your device does not supply data)
 
     --lon=<longitude>
       plant longitude geo coordinate
 
     --lat=<lattitude>
       plant lattitude geo coordinate
 
     --loop  
       poll SMA inverters in a loop  
 
     --delay=<secs>  
       how many secs to wait between each reading (default: 15 secs)
 
     --configure 
       create SMAsport configuration file(s) 
         modify ./sma2vz inverters shell function 
         here document to fit your plant's data
configure example
./sma2vz --lat 51.244 --lon 6.52 --configure
loop example
./sma2vz \
  --vzurl "http://capri/vz/middleware.php/data" \
  --cuuid_pwr "7744bbf0-e74d-11e3-9ec7-xxxx" \
  --cuuid_kwh "460feba0-e74f-11e3-8a0d-xxxx" \
  --daytimeonly --lat 51.244 --lon 6.52 \
  --loop --delay 15
cron example
cd /home/wf/smaspot
/home/wf/smaspot/sma2vz \
  --vzurl "http://capri/vz/middleware.php/data" \
  --cuuid_pwr "7744bbf0-e74d-11e3-9ec7-xxxx" \
  --cuuid_kwh "460feba0-e74f-11e3-8a0d-xxxx" \
  --daytimeonly --lat 51.244 --lon 6.52 >> /var/log/energymeter.log
logoutput example
2014-06-01 12:52:08 PV:  1409 Watt   6699.801 kwH sun rise:05:21 set:21:42
2014-06-01 12:53:07 PV:  2176 Watt   6699.826 kwH sun rise:05:21 set:21:42
2014-06-01 12:54:07 PV:  1699 Watt   6699.855 kwH sun rise:05:21 set:21:42
2014-06-01 12:55:07 PV:  3375 Watt   6699.898 kwH sun rise:05:21 set:21:42
2014-06-01 12:56:07 PV:  3578 Watt   6699.957 kwH sun rise:05:21 set:21:42

Any feedback please to wf (at) bitplan.com - enjoy!

sma2vz
#/bin/bash
# SMAspot with Volkszaehler
# WF 2014-05-29
# $Header: /home/wf/smaspot/RCS/sma2vz,v 1.8 2014/06/01 10:49:38 wf Exp wf $
 
#
# get a configuration
#  param 1: bluetooth address
#  param 2: name of configuration/plant
#  param 3: "USER" password for SMA device
#  param 4: latitude of plant
#  param 5: longitude of plant
#  param 6: path to output files of SMAspot
#
smaspot_config() {
 local l_btaddr=$1
 local l_name=$2
 local l_password=$3
 local l_lon=$4
 local l_lat=$5
 local l_path=$6
cat << EOF
################################################################################
#                ____  __  __    _                    _   
#               / ___||  \/  |  / \   ___ _ __   ___ | |_ 
#               \___ \| |\/| | / _ \ / __| '_ \ / _ \| __|
#                ___) | |  | |/ ___ \\__ \ |_) | (_) | |_ 
#               |____/|_|  |_/_/   \_\___/ .__/ \___/ \__|
#                                        |_|              
#
#  SMAspot.cfg - Configuration file for SMAspot.exe
#  SMAspot - Yet another tool to read power production of SMA solar inverters
#  (c)2012-2014, SBF
#
#  DISCLAIMER:
#  A user of SMAspot software acknowledges that he or she is receiving this
#  software on an "as is" basis and the user is not relying on the accuracy 
#  or functionality of the software for any purpose. The user further
#  acknowledges that any use of this software will be at his own risk 
#  and the copyright owner accepts no responsibility whatsoever arising from
#  the use or application of the software. 
#
################################################################################
 
# SMA Inverter's Bluetooth address
# Windows: smaspot -scan
# Linux  : hcitool scan
# IMPORTANT FOR SPEEDWIRE USERS: COMMENT OUT BTADDRESS (PUT # IN FRONT)
BTAddress=$l_btaddr
 
# SMA Inverter's Speedwire IP address
# If IP_Address is not set or is 0.0.0.0 SMAspot will try to detect the speedwire inverter by broadcast
# If IP_Address is set to a valid IP, SMAspot will try to connect directly to that IP without broadcast detection
#IP_Address=0.0.0.0
 
# User password (default 0000)
Password=$l_password
 
# MIS_Enabled (Multi Inverter Support: Default=0 Disabled)
# +------------+-------+-------------+
# | #Inverters | NetID | MIS_Enabled |
# +------------+-------+-------------+
# |      1     |   1   | Don't Care  |
# +------------+-------+-------------+
# |      1     |   >1  |      0      |
# +------------+-------+-------------+
# |      >1    |   >1  |      1      |
# +------------+-------+-------------+
MIS_Enabled=0
 
# Plantname
Plantname=$l_name
 
# OutputPath (Place to store CSV files)
# 
# Windows: C:\TEMP\SMA\%Y
# Linux  : /home/sbf/Documents/sma/%Y
# %Y %m and %d will be expanded to Year Month and Day
OutputPath=$l_path/%Y
 
# OutputPathEvents (Place to store CSV files for events)
# If omitted, OutputPath is used
OutputPathEvents=$l_path/Events
 
# Position of pv-plant http://itouchmap.com/latlong.html
# Example for Ukkel, Belgium
Latitude=$l_lat
Longitude=$l_lon
 
# Calculate Missing SpotValues
# If set to 1, values not provided by inverter will be calculated
# eg: Pdc1 = Idc1 * Udc1
CalculateMissingSpotValues=1
 
# DateTimeFormat (default %d/%m/%Y %H:%M:%S)
# For details see strftime() function
# http://www.cplusplus.com/reference/clibrary/ctime/strftime/
DateTimeFormat=%Y-%m-%d %H:%M:%S
 
# DateFormat (default %d/%m/%Y)
DateFormat=%-%m-%d
 
# DecimalPoint (comma/point default comma)
DecimalPoint=point
 
# TimeFormat (default %H:%M:%S)
TimeFormat=%H:%M:%S
 
# SynchTime (default 1 = On)
# If set to 1 the Inverter time is synchronised with pc time
# Some inverters don't have a real-time clock
SynchTime=1
 
# SunRSOffset
# Offset to start before sunrise and end after sunset (0-3600 - default 900 seconds)
SunRSOffset=900
 
# Locale
# Translate Entries in CSV files
# Surpported locales: de-DE;en-US;fr-FR;nl-NL;es-ES;it-IT
# Default en-US
Locale=de-DE
 
# Timezone
# Select the right timezone in date_time_zonespec.csv
# e.g. Timezone=Europe/Brussels
Timezone=Europe/Berlin
 
###########################
### CSV Export Settings ###
###########################
# With CSV_* settings you can define the CSV file format
 
# CSV_Export (default 1 = Enabled)
# Enables or disables the CSV Export functionality
CSV_Export=1
 
# CSV_ExtendedHeader (default 1 = On)
# Enables or disables the SMA extended header info (8 lines)
# isep=;
# Version CSV1|Tool SMAspot|Linebreaks CR/LF|Delimiter semicolon|Decimalpoint comma|Precision 3
# etc...
# This is usefull for manual data upload to pvoutput.org
CSV_ExtendedHeader=1
 
# CSV_Header (default 1 = On)
# Enables or disables the CSV data header info (1 line)
# dd/MM/yyyy HH:mm:ss;kWh;kW
# This is usefull for manual data upload to pvoutput.org
# If CSV_ExtendedHeader is enabled, CSV_Header is also enabled
CSV_Header=1
 
# CSV_SaveZeroPower (default 1 = On)
# When enabled, daily csv files contain all data from 00:00 to 23:55
# This is usefull for manual data upload to pvoutput.org
CSV_SaveZeroPower=1
 
# CSV_Delimiter (comma/semicolon default semicolon)
CSV_Delimiter=semicolon
 
# CSV_Spot_TimeSource (Inverter|Computer default Inverter)
CSV_Spot_TimeSource=Inverter
 
# CSV_Spot_WebboxHeader (Default 0 = Off)
# When enabled, use Webbox style header (DcMs.Watt[A];DcMs.Watt[B]...)
CSV_Spot_WebboxHeader=0
 
#################################
### Online Monitoring Systems ###
#################################
#
# In the future, multiple online monitoring systems can be defined
# Here we can activate the ones we like
#
################################
### PVoutput Upload Settings ###
################################
# PVoutput (default 0 = Disabled)
# Enables or disables the upload functionality to pvoutput.org
# When enabled, be sure to use -u switch on the command line
PVoutput=0
 
#PVoutput_SID
#Sets PVoutput System ID
PVoutput_SID=
 
#PVoutput_Key
#Sets PVoutput API Key
PVoutput_Key=
 
# VoltageLogging sets AC or DC logging.
# Possible values are:
# NONE (disabled)
# MAX(AC) (default)
# AC(PH1) or AC(PH2) or AC(PH3)
# MAX(DC) or DC(ST1) or DC(ST2)
VoltLogging=MAX(AC)
 
# InverterTemp (default 0 = disabled)
# Enables or disables the upload of the inverter's temperature
InverterTemp=0
 
# InverterTempMapTo (default v5 = Use standard PVoutput Temperature Graph)
# In Donation Mode only, map inverter's temperature to extended data field (v7..v12)
# For more info, see http://pvoutput.org/help.html#donations 
InverterTempMapTo=v5
 
# CumulativeEnergy (default 0 = Today's Energy)
# Set the cumulative flag = 1 when you wish to pass lifetime energy or 0 for today's energy
# WARNING!!! DO NOT CHANGE THIS FLAG DURING DAYLIGHT AS THIS WILL MESS UP YOUR PVOUTPUT GRAPHS
CumulativeEnergy=0
EOF
}
 
#
#inverters
#
inverters() {
# insert your data from 
#   hcitool scan | grep SMA
# 
# first  column is bluetooth address
# second column is the name of the device (here just the wattage is used)
# third  column is the password for "USER" that the SMA Device expects
# fourth column is the latitude
# fifth  column is the longitude
cat << EOF
00:80:25:24:xx:xy 1300Watt password /home/wf/smaspot
00:80:25:29:xx:xy 4000Watt password /home/wf/smaspot
EOF
}
 
#
# create configuration files
#
configure() {
  checklonlat "configure"
inverters | while read btaddr name password path; do
  echo "creating ${name}.cfg for bluetooth addr $btaddr" 1>&2
  smaspot_config $btaddr $name $password $lon $lat $path > ${name}.cfg
done
}
 
#
# get the sma meter reading
#
getsmameter() {
  # temp filename base for SMAspor result
  tmp=/tmp/smaspot$$
  # read data from all inverters
  inverters | while read btaddr name password path; do
    # single shot run of SMAspot with no CVS export
    # uncomment to debug
    #echo "running smaspot with ${name}.cfg for bluetooth addr $btaddr"
    ./SMAspot -v -nocsv -cfg${name}.cfg > ${tmp}_${name}
    # the lines we need look like:
    #  EToday: 3.358kWh
	  #  ETotal: 5151.294kWh
    #  Total Pac   :   0.442kW
 
    # let's filter the result with awk
    cat ${tmp}_${name} | awk '
# set the field separator fitting the x: y format
BEGIN        { FS=":";doublequote="\x22" }
# check the input lines for the three patterns and remove
# the unit at the end - assign to the three variables
# etoday, etotal and totalpac
/EToday:/    { etoday  =$2;gsub("kWh","",etoday)   }
/ETotal:/    { etotal  =$2;gsub("kWh","",etotal)   }
/Total Pac/  { totalpac=$2;gsub("kW" ,"",totalpac) }
# at the end of all lines print out a single json formatted result line
END          { 
                printf("{%s,%s,%s}\n", 
                  json("etoday",etoday),
                  json("etotal",etotal),
                  json("totalpac",totalpac)) 
             }
# helper function to create json name values
function json(name,value,result) {
  # trim value
  gsub(" ","",value);
  result=quote(name)":"quote(value);
  return result 
}
 
# helper function to quote a string
function quote(s,result) {
  result=doublequote s doublequote;
  return result 
}' 
  rm ${tmp}_${name}
done
}
 
#
# show usage
#
usage() { 
cat << EOF
   usage: $0 --vzurl=vzurl --cuuid_pwr=x --cuuid_kwh=y 
     [ --daytimeonly --lat=lattitude --lon=longitude]
     [--loop --delay=delay] 
    | [--help] 
    | [--configure] 
 
     --vzurl=<url> 
        volkszaehler middleware url  
 
     --cuuid_pwr=<uuid> 
        channel uuid for power (watt) PV output 
 
     --cuuid_kwh=<uuid>  
       channel uuid for energy (kwH) PV total 
 
     --daytimeonly
       do not post data at night (e.g. if your device does not supply data)
 
     --lon=<longitude>
       plant longitude geo coordinate
 
     --lat=<lattitude>
       plant lattitude geo coordinate
 
     --loop  
       poll SMA inverters in a loop  
 
     --delay=<secs>  
       how many secs to wait between each reading (default: 15 secs)
 
     --configure 
       create SMAsport configuration file(s) 
         modify $0 inverters shell function 
         here document to fit your plant's data
EOF
  exit 1
}
 
# 
# show error and exit
#
error() {
  local l_msg=$1
  local l_hint=$2
  echo "error $0: $l_msg" 1>&2
  echo "  you might want to $l_hint !" 1>&2
  exit 1
}
 
#
# check that longitude and lattitude are supplied
#
checklonlat() {
  local l_title="$1"
  if [ "$lon" == "" ]
  then
    error "option --lon" "supply lon longitude setting for $l_title to work"
  fi
  if [ "$lat" == "" ]
  then
    error "option --lat" "supply lat lattitude setting for $l_title to work"
  fi
}
 
# defaults
delay=15
maxloops=1
daytimeonly=0
 
# check command line arguments
while [ $# -gt 0 ]
do
  arg="$1"
  #echo "$arg"
  case "$arg" in 
     --help)
       usage
       ;;
 
     --vzurl) 
       vzurl="$2"
       shift
       ;;
 
     --cuuid_pwr) 
       cuuid_pwr="$2"
       shift
       ;;
 
     --cuuid_kwh) 
       cuuid_kwh="$2"
       #echo $cuuid_kwh
       shift
       ;;
 
     --daytimeonly)
       daytimeonly=1;
       ;;
 
     --lat) 
       lat="$2"
       shift
       ;;
 
     --lon) 
       lon="$2"
       shift
       ;;
 
     --delay) 
       delay=$2     
       shift
       ;;
 
     --loop)
       maxloops=10000000;
       ;;
 
     --configure)
 
       echo "creating configuration files"
       configure
       exit
       ;;
     *)
       echo >&2 "Invalid argument: $1"
       exit 1
     ;;
   esac
   shift
done
 
# check that options are set
if [ "$vzurl" == "" ]
then
  error "option --vzurl is missing" "check and use volkszaehler middleware url"
fi
if [ "${cuuid_pwr}" == "" ]
then
  error "option --cuuid_pwr is missing" "check the uuid for the PV power (Watt) channel"
fi
if [ "$cuuid_kwh" == "" ]
then
  error "option --cuuid_kwh" "check the uuid for the PV energy (kwH) channel"
fi
if [ "$daytimeonly" -eq 1 ]
then
  checklonlat "daytimeonly"
fi
 
loop=0
while [ $loop -lt $maxloops ]
do
  # get the SMA meter readings
  getsmameter | php sma2vz.php --vzurl=$vzurl --cuuid_pwr=$cuuid_pwr --cuuid_kwh=$cuuid_kwh --daytimeonly=$daytimeonly --lat=$lat --lon=$lon
  # sleep a while
  if [ $maxloops -gt 1 ]
  then
    sleep $delay
  fi
  loop=`expr $loop + 1 `
done
sma2vz.php
<?php
  /**
   * read meter data from SMA device
   * and post it to volkszaehler
   * $Header: /home/wf/smaspot/RCS/sma2vz.php,v 1.6 2014/06/01 10:40:27 wf Exp wf $
   */
 
// common code for reading and posting
require __DIR__.'/vzapihelper.php';
 
  /**
   * check the daytime values
   */
  function daytime($latitude,$longitude) {
    $result=array();
    // 08:53 CEST
    // $time_format = 'H:i T';
    // 08:53
    $time_format = 'H:i';
 
    // find time offset in hours
    $tzoffset = date("Z")/60 / 60;
 
    $zenith = 90+(50/60); // True sunrise/sunset
 
    // determine sunrise time
    $sunrise = date_sunrise(time(), SUNFUNCS_RET_STRING, $latitude, $longitude, $zenith, $tzoffset);
    $sunrise_time = date($time_format, strtotime(date("Y-m-d") . ' '. $sunrise));
    // determine sunset time
    $sunset = date_sunset(time(), SUNFUNCS_RET_STRING, $latitude, $longitude, $zenith, $tzoffset);
    $sunset_time = date($time_format, strtotime(date("Y-m-d") . ' '. $sunset));
 
    // check whether it's daytime
    $sunrise_epoch = date_sunrise(time(), SUNFUNCS_RET_TIMESTAMP, $latitude, $longitude, $zenith, $tzoffset);
    $sunset_epoch  = date_sunset(time(), SUNFUNCS_RET_TIMESTAMP, $latitude, $longitude, $zenith, $tzoffset);
    $time_epoch = time(); // time now
 
    $result["daytime"]=($time_epoch < $sunset_epoch and $time_epoch > $sunrise_epoch);
 
    $result["rise"] = $sunrise_time;
    $result["set"] = $sunset_time;
    $result["time"] = date("Y-m-d H:i:s",time());
    return $result;
  } 
 
  $loop=0;
  // possible command line options
  // --vzurl= --cuuid_pwr= --cuuid_kwh= --daytimeonly
  $longopts=array("vzurl:","cuuid_pwr:","cuuid_kwh:","lat:","lon:","daytimeonly:");
  $shortopts="";
  $options=getopt($shortopts,$longopts);
  // the volkszaehler middleware url
  $vzurl=checkoption("vzurl",$options);
  // channel uuids
  // power (watts)
  $cuuid_pwr=checkoption("cuuid_pwr",$options);
  // energy (kWh)
  $cuuid_kwh=checkoption("cuuid_kwh",$options);
  // daytimeonly? 
  $daytimeonly=checkoption("daytimeonly",$options);
  if ($daytimeonly) {
    $latitude=checkoption("lat",$options);
    $longitude=checkoption("lon",$options);
  }
 
  $jsonlines=file("php://stdin");
  $tmeter=array();
  foreach ($jsonlines as $line_nume => $json) {
    // get the meter reading
    $meter=json_decode($json, true);
    #var_dump($meter);
    foreach ($meter as $name => $value) {
      if (!array_key_exists($name,$tmeter)) 
        $tmeter[$name]=0;
      $tmeter[$name]+=$value;
    }
  }
  #var_dump($tmeter);
  //array(3) { ["etoday"]=> float(2.52)
  // ["etotal"]=> float(6632.462)
  //["totalpac"]=> float(2.478) }
  $etotal=$tmeter["etotal"];
  $totalpac=$tmeter["totalpac"]*1000;
  if ($daytimeonly) {
    $daytime=daytime($latitude,$longitude);
    if (!$daytime["daytime"]) {
      printf("%s after sunset: %s wait for sunrise: %s\n",$daytime["time"],$daytime["set"],$daytime["rise"]);
      exit(2); 
    }
  }
  # total wattage of all inverters
  post2vz($vzurl,$cuuid_pwr,$totalpac);
  # total kwh of all inverters 
  post2vz($vzurl,$cuuid_kwh,$etotal);
  if ($daytimeonly) {
    printf("%s PV: % 5d Watt % 10.3f kwH sun rise:%s set:%s\n",$daytime["time"],$totalpac,$etotal,$daytime["rise"],$daytime["set"]); 
  } else {
    printf("PV Current/Total: %4.0f Watt %.3f kwH\n",$totalpac,$etotal); 
  }
  exit(0);
?>
vzapihelper.php
<?php
  /**
   * vzapi helper functions
   * $Header: /home/wf/youless/RCS/vzapihelper.php,v 1.3 2014/05/30 06:57:23 wf Exp $
   */
 
  /**
   * get a curl channel
   */
  function curl($url) {
    //  Initiate curl
    $ch = curl_init();
    // Disable SSL verification
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
    // Will return the response, if false it print the response
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    // Set the url
    curl_setopt($ch, CURLOPT_URL,$url);
 
    return $ch;
  }
 
  /**
   * read the given url
   * @param $url:the url to read from
   */
  function readUrl($url)  {
    $ch=curl($url);
 
    // Execute
    $result=curl_exec($ch);
    return $result;
  }
 
  /**
   * post to the given url
   */
  function postUrl($url,$fields) {
     $ch=curl($url);
     $fields_string="";
     //url-ify the data for the POST
    foreach($fields as $key=>$value) { 
      $fields_string .= $key.'='.$value.'&'; 
    }
    rtrim($fields_string, '&');
    curl_setopt($ch,CURLOPT_POST, count($fields));
    curl_setopt($ch,CURLOPT_POSTFIELDS, $fields_string);  
    $result=curl_exec($ch);
    return $result;
   }
 
   /**
    * post data to vz middleware
    *   param 1: vzurl - middleware url of volkszaehler
    *   param 2: channel uuid 
    *   param 3: value to post
    */
   function post2vz($vzurl,$cuuid,$value,$debug=false) {
     // post data to middleware according to: 
     // http://wiki.volkszaehler.org/development/api/reference
 
     // adapt timestamp to volkszaehler conventions
		 $timestamp=time()*1000;
 
     # first 
     $posturl=$vzurl."/".$cuuid.".json";
     $fields=array("ts"=>$timestamp,"value" => $value );
     $presult=postUrl($posturl,$fields);
     if ($debug) {
       echo $presult;
     }
   }
 
  /**
   * check that option $opt is available in $options
   * return the value if available
   */
  function checkoption($opt,$options) {
    if (array_key_exists($opt,$options))
      return $options[$opt];
    else
      die("option $opt missing!\n");
  }
 
?>

SMA Sunny Boy

Den SMA Sunny Boy kann man per URL auslesen, dazu muss man die Default seite von dem Sunny Boy freischalten, so dass man auch online login die Daten sehen kann. Dann kann man per request die JSON daten abfragen.

http://sunnyboyurl/dyn/getDashValues.json json response example:

...
"6100_40263F00": {
"1": [
{
"val": 1488
}
]
},
...

Hierzu ein python script, dass den aktullen Wert in eine Datei schreibt, die wir dann mit dem vzlogger auslesen können:

import requests
import time
import logging
import argparse


logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s  [%(name)-12.12s] [%(threadName)-12.12s] [%(levelname)-5.5s]  %(message)s",
    handlers=[logging.StreamHandler()],
)

sma_host = : str = None
out_put_file  : str = None

def pullData():
    try:
        data = requests.get(
            "http://" + sma_host + "/dyn/getDashValues.json", timeout=5
        ).json()
        value = data["result"]["012F-730C09D7"]["6100_40263F00"]["1"][0]["val"]
        logging.debug(data)
        if value:
            return value
    except Exception as e:
        logging.error("Unexpected error: {}".format(e))
    return -1


def main():
    while True:
        value = pullData()
        if value != -1:
            if out_put_file:
                f = open(out_put_file, "w")
                f.write(str(value))
                f.close()
            else:
                print(value)
        time.sleep(5)


if __name__ == "__main__":
    print("SMA sunny boy file writer logger v0.1")
    parser = argparse.ArgumentParser()
    parser.add_argument("--sma", "-s", type=str, help="sma sunny boy host name")
    parser.add_argument("--out", "-o", type=str, help="output file name is not set sysout is used")
    args = parser.parse_args()

    if not args.sma:
        print("missing sma host name parameter")
        exit(-1)

    #global sma_host
    
    sma_host = args.sma
    print("connect to sma host: " + sma_host)

    if args.out:
        out_put_file = args.out
        print("use output file: " + out_put_file)
    else:
        print("output file not set use sysout")
        

    main()

vzlogger.conf file meter:

 {
      "enabled": true,
      "allowskip": false,
      "aggtime": 300,
      "channels": [
        {
          "api": "volkszaehler",
          "uuid": "60964f00-9f85-11ea-8200-37539aba2eb7",
          "identifier": "",
          "middleware": "http://localhost/middleware.php",
          "aggmode": "AVG",
          "duplicates": 0
        }
      ],
      "protocol": "file",
      "path": "/home/pi/sma/sma.log",
      "rewind": true
    }