DEYE Wechselrichter: Unterschied zwischen den Versionen
Die Seite wurde neu angelegt: „= Balkonkraftwerk: DEYE Wechselrichter ohne Cloud auslesen und per MQTT monitoren = Anfang März habe ich endlich ein '''Balkonkraftwerk''' über Amazon beste…“ |
|||
Zeile 24: | Zeile 24: | ||
Diese Lösung ist als Docker Container implementiert, was mir aber zu viel Overhead ist. Da es sich in Python realisiert ist, starte ich das Skript ganz einfach direkt, ohne Docker. | Diese Lösung ist als Docker Container implementiert, was mir aber zu viel Overhead ist. Da es sich in Python realisiert ist, starte ich das Skript ganz einfach direkt, ohne Docker. | ||
== Installation == | |||
Das Projekt von Github laden und in einem Verzeichnis speichern, Python ist normalerweise ja schon installiert. | |||
Unter Debian habe ich im Skript '''deye_cli.sh''' python durch python3 ersetzen müssen: | |||
<pre>#!/bin/bash | |||
set -a; source config.env; set +a | |||
python3 deye_cli.py "$@" | |||
</pre> | |||
Ausserdem habe ich ein kleines ''Wrapper Skript'' geschrieben, um '''Daten des WR einfacher lesen und schreiben''' zu können: | |||
'''deye_inverter.sh''' [-c <pause>] r <register> | w <register> <value> | |||
<pre>#!/bin/bash | |||
# Read / Write DEYE Inverters | |||
# via deye_inverter_mqtt python package | |||
# https://github.com/kbialek/deye-inverter-mqtt | |||
retries=2 # retry deye_inverter command multiple times | |||
sleep=5 # sleep time between retries | |||
# locate script in deye_inverter_mqtt directory | |||
cd $(dirname $0) | |||
if [ ! -f ./deye_cli.sh ]; then | |||
echo "Error: ./deye_cli.sh not found." | |||
echo " Please move $(basename $0) in deye-inverter-mqtt directory." | |||
exit 1 | |||
fi | |||
log_info () | |||
{ | |||
. ./config.env | |||
if [ ${LOG_LEVEL} = "INFO" ]; then | |||
logger -t $(basename $0) $@ | |||
fi | |||
} | |||
log_error () | |||
{ | |||
echo 2>&1 $@ | |||
} | |||
# read parameters: mode [rw], register, value and optional -c <check pause> | |||
pause=0 | |||
while [ $# -gt 0 ]; do | |||
case $1 in | |||
-c|--check) shift | |||
[ $# -ge 3 ] || exit 1 | |||
pause=$1 | |||
;; | |||
-w|--write) mode=w | |||
shift | |||
[ $# -eq 2 ] || exit 1 | |||
reg=$1 | |||
val=$2 | |||
shift | |||
;; | |||
-r|--read) mode=r | |||
shift | |||
[ $# -eq 1 ] || exit 1 | |||
reg=$1 | |||
;; | |||
esac | |||
shift | |||
done | |||
# handle offline deye_inverter by | |||
# ${pause} > 0 -> endless loop | |||
# ${pause} = 0 -> error after ${retries} | |||
while true; do | |||
# try deye_cli.sh $retries times every $sleep secs | |||
check=${retries} | |||
while [ ${check} -gt 0 ]; do | |||
case ${mode} in | |||
r) result=$(./deye_cli.sh $mode $reg | grep 'int: ') | |||
if [ -n "$result" ]; then | |||
echo "$result" | |||
log_info -t $0 deye_cli.sh $mode $reg | |||
exit 0 | |||
fi | |||
;; | |||
w) result=$(./deye_cli.sh $mode $reg $val) | |||
if [ "$result" = "Ok" ]; then | |||
log_info -t $0 deye_cli.sh $mode $reg $val | |||
exit 0 | |||
fi | |||
;; | |||
esac | |||
# wait ${sleep} or ${pause} secs | |||
sleep $(( ${pause} ? ${sleep} : ${pause} )) | |||
# try ${retries} times | |||
(( check-- )) | |||
done | |||
# error, or retry endless until wakeup of deye_inverter | |||
if [ ${pause} -eq 0 ]; then | |||
log_error "Error: deye_cli.sh $mode $reg $val failed." | |||
exit 1 | |||
fi | |||
done | |||
</pre> | |||
Obiges Skript '''deye_inverter.sh -r 22''' liest z.B. das Register 22 aus dem Wechselrichter aus, '''deye_inverter.sh -w 22 5892''' schreibt das Jahr 2023 und den Monat April (23 *256 + 4) in Register 22. | |||
Das benötigt man, um dem WR jeden Morgen zum Start Datum und Uhrzeit beizubringen. Damit wird auch der Tageszähler täglich auf 0 gesetzt, ansonsten bliebe der alte Wert stehen und der Tageszähler würde einfach stetig addieren, wie der Gesamtertragszähler. | |||
Also, jede Nacht geht der WR aus, und sobald er beim ersten Tageslicht wieder startet möchte ich Datum und Uhrzeit setzen und anschliessend die Werte kontinuierlich auslesen und über MQTT ausgeben. | |||
Folgendes Skript starte ich jeden Morgen um 04:00 Uhr per Cron (und in rc.local natürlich): | |||
'''deye_daemon.sh''' | |||
<pre>#!/bin/bash | |||
# reset Daily_Power of DEYE Inverters | |||
# via deye_inverter_mqtt python package | |||
# https://github.com/kbialek/deye-inverter-mqtt | |||
# config: | |||
deye_inverter_mqtt_cfg='config.env' | |||
deye_inverter_mqtt_cmd='python3 deye_docker_entrypoint.py' | |||
# installation: | |||
# locate script in deye_inverter_mqtt directory | |||
cd $(dirname $0) | |||
if [ ! -f deye_cli.sh -o ! -f ${deye_inverter_mqtt_cfg} ]; then | |||
echo "Error: ./deye_cli.sh not found." | |||
echo " Please move $(basename $0) in deye-inverter-mqtt directory." | |||
exit 1 | |||
fi | |||
# start deye_inverter_mqtt | |||
deye_inverter_mqtt_start () | |||
{ | |||
# read config file | |||
set -a; source ${deye_inverter_mqtt_cfg}; set +a | |||
# detach deye_inverter_mqtt with screen | |||
screen -d -m ${deye_inverter_mqtt_cmd} | |||
} | |||
# get deye_inverter_mqtt PIDs | |||
deye_inverter_mqtt_pid () | |||
{ | |||
ps ax | grep "${deye_inverter_mqtt_cmd}" | grep -v grep | grep -i -v SCREEN | awk '{print $1}' | |||
} | |||
# stop all runnning deye_inverter_mqtt instances | |||
deye_inverter_mqtt_stop () | |||
{ | |||
# get PIDs and stop them | |||
deye_mqtt_pid=`deye_inverter_mqtt_pid` | |||
[ -z ${deye_mqtt_pid} ] || kill ${deye_mqtt_pid} | |||
# check if any still runs | |||
deye_mqtt_pid=`deye_inverter_mqtt_pid` | |||
if [ ! -z ${deye_mqtt_pid} ]; then | |||
# be patient for kill cmd, try again | |||
logger -t deye_inverter "Info: found running deye_inverter_mqtt" | |||
sleep 1 | |||
[ -z ${deye_mqtt_pid} ] || kill ${deye_mqtt_pid} 2>/dev/null | |||
# check again | |||
deye_mqtt_pid=`deye_inverter_mqtt_pid` | |||
if [ ! -z ${deye_mqtt_pid} ]; then | |||
logger -t deye_inverter "Error: couldn't stop running deye_inverter_mqtt" | |||
echo -n "Error: couldn't stop running deye_inverter_mqtt (${deye_mqtt_pid})" | |||
echo | |||
exit 1 | |||
fi | |||
fi | |||
} | |||
# store actual date + time into vars | |||
get_date_time () | |||
{ | |||
year=$(date +%y) | |||
month=$(date +%m | sed 's/^0//g') | |||
day=$(date +%d | sed 's/^0//g') | |||
hour=$(date +%H | sed 's/^0//g') | |||
minute=$(date +%m | sed 's/^0//g') | |||
second=$(date +%S | sed 's/^0//g') | |||
} | |||
# ------------------------ | |||
# MAIN: Script starts here | |||
# ------------------------ | |||
# stop any runnning deye_inverter_mqtt processes | |||
deye_inverter_mqtt_stop | |||
# wait for deye inverter connection by reading (old) date | |||
val22_old=$(./deye_inverter.sh -c 60 -r 22 | sed 's/int: //g' | cut -d, -f1) | |||
logger -t deye_inverter "date/time update initialized" | |||
# ensure with loop, that all values are written | |||
while [ -z ${daily_reset} ]; do | |||
# read (old) day (this also waits for deye inverter start in the morning) | |||
val23_day=$(./deye_inverter.sh -c 10 -r 23 | sed 's/.*h: //g' | cut -d, -f1) | |||
# set actual year, month, day, hour, minute, second | |||
get_date_time | |||
# calculate register 22-24 by date and time | |||
val22=$(( ${year} * 256 + ${month} )) | |||
val23=$(( ${day} * 256 + ${hour} )) | |||
val24=$(( ${minute} * 256 + ${second} )) | |||
# reset date only once a day -> nothing to do = exit loop | |||
[ ${val22_old} = ${val22} -a ${val23_day} = ${day} ] && break | |||
# reset daily_power by | |||
# setting register 22-24 with actual date + time | |||
./deye_inverter.sh -w 22 $val22 || continue | |||
./deye_inverter.sh -w 23 $val23 || continue | |||
./deye_inverter.sh -w 24 $val24 || continue | |||
# exit loop if all vars are written | |||
daily_reset=done | |||
done | |||
logger -t deye_inverter "date/time updated" | |||
# stop old runnning deye_inverter_mqtt processes | |||
# shouldn't find any running instance | |||
deye_inverter_mqtt_stop | |||
# start deye_inverter_mqtt as daemon | |||
deye_inverter_mqtt_start | |||
logger -t deye_inverter "MQTT started" | |||
</pre> |
Version vom 15. April 2023, 17:22 Uhr
Balkonkraftwerk: DEYE Wechselrichter ohne Cloud auslesen und per MQTT monitoren
Anfang März habe ich endlich ein Balkonkraftwerk über Amazon bestellt und installiert. Es ist ja inzwischen keine Raketenwissenschaft mehr, und die Akzeptanz und Möglichkeiten zur einfachen Installation per Do-It-Yourself haben mich nun endlich Fakten schaffen lassen.
Ich habe zwei Standard PV-Module je 410W samt einem passendem DEYE Wechselrichter (WR) mit Anschlusskabel als Komplettpaket bestellt. Da ich am Balkon schon eine Klimaanlage stehen habe, ist der Stromanschluss auch kein Problem gewesen. Und endlich gilt im Sommer: Wenn es heiß ist und die Klimaanlage läuft, produzieren die PV-Module ordentlich Strom dafür, außerdem ich muss dann keinen Überschuss verschenken.
Zum Start hatten die DEYE Wechselrichter, die meines Wissens identisch zu z.B. Bosswerk, Blaupunkt und Turbo Energy sind, allerdings noch ein Security Problem mit der Firmware, darüber hatte u.a. Heise berichtet. Ein Firmware Upgrade stand kurz darauf zur Verfügung und wurde automatisch eingespielt.
Die DEYE Wechselrichter haben bereits WLAN eingebaut, d.h. steckt die wirklich nur in die Steckdose und die PV-Module und kann loslegen. Die Einbindung ins eigene WLAN erfolgt wie üblich mittels eingebauten WLAN Accesspoint und internem Webserver, wo man die Konfiguration vornimmt und dann am besten den WLAN-AP deaktiviert. Die Internetverbindung des Wechselrichters habe ich dann über meine Fritzbox gesperrt.
Der WR ist über WLAN nur erreichbar, wenn die PV-Module eine Spannung (DC) erzeugen. Der Anschluss an das Stromnetz (AC) ist hierfür irrelevant. Das heisst wenn es dunkel ist, ist er nicht erreichbar bzw. aus.
Der WR stellt bietet verschiedene Möglichkeiten zur Konfiguration und Abfrage der aktuellen Werte:
- einen internen Webserver (http://10.10.10.254)
- die Cloud Anbindung zu Solarman (Solarman Smart App)
- das Modbus Protokoll am Port 8889
- AT+ Befehle über Port 48889
Ich verwende das Modbus Protokoll zum auslesen des WR, das
- effizient/schnell ist (Performance)
- ohne Internetverbindung auskommt
Für DEYE kompatible WR stehen inzwischen einige Lösungsansätze zur Verfügung. Ich verwende die DEYE Inverter MQTT Bridge von Krzysztof Białek, da ich hier bereits einen MQTT Server innerhalb der Heimautomatisierungslösung FHEM zur Einbindung meiner Tasmota Devices (Shelly; Hichi) eingerichtet habe.
Diese Lösung ist als Docker Container implementiert, was mir aber zu viel Overhead ist. Da es sich in Python realisiert ist, starte ich das Skript ganz einfach direkt, ohne Docker.
Installation
Das Projekt von Github laden und in einem Verzeichnis speichern, Python ist normalerweise ja schon installiert. Unter Debian habe ich im Skript deye_cli.sh python durch python3 ersetzen müssen:
#!/bin/bash set -a; source config.env; set +a python3 deye_cli.py "$@"
Ausserdem habe ich ein kleines Wrapper Skript geschrieben, um Daten des WR einfacher lesen und schreiben zu können:
deye_inverter.sh [-c <pause>] r <register> | w <register> <value>
#!/bin/bash # Read / Write DEYE Inverters # via deye_inverter_mqtt python package # https://github.com/kbialek/deye-inverter-mqtt retries=2 # retry deye_inverter command multiple times sleep=5 # sleep time between retries # locate script in deye_inverter_mqtt directory cd $(dirname $0) if [ ! -f ./deye_cli.sh ]; then echo "Error: ./deye_cli.sh not found." echo " Please move $(basename $0) in deye-inverter-mqtt directory." exit 1 fi log_info () { . ./config.env if [ ${LOG_LEVEL} = "INFO" ]; then logger -t $(basename $0) $@ fi } log_error () { echo 2>&1 $@ } # read parameters: mode [rw], register, value and optional -c <check pause> pause=0 while [ $# -gt 0 ]; do case $1 in -c|--check) shift [ $# -ge 3 ] || exit 1 pause=$1 ;; -w|--write) mode=w shift [ $# -eq 2 ] || exit 1 reg=$1 val=$2 shift ;; -r|--read) mode=r shift [ $# -eq 1 ] || exit 1 reg=$1 ;; esac shift done # handle offline deye_inverter by # ${pause} > 0 -> endless loop # ${pause} = 0 -> error after ${retries} while true; do # try deye_cli.sh $retries times every $sleep secs check=${retries} while [ ${check} -gt 0 ]; do case ${mode} in r) result=$(./deye_cli.sh $mode $reg | grep 'int: ') if [ -n "$result" ]; then echo "$result" log_info -t $0 deye_cli.sh $mode $reg exit 0 fi ;; w) result=$(./deye_cli.sh $mode $reg $val) if [ "$result" = "Ok" ]; then log_info -t $0 deye_cli.sh $mode $reg $val exit 0 fi ;; esac # wait ${sleep} or ${pause} secs sleep $(( ${pause} ? ${sleep} : ${pause} )) # try ${retries} times (( check-- )) done # error, or retry endless until wakeup of deye_inverter if [ ${pause} -eq 0 ]; then log_error "Error: deye_cli.sh $mode $reg $val failed." exit 1 fi done
Obiges Skript deye_inverter.sh -r 22 liest z.B. das Register 22 aus dem Wechselrichter aus, deye_inverter.sh -w 22 5892 schreibt das Jahr 2023 und den Monat April (23 *256 + 4) in Register 22.
Das benötigt man, um dem WR jeden Morgen zum Start Datum und Uhrzeit beizubringen. Damit wird auch der Tageszähler täglich auf 0 gesetzt, ansonsten bliebe der alte Wert stehen und der Tageszähler würde einfach stetig addieren, wie der Gesamtertragszähler.
Also, jede Nacht geht der WR aus, und sobald er beim ersten Tageslicht wieder startet möchte ich Datum und Uhrzeit setzen und anschliessend die Werte kontinuierlich auslesen und über MQTT ausgeben.
Folgendes Skript starte ich jeden Morgen um 04:00 Uhr per Cron (und in rc.local natürlich):
deye_daemon.sh
#!/bin/bash # reset Daily_Power of DEYE Inverters # via deye_inverter_mqtt python package # https://github.com/kbialek/deye-inverter-mqtt # config: deye_inverter_mqtt_cfg='config.env' deye_inverter_mqtt_cmd='python3 deye_docker_entrypoint.py' # installation: # locate script in deye_inverter_mqtt directory cd $(dirname $0) if [ ! -f deye_cli.sh -o ! -f ${deye_inverter_mqtt_cfg} ]; then echo "Error: ./deye_cli.sh not found." echo " Please move $(basename $0) in deye-inverter-mqtt directory." exit 1 fi # start deye_inverter_mqtt deye_inverter_mqtt_start () { # read config file set -a; source ${deye_inverter_mqtt_cfg}; set +a # detach deye_inverter_mqtt with screen screen -d -m ${deye_inverter_mqtt_cmd} } # get deye_inverter_mqtt PIDs deye_inverter_mqtt_pid () { ps ax | grep "${deye_inverter_mqtt_cmd}" | grep -v grep | grep -i -v SCREEN | awk '{print $1}' } # stop all runnning deye_inverter_mqtt instances deye_inverter_mqtt_stop () { # get PIDs and stop them deye_mqtt_pid=`deye_inverter_mqtt_pid` [ -z ${deye_mqtt_pid} ] || kill ${deye_mqtt_pid} # check if any still runs deye_mqtt_pid=`deye_inverter_mqtt_pid` if [ ! -z ${deye_mqtt_pid} ]; then # be patient for kill cmd, try again logger -t deye_inverter "Info: found running deye_inverter_mqtt" sleep 1 [ -z ${deye_mqtt_pid} ] || kill ${deye_mqtt_pid} 2>/dev/null # check again deye_mqtt_pid=`deye_inverter_mqtt_pid` if [ ! -z ${deye_mqtt_pid} ]; then logger -t deye_inverter "Error: couldn't stop running deye_inverter_mqtt" echo -n "Error: couldn't stop running deye_inverter_mqtt (${deye_mqtt_pid})" echo exit 1 fi fi } # store actual date + time into vars get_date_time () { year=$(date +%y) month=$(date +%m | sed 's/^0//g') day=$(date +%d | sed 's/^0//g') hour=$(date +%H | sed 's/^0//g') minute=$(date +%m | sed 's/^0//g') second=$(date +%S | sed 's/^0//g') } # ------------------------ # MAIN: Script starts here # ------------------------ # stop any runnning deye_inverter_mqtt processes deye_inverter_mqtt_stop # wait for deye inverter connection by reading (old) date val22_old=$(./deye_inverter.sh -c 60 -r 22 | sed 's/int: //g' | cut -d, -f1) logger -t deye_inverter "date/time update initialized" # ensure with loop, that all values are written while [ -z ${daily_reset} ]; do # read (old) day (this also waits for deye inverter start in the morning) val23_day=$(./deye_inverter.sh -c 10 -r 23 | sed 's/.*h: //g' | cut -d, -f1) # set actual year, month, day, hour, minute, second get_date_time # calculate register 22-24 by date and time val22=$(( ${year} * 256 + ${month} )) val23=$(( ${day} * 256 + ${hour} )) val24=$(( ${minute} * 256 + ${second} )) # reset date only once a day -> nothing to do = exit loop [ ${val22_old} = ${val22} -a ${val23_day} = ${day} ] && break # reset daily_power by # setting register 22-24 with actual date + time ./deye_inverter.sh -w 22 $val22 || continue ./deye_inverter.sh -w 23 $val23 || continue ./deye_inverter.sh -w 24 $val24 || continue # exit loop if all vars are written daily_reset=done done logger -t deye_inverter "date/time updated" # stop old runnning deye_inverter_mqtt processes # shouldn't find any running instance deye_inverter_mqtt_stop # start deye_inverter_mqtt as daemon deye_inverter_mqtt_start logger -t deye_inverter "MQTT started"