Inhaltsverzeichnis

Kostal Piko

Diese Wikiseite zeigt diverse Ansätze zur Einbindung von Kostal Wechselrichtern an den Volkszähler über die Ethernetschnittstelle der Wechselrichter.

Abhängig von der Firmware werden die Daten nur über ein Webfrontend (in html) ausgegeben oder zusätzlich per JSON. Gerade als JSON kann eine Vielzahl an Werten abgefragt werden, wesentlich mehr als über das Webfrontend.
Diese Daten werden ausgelesen und an die Volkszähler-Middleware übergeben.

Zum Auslesen und Anzeigen der Messwerte genügt ein Raspberry Pi. Ausgangsbasis für diese Anleitung ist das vorbereitete Volkszähler-Image. Es ist aber nicht notwendig, dass Middleware und Datenbank auf dem selben System liegen.

JSON

Über den Weg können die meisten Werte abgefragt werden, müssen aber auch konkret angefragt werden. Es gibt eine Reihe Listen in denen die dazu nötige IDs aufgelistet sind, mal mehr mal weniger Umfangreich.
Dem Zähler werden die IDs in einer http-Anfrage zugestellt und er antwortet mit eine strukturierten JSON in der die aktuellen Werte zu diesen IDs enthalten sind.

Typ ID Einheit
Leistungswerte
DC-Leistung 33556736 W
AC-Leistung 67109120 W
PV Gesamtproduktion 251658753 kW
Batterie
Ladezustand 33556229 %
Ladezyklen 33556228 Int
Temperatur 33556227 °C
Stromspannung 33556226 V
Stromstärke 33556238 A
Laden/Entladen 33556230 bool

Eine umfangreichere Liste

Software vorbereiten

Um den JSON-String vom Wechselrichter auswerten zu können ist ein zusätzliche Tool nötig. Installation mit

sudo apt install jq

Zur Übermittlung der Daten an die Middleware nutzen wir vzlogger mit meter-exec. Um den nutzen zu können sollte vzlogger nicht unter User root laufen. Im Image ist das vorbereitet, falls nicht nachholen: vzlogger_als_anderer_benutzer_ausfuehren

Kanäle anlegen

Für dieses Beispiel wurden 2 Kanäle angelegt:

Kanal 1: "PV Zählerstand"
Typ: El Energie (Zählerstände)
style: steps
öffentlich: x
Farbe: aqua
aktiv: x
Kanal 1: "PV aktuelle Leistung"
Typ: El Energie (Leistungwerte)
style: steps
öffentlich: x
Farbe: black
aktiv: x

Die UUID notieren, die wird noch benötigt.

Bash-Script

Als Bindeglied zwischen vzlogger und Wechselrichter dient ein kurzes Script.

kostal.sh
#!/bin/bash
set -u
set -e
 
# preliminaries
HOSTNAME="$1"
shift
READINGS="$*"
 
URL="http://${HOSTNAME}/api/dxs.json?"
 
# build up URL
for ADDURL in $READINGS; do
    URL="${URL}dxsEntries=${ADDURL}&"
    echo $URL
done
URL=$(echo $URL | sed 's/.$//') # remove last character (&) from URL
echo $URL
 
# get time and json
NOW="$(date +%s)"
CURLOUT=$(curl --connect-timeout 5 -s $URL)
 
# process json
for READING in $READINGS; do
    OUTPUT=$(echo $CURLOUT | jq ".dxsEntries[] | select(.dxsId == $READING) | .value")
#    OUTPUT=$(cat pico.json | jq ".dxsEntries[] | select(.dxsId == $READING) | .value")
    if [ -n "$OUTPUT" ]; then # only if value present
        printf "%d: %s = %s\\n" "${NOW}" "${READING}" "${OUTPUT}"
    else
        printf >&2 "no reading %s\\n" "${READING}"
        exit 1
    fi
done

Unter /usr/local/bin/kostal.sh ablegen und mit chmod +x ausführbar machen.

vzlogger

In der vzlogger-Konfiguration diesen Abschnitt hinzufügen:

json
        {
            "enabled": true,
            "allowskip": true,
            "protocol": "exec",
            "command": "/usr/local/bin/kostal.sh 192.168.1.99 251658753 67109120",
            "format": "$t: $i = $v",
            "interval": 10,
            "channels":  [
                {
                    "uuid": "c673b290-fdac-11e0-a470-1d9351203a00",
                    "identifier": "251658753",
                    "middleware": "http://localhost/middleware.php"
                },
                {
                    "uuid": "e640a820-7a2a-11e8-b04b-0fe628c3aa8d",
                    "identifier": "67109120",
                    "middleware": "http://localhost/middleware.php"
                }]
        }

Folgende Stellen müssen bei Bedarf angepasst werden:

„command“: „/usr/local/bin/kostal.sh 192.168.1.99 251658753 67109120“ sagt vzlogger wo er das Script findet (/usr/local/bin/kostal.sh), welche IP-Adresse der Wechselrichter hat (192.168.1.99) und welche IDs (in dem Fall 2: 251658753 & 67109120) abgefragt werden sollen. Möchte man mehr Daten holen die gewünschten IDs einfach, durch ein Leerzeichen getrennt, anhängen.

Im Abschnitt channels weisen wir dann die IDs den bereits erstellten Kanälen zu. „identifier“: „251658753“ Die ID steht für Gesamtertrag und entspricht einem Zählerstand. Dementsprechend steht im selben Abschnitt unter „uuid“: „c673b290-fdac-11e0-a470-1d9351203a00“ die tatsächliche UUID des Kanals (die UUID hier ist nur ein Platzhalter).
Für Kanal/ID „aktuelle Leistung“ (oder auch zusätzliche) gilt das Selbe.

Das sollte es gewesen sein, vzlogger starten mit

sudo systemctl start vzlogger

Webfrontend

In dem Fall werden die als http bereitgestellten, aktuellen Werte angefordert und maschinell extrahiert. Es gibt 2 Lösungsansätze dazu.

Kanäle im Frontend erstellen

Für den Wechselrichter zwei Kanäle anlegen:

Kanal 1: "PV aktuelle Leistung"
Typ: El Energie (Leistungwerte)
style: steps
öffentlich: x
Farbe: black
aktiv: x
Kanal 2: "PV Zählerstand"
Typ: El Energie (Zählerstände)
style: steps
öffentlich: x
Farbe: aqua
aktiv: x

Der zweite Kanal ist eigentlich nicht nötig. Man kann jedoch später den Zählerstand leichter auslesen.

Ansatz 1: PHP-Script und vzclient

Das Script htmlvz.php liest die Parameter aus der Konfigurationsdatei htmlvz.ini und schreibt die Daten über vzclient in die entsprechenden Kanäle.

Dieser Ansatz wurde verifiziert mit dem Kostal Piko 5.5.

Die korrekte Installation von vzclient überprüfen

Falls

vzclient get channel

nicht die öffentlichen Channels auflistet, fehlt vielleicht:

sudo ln -s /usr/local/etc/vzclient.conf /etc/vzclient.conf

Weitere Infos unter vzclient

PHP-Skript zum Auslesen der Wechselrichter-Webseite

Die Scriptdatei htmlvz.php und die Konfigurationsdatei htmlvz.ini im Homeverzeichnis /home/pi des Benutzers pi ablegen. Die UUID des gewünschten Kanals sowie die anderen Parameter (hoffentlich selbsterklärend) trägt man anschließend die Datei htmlvz.ini ein:

[allgemein]
; der Pfad zum Programm vzclient kann durch which vzclient ermittelt unter Linux werden
vzclient_pfad = '/usr/local/bin/vzclient'
logdatei = '/home/pi/piko.log' 

[piko]
user = 'pvserver'                         ; user = Anmeldename des Wechselrichters
passwort = 'pvwr'                         ; Passwort dazu
wechselrichter_name = 'kostal'            ; Name oder IP-Adresse des WR
UUID_aktuelle_leistung = 'abc-defg-hij'
UUID_gesamtenergieerzeugung =''
htmlvz.php
<?php
/**
 * Skript zum Auslesen von Kostal Piko Wechselrichter und
 * Schreiben in die Volkszähler DB
 * getestet mit Modell Kostal Piko 5.5 ver. 3.1
 * Konfig Datei htmlvz.ini muss vorher bearbeitet werden
 * erstellt am 16.7.2013 von Karlheinz
 * Stand: 15.09.2013
 */
 
    $ini_file = parse_ini_file("htmlvz.ini", TRUE);
    //print_r($ini_file);
    $user = $ini_file["piko"]["user"];              // User-Name i.d.R. pvserver
    $pwd = $ini_file["piko"]["passwort"];           // Passwort des WR
    $wr = $ini_file["piko"]["wechselrichter_name"]; // Name oder IP-Adresse des Wechselrichters
    $UUID_gesamt = $ini_file["piko"]["UUID_gesamtenergieerzeugung"];         //Zielkanal im VZ
    $UUID_aktuell = $ini_file["piko"]["UUID_aktuelle_leistung"];
    $logdatei = $ini_file["allgemein"]["logdatei"];
    if (empty($logdatei)) {
	echo "ini-Parameter logdatei muss gefüllt werden!";
	exit;
	}
    if (empty($wr)) {
	echo "ini-Parameter wechselrichter_name muss gefüllt werden!";
	exit;
	}
    $url = "http://$user:$pwd@$wr/index.fhtml";
    //echo "url: $url \n";
 
    $contents = '';
    $handle = fopen ($url, "r");
    //echo $handle;
 
    while (!feof($handle)) {
        $contents .= fread($handle, 8192);
    }
    fclose($handle);
    $searchtext = array("\r","\n","&nbsp");
    $contents = str_replace($searchtext, "",$contents); //lösche Return u. so
    $full = explode("</tr>",$contents);    // erzeugt ein array mit </tr> als Trennung
    foreach ( $full as $line) {
        $line = strip_tags(html_entity_decode($line));  //lösche HTML Tags
        //echo $line;
 
        if ( preg_match("/aktuell/",$line))
            {$line = str_replace(" x x x","0",trim($line));
            unset($line2);
	    $line2 = array_filter(explode(" ",$line)); // durch Leerzeichen trennen u. leere Felder löschen
	    $line2 = array_values($line2); // array Index neu sortieren
	    //print_r($line2);
	    if ($line2[1] == "W") {
		//unterschiedliche Anzeige Status An und Aus
		//add array 1 mit 0 Watt
		$line2[5] = $line2[4];
		$line2[4] = $line2[3];
		$line2[3] = $line2[2];
		$line2[2] = $line2[1];
		$line2[1] = 0;
	    }
	    //print_r($line2);
            $aktuell = $line2[1];
	    $aktuellE = $line2[2];       //Einheit von aktuell
            $Gesamtenergie = $line2[4];
	    $GesamtenergieE = $line2[5]; //Einheit von Gesamtenergie
            //echo "aktuell: $aktuell $aktuellE \nGesamtenergie: $Gesamtenergie $GesamtenergieE \n";
        }
	if ( preg_match("/Tagesenergie/",$line)) {
            //echo "$line \n";
	    unset($line2);
	    $line2 = array_filter(explode(" ",$line)); // durch Leerzeichen trennen u. leere Felder löschen
	    $line2 = array_values($line2); // array Index neu sortieren
	    //print_r($line2);
 
            $tleistung = $line2[1];  // str_replace("Tagesenergie","",$line);
	    $tleistungE = $line2[2]; // Einheit von tleistung
            //echo "Tagesenergie: $tleistung $tleistungE \n";
	    //print_r($line2);
        }
        if ( preg_match("/Status/",$line))
            { $status = trim(str_replace("Status","",$line));
            //echo "Status: $status \n";
        }
 
    } // foreach Ende
 
    // Daten mit vzclient in die DB schreiben
    //shell_exec("date >> $logdatei");
    // Zusammenfassung:
    echo date("YmdGis"), " Erzeugung aktuell: $aktuell $aktuellE, Gesamtenergie: $Gesamtenergie $GesamtenergieE, Status: $status \n";
    if (isset($UUID_aktuell) && !empty($UUID_aktuell)){
	//echo "\n not empty UUID aktuell Zweig \n";
        //shell_exec($ini_file["allgemein"]["vzclient_pfad"]." -u $UUID_aktuell add data value=$aktuell 2>> $logdatei");
        exec($ini_file["allgemein"]["vzclient_pfad"]." -u $UUID_aktuell add data value=$aktuell ", $output, $vzrc);
	//echo "vzrc: $vzrc \n";
	if ($vzrc == 0) {
	    //shell_exec("echo aktuell übertragen: $aktuell >> $logdatei");
	    echo "aktuell übertragen: $aktuell \n";
	}
	else {
	    //shell_exec("echo Übertragung nicht erfolgreich! $output >> $logdatei");
	    echo "Übertragung nicht erfolgreich: $output \n";
	}
    }
    if (isset($UUID_gesamt) && !empty($UUID_gesamt)) {
	//echo "not empty UUID Gesamt Zweig \n";
        exec($ini_file["allgemein"]["vzclient_pfad"]." -u $UUID_gesamt add data value=$Gesamtenergie ", $output, $vzrc);
	if ($vzrc == 0) {
	    //shell_exec("echo Gesamtenergie übertragen: $Gesamtenergie >> $logdatei");
	    echo "Gesamtenergie übertragen: $Gesamtenergie \n";
	}
	else {
	    //shell_exec("echo Übertragung nicht erfolgreich! $output >> $logdatei");
	    echo "Übertragung nicht erfolgreich: $output \n";
	}
        //shell_exec("echo Gesamtenergie übertragen: $Gesamtenergie >> $logdatei");
    }
    echo date("YmdGis"), " Skriptende";
 
?>

Das Script muss noch als ausführbar gekennzeichnet werden:

chmod +x htmlvz.php

htmlvz ausführen:

pi@raspberrypi ~ $ php htmlvz.php
20130916222335 Erzeugung aktuell: 100 W, Gesamtenergie: 10000 kWh, Status: Aus
aktuell übertragen: 0
Gesamtenergie übertragen: 25822
20130916222337 Skriptende 

Eine Zusammenfassung und Übertragungsbestätigung findet sich anschliessend in der log-Datei.

Um die Übertragung zu automatisieren, wird ein Cron-Job erstellt: Mit dem voreingestellten Editor hatte ich meine Probleme und habe deshalb den Editor auf nano umgestellt:

$ export EDITOR=nano

Anschliessend wird das Script alle fünf Minuten eingeplant:

$ crontab -e
*/5 * * * * /usr/bin/php htmlvz.php >> /home/pi/piko.log 2>> /home/pi/piko.log

crontab -l zeigt die Einplanung an.

Wenn das Script bereits einige Male manuell ausgeführt wurde, kann man im VZ-Frontend die öffentlichen Kanäle anzeigen.

Ansatz 2: vzlogger exec-Channel

Bei diesem Ansatz erfolgt die Abfrage des Wechselrichters mit einem Shellscript, das vom vzlogger regelmäßig mit einem vzlogger exec-Kanal aufgerufen wird. Ein dedizierter Cronjob oder vzclient sind hierbei nicht notwendig. Dieser Ansatz wurde verifiziert im Juni 2019 mit einem Kostal Piko 4.2 und Debian Buster.

Script: (im Beispiel abgelegt unter /home/pi/)

exec-pvwr.sh
#!/bin/bash
# curl --silent http://pvserver:password@pvwr01.example/index.fhtml
#  | html2text | grep --max-count=1 'aktuell.*Gesamtenergie'
#  aktuell    50   WGesamtenergie 23257   kWh                            
 
# catch errors
 
set -u
set -e
 
# preliminaries
 
HOSTNAME="$1"
shift
REPEAT="$1"
shift
READINGS="$*"
USERNAME="pvserver"
PASSWORD="password"
URL="http://${USERNAME}:${PASSWORD}@${HOSTNAME}/index.fhtml"
REGEXP="aktuell.*Gesamtenergie"
 
# SC2034
for repeat in $(seq 1 "${REPEAT}"); do
        NOW="$(date +%s)"
 
        CURLOUT="$(curl --silent "${URL}" | html2text | grep --max-count=1 "${REGEXP}")"
 
        # pull requested readings from curl output
 
        for READING in $READINGS; do
                case "$READING" in
                  "Gesamtenergie"|"aktuell")
                        OUTPUT="$(printf "%s\\n" "${CURLOUT}" | sed -n "/.*${READING}[[:space:]]\\+\\([[:digit:]]\\+\\).*/{s//\\1/;p;q;}")"
                        # PVWR prints "x x x" in the dark, that is parsed
                        # into the empty string by the sed regexp, make
                        # that zero here
                        printf "%d: %s = %d\\n" "${NOW}" "${READING}" "${OUTPUT:-0}"
                        ;;
                  *)
                        # we don't have what the user asked for, print error message
                        printf >&2 "no reading %s\\n" "${READING}"
                        exit 1
                        ;;
                esac
        done
        if [ "${repeat}" != "${REPEAT}" ]; then
                sleep 30
        fi
done

vzlogger.conf

Im Aufruf des Scripts („command“) ist die IP-Adresse (oder URL) des Wechselrichters und bei Bedarf Pfad und Durchläufe des Scripts (3) anzupassen. Es sind auch die UUID der Kanäle einzutragen die in der Middleware für den Wechselrichter angelegt wurden.

vzlogger.conf
{
"retry" : 0,
"verbosity" : 0,
"log" : "/var/log/vzlogger/vzlogger.log",
 
"local" : {
        "enabled" : false,
        "port" : 8081,
        "index" : true,
        "timeout" : 0,
        "buffer" : 0
},
 
"meters" : [
        {
            "enabled": true,
            "allowskip": true,
            "protocol": "exec",
            "command": "/home/pi/exec-pvwr.sh 192.168.1.11 3 Gesamtenergie aktuell",
                       "format": "$t: $i = $v",
            "interval": 30,
            "channels":  [
                {
                    "uuid": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
                    "identifier": "aktuell",
                    "middleware": "http://localhost/middleware.php"
                },
                {
                    "uuid": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
                    "identifier": "Gesamtenergie",
                    "middleware": "http://localhost/middleware.php"
                }
            ]
        }
    ]
}

Script für Gesamt- u. Direktverbrauch

Quellen

https://www.loxwiki.eu/pages/viewpage.action?pageId=28803569
https://www.photovoltaikforum.com/thread/150800-kostal-piko-neue-generation-in-vzlogger/