Zendure Solarflow in Fhem via MQTT einbinden

Begonnen von Reinhart, 15 Februar 2024, 17:29:53

Vorheriges Thema - Nächstes Thema

Shadow3561

Moin,

ich versuche gerade meinen Hyper2000 ein zu binden.
Es klappt alles nur bekomme ich keine readings.

was muss ich bei
Zitat<Ser.Nr.Hub>:<appKey>/<Client Topic>/state:.* { json2nameValue($EVENT) }

<Client Topic>
einfügen?

Mit freundlichen Grüßen

Shadow3561

#16
Dann antworte ich mir mal selbst und stelle die Lösung hier rein, falls andere das gleiche Problem haben.
als erstes die erforderliche Daten besorgen

in OSX ein Terminal öffnen und folgendes eingeben


curl -H "Content-Type: application/json" -X POST -d '{"snNumber":"<Serial vom Hub oder Inverter>","account": "Username/email von der Zendure App>"}' https://app.zendure.tech/eu/developer/api/applymit Enter bestätigen,

Jetzt solltet ihr folgende Meldung bekommen
{"code":200,"success":true,"data":{"appKey":"xxxxxxxxxx","secret":"xxxxxxxxxxxx","mqttUrl":"mqtt-eu.zen-iot.com","port":1883},"msg":"Successful operation"}%
appKey und secret abspeichern, die benötigen wir noch.




MQTT2 Client definieren


defmod ZendureCloud MQTT2_CLIENT mqtt-eu.zen-iot.com:1883
attr ZendureCloud autocreate complex
attr ZendureCloud clientId <appKey>
attr ZendureCloud rawEvents .*
attr ZendureCloud room Zendure
attr ZendureCloud subscriptions <appKey>/#
attr ZendureCloud username <appKey>

danach ein
set ZendureCloud password <secret>
jetzt noch
set ZendureCloud connect
Nun sollte der state "opened" angezeigt werden.

jetzt ein neues Device anlegen

defmod MQTT2_ZendureHyper2000 MQTT2_DEVICE eCXh0pnS
attr MQTT2_ZendureHyper2000 icon mqtt_bridge_2
attr MQTT2_ZendureHyper2000 readingList <Serial vom Hub oder Inverter>:<appKey>/<appKey>/.* { json2nameValue($EVENT) }
attr MQTT2_ZendureHyper2000 room Zendure

Jetzt abwarten bis die readings erscheinen.

Hier noch ein paar Änderungen für stateFormat und userReading die ich vorgenommen habe.

attr MQTT2_ZendureHyper2000 stateFormat Akkustand: <span style='color:{(ReadingsNum("$name",'state_packData_1_socLevel','')>=50 ? "green":"orange")}'>[$name:state_packData_1_socLevel] %</span> <span style='color:{(ReadingsVal("$name",'Akku-be-endladen','on') eq "entlädt" ? "red":"green")}'>[$name:Akku-be-endladen] </span><br/>\
Akku-Restzeit: <span style='color:{(ReadingsNum("$name",'akku-remaining','')>=50 ? "green":"orange")}'>[$name:akku-remaining] </span><br/>\
PV-in: <span style='color:{(ReadingsNum("$name",'state_solarInputPower','')<=0 ? "red":"green")}'>[$name:state_solarInputPower] W</span>\

attr MQTT2_ZendureHyper2000 userReadings Akku-be-endladen {if(ReadingsVal($NAME,"state_packState","") eq "1") {return "lädt"} elsif (ReadingsVal($NAME,"state_packState","") eq "0") {return "standby"} elsif (ReadingsVal($NAME,"state_packState","") eq "2") {return "entlädt"} else {"prüfen"}},\
akku-remaining {if(ReadingsVal($NAME,"state_remainOutTime","") eq "59940") {return "∞"} elsif (ReadingsVal($NAME,"state_packState","") ne "59940") {return ( strftime('%T', gmtime(ReadingsNum($NAME, "state_remainOutTime", 0))) )}}
attr MQTT2_ZendureHyper2000 verbose 0

Mit freundlichen Grüßen


RalfP

Hallo,

habe mir mal verschiedene Lösungen angeschaut und den ersten Schritt für Fhem umgesetzt - Login und Token für einen MQTT Client

https://github.com/RP-Develop/Zendure

Ist noch nicht perfekt, hilft aber einen MQTT Client so zu konfigurieren, das er im Prinzip die Funktion der Zendure App übernimmt. Läuft seit einigen Tagen, ohne das ich den Token erneuern musste als Mosquitto Bridge. Output setzen funktioniert!

Bin am überlegen, ob ich einen eigenen MQTT Client integriere, um ein Token-Refresh besser hinzubekommen. Liegt aber noch in weiter Ferne. Mal sehen was ihr sagt.

Testen - natürlich auf eigene Gefahr und möglichst erst in einer Testinstanz von Fhem.

Achtung! Wenn jemand Logs oder Konfigurationen posten möchte, bitte Nutzernamen, Passwörter und deviceId's unkenntlich machen. Das Ganze ist ein POC und an mancher Stelle noch nicht ganz sicher.

Grüße Ralf

Shadow3561

#18
Moin,
Weil ich auf eine schnellere Aktualisierung der readings hoffe habe ich es mal probiert und bekomme leider keinen Login hin.

es sieht so aus:
define <name> MQTT2_CLIENT mqtteu.zen-iot.com:1883
set <name> password oK#*********
attr <name> username zenApp
attr <name> clientId *****:*******-***-****-***-*******
attr <name> autocreate no
attr <name> subscriptions

FHEM meckert bei "attr <name> subscriptions" weil kein Eintrag gemacht wird, also habe ich es erst mal weg gelassen.

Jedoch bekomme ich keinen connect hin.
Im Log steht
Connection refused, bad user name or password
Mit freundlichen Grüßen


RalfP

Hallo Shadow,

ja, das liegt daran, das du dich am EU Server in deiner App angemeldet hast. Der hat ein anderes Password. Es gab mal die Aussage, das der EU Server nicht richtig funktioniert, was ich damals auch in meiner App merkte. Darum hatte ich schon frühzeitig auf Global gewechselt. Und wie es so ist, habe ich alles darauf ausgelegt, sorry. Wenn du wechseln würdest, gehen dir alle bisherigen Daten verloren, so meine Kenntnis.

Bitte sei noch etwas geduldig. Habe gerade eine Lösung mit einer Anmeldung im MQTT Client am entwickeln. Ich versuche die unterschiedlichen Server gleich mit zu beachten.

Grüße Ralf

RalfP

Hallo,

hatte einen guten Tipp von Rudi bekommen, wie man das Attr connectFn im MQTT2_CLIENT benutzen kann, um den Client mit einem Login zu versehen. Schaut mal bei https://github.com/RP-Develop/Zendure vorbei.

Theoretisch hab ich alles beschrieben. Bei Fragen bitte melden.

@Shadow: Da ich nicht wieder auf einen EU Server wechseln kann, hätte ich natürlich gern ein Feedback, ob die eine, wie die andere, Variante mit dem EU Server überhaupt arbeitet. Vielen Dank

Interessant wären auch, ob mehr als ein Gerät angelegt wird, wenn jemand >1 Geräte in seiner App hat. Habe selbst nur ein HUB 2000.

Grüße Ralf

Shadow3561

Moin Ralf,
Ich bin auch auf den globalen Server gewechselt. Daher kann ich es mit der EU-Variante nicht probieren.

Dein Modul funktioniert wunderbar und die readings kommen schnell(schneller als bei der Variante ohne Modul) und zuverlässig.

Bei mir erscheint bei einem "get DeviceList" nur der Hyper2000. Der Schelly pro 3EM taucht nicht in der Liste auf.

Danke für deine tolle Arbeit.

Mit freundlichen Grüßen

RalfP

Hallo Shadow,

hört sich gut an, vielen Dank.

Einen Stromzähler Pro 3EM hab ich auch. Den habe ich aber lokal auf meinem Mosquitto MQTT eingebunden. Der sendet in keine Cloud (hätte ich mir für Zendure auch gewünscht). Auch wenn du ihn mit der Cloud verbunden hast und den CT-Mode (glaube so nannte sich das) arbeitest, hätte ich mich gewundert, wenn Zendure die Daten auf ihrem Server präsentiert.

Diese Regelung mache ich lokal bei mir mit Fhem. Hauptsächlich steuere ich derzeit den Umrichter je nach Verbrauch (Daten vom 3EM), um keine Energie ins Netz zu verschenken, sondern in die Batterie zu speichern. Mit den jetzigen Steuermöglichkeiten kann ich dan Ganze bestimmt nochmals optimieren und vor allem den HUB auf meine Einstellungen wieder setzen, wenn er mal aus war.

PS: lass mal hören, wie lange der Token bei dir gültig ist. Einen habe ich schon über eine Woche aktiv. Mir geht es darum, ob man ihn mal refreshen muss.

Grüße Ralf 

Shadow3561

Der Token sollte solange gültig sein bis sich ein anderes Gerät mit dem selben einloggt.
Wie steuerst du den Hyper 2000 mit FHEM?
Das würde mich brennend interessieren da die CT_Mode Steuerung nicht wirklich zuverlässig funktioniert.
Meine PV speist teilweise über Stunden ins Netz ein bevor der Hyper anfängt aus der Steckdose zu laden.
Genauso speist er den Überschüssigen Strom aus den angeschlossenen PV nicht ins Netz ein obwohl der Akku voll ist. Dann geht PV einfach auf 0 obwohl "ins Netz einspeisen" auf zugelassen steht.

Wenn du also deinen Code oder deine DEF zur Steuerung des Hyper bereitstellen würdest wäre ich dir sehr dankbar.

BTW
Was bewirken die Bypass und autoRecover Einstellungen in deinem Modul?

Mit freundlichen Grüßen

RalfP

#24
Hallo Shadow,

Bypass - ist unter den Einstellungen deines HUB in der Zendure App zu finden.

Bypass auto - der HUB regelt es selbst. Er soll erkennen wann die Batterie voll ist und dann alles weitere an Energie an den Umrichter geben. Mein Hoymiles (über AHOYDTU - MQTT angesteuert) hat in dieser Situation komplett auf gemacht und sämtliche Energie ins Netz geliefert. Das Ganze hat sich aber nicht wieder zurückgestellt und hat selbst Batterieenergie ins Netz gespeist. War für mich somit keine Option.
Bypass immer an - Die ganze Energie wird an den Umrichter geliefert.
Bypass immer aus - Wenn die Batterie voll ist und nichts abgenommen wird, werden die Eingänge der Paneele zu gemacht. Diese Funktion nutze ich.

Du darfst diesen Dateianhang nicht ansehen.

Standart ist auto, was sich leider auch nach einem ausgeschalteten HUB wieder setzt.

autoRecover ist der Schalter unter Bypass Schalter, der auf Bypass ein oder aus reagiert und den nächsten Tag den Bypass wieder auf auto schalten würde.

Da ich aber Bypass AUS und den autoRecover auch immer auf AUS haben möchte, hab ich Beides zum Steuern definiert.

Jetzt zu deiner anderen Frage, welche nicht mit wenig Erklärung zu erläutern ist.

Man kann im Allgemeinen dem Umrichter sagen, wieviel Energie er vom HUB abfordern soll oder man setzt den Output des HUB. Beides ist möglich. Wenn nur 100W gefordert werden (Umrichter auf 100W begrenzt oder Output HUB auf 100W gesetzt) aber die PV's 200W gerade liefert, gehen 100W in die Batterie. Bedeutet, wenn dein Haushalt gerade nur 100W verbraucht (ermitteln über Pro 3EM) musst du den Umrichter oder den HUB so einstellen, das er nur 90W oder 80W liefert, um nicht unter 0 zu kommen und etwas verschenkt wird. Der Rest geht automatisch in die Batterie.

Das Ganze habe ich in einem zyklisch ablaufenden Script behandelt. Das wird aller 2min aufgerufen. Dabei wird geprüft, welche Leistung gerade mein Haushalt benötigt. Ich regele nicht genau auf 0, das würde das System evtl. zu schnell machen und zum Überschwingen neigen. Darum lasse ich immer nur feste Schrittweiten zu (30W). Als unterer Grenze habe ich 20W festgelegt. Liege ich im Verbrauch über einem Bereich von 20W + 30W erhöhe ich den Umrichter um 30W, um wieder in Richtung 20W zu kommen. Nach 2min wird wieder nachgeschaut, liege ich immer noch höher, wird die Leistung am Umrichter um weitere 30W erhöht. Das ganze auch wieder anders herum, nur mit doppelter Schrittweite.

Das Ganze hat den Vorteil, das es nicht zu einem übermäßigen Überschwingen ins Negative kommt, wenn Verbraucher (z.B. Wasserkocher) eingeschaltet werden, die nur für kurze Zeit hohe Energie ziehen.

Du darfst diesen Dateianhang nicht ansehen.
Du darfst diesen Dateianhang nicht ansehen. 

Ich kann hier zwar mein Script dazu posten, es wird aber maximal nur als Anregung dienen, da du es nicht 1:1 verwenden kannst.

Derzeit steuere ich meinen Umrichter. Wenn du aber keine Schnittstelle zu deinem Umrichter hast, hättest du jetzt die Möglichkeit mit Zendure Fhem den Output vom HUB zu steuern und damit die Regelung aufzubauen.

...habe es eigentlich gut kommentiert, sonst würde ich selbst nicht mehr wissen, was ich da programmiert hab  ;)


+*00:02:00 {
    # ToDo
    #

    # Verbrauch
    my $total_act_power = ReadingsVal("WG.Energie","total_act_power",0);
   
    # Ausgangsleistung Inverter
    # Wenn Bypass an ist bzw. die Batterie voll und Schaltung auf Bypass auto steht,
    # wird die Limitierung des Inverters übergangen und alles von den Paneelen wird geliefert.
    # ToDo: Limit auf 100% dann...???
    my $P_AC = ReadingsVal("WG.Balkonkraftwerk","P_AC",0);
   
    # Batterie Status
    # 0    standbyBat
    # 1    loadBat (Laden)
    # 2 unloadBat (Enladen)   
    my $packState = ReadingsVal("WG.Balkonkraftwerk.HUB","packState",0);
    my $electricLevel = ReadingsVal("WG.Balkonkraftwerk.HUB","electricLevel",0);
    my $socSet = ReadingsVal("WG.Balkonkraftwerk.HUB","socSet",0) / 10;
    my $inverseMaxPower = ReadingsVal("WG.Balkonkraftwerk.HUB","inverseMaxPower",0);
   
    # Output Limit vom HUB
    my $outputLimit = ReadingsVal("WG.Balkonkraftwerk.HUB","outputLimit",0);
   
    # Reading Inverter available
    # 0    not available and not producing
    # 1    available but not producing
    # 2    available and producing
    # 3    available and was producing
    # 4    was available
    my $available = ReadingsVal("WG.Balkonkraftwerk.Inverter","available",0);

    # aktuelles Limit
    my $limit_act = ReadingsVal("WG.Balkonkraftwerk.Inverter","limit",0);
   
    # Default
    my $default = ReadingsVal("WG.Balkonkraftwerk.control","default",0);
   
    # Control
    my $controlLevel = ReadingsVal("WG.Balkonkraftwerk.control","controlLevel",20);
    my $controlStep = ReadingsVal("WG.Balkonkraftwerk.control","controlStep",50);
    my $controlFirstStart = ReadingsVal("WG.Balkonkraftwerk.control","controlFirstStart",0);
    my $controlBatteryLevel = ReadingsVal("WG.Balkonkraftwerk.control","controlBatteryLevel",20);
    my $controlBatteryLevelReached = ReadingsVal("WG.Balkonkraftwerk.control","controlBatteryLevelReached",20);
   

    # State
    # 0 keine Regelung
    # 1 in Regelung
    # 2 in Regelung, aber Begrenzung auf default, wegen Batterie unter 20%
    # 3 Inverter nicht verfügbar
    # 4 Limit noch nicht verarbeitet, Abbruch
    # 5 Sonderbetrieb weil Batterie voll oder Bypass geschaltet   
    my $controlState = ReadingsVal("WG.Balkonkraftwerk.control","controlState",0);
   
    # Regelung an, wenn Batterie voll ist == 1
    my $controlBatteryFull = ReadingsVal("WG.Balkonkraftwerk.control","controlBatteryFull",0);

    # Überwachung "set limit", derzeit nur Test und Log
    my $controlSetCount = ReadingsVal("WG.Balkonkraftwerk.control","controlSetCount",0);
   
    # Prüfung, ob Bypass auf "immer aus" steht, sonst setzen. Nach Reboot vom Hub ist sonst Automatik eingeschaltet
    my $controlBypass = ReadingsNum("WG.Balkonkraftwerk.control","controlBypass",0);
    my $passMode = ReadingsNum("WG.Balkonkraftwerk.HUB.global","report_properties_passMode",0);
    my $autoRecover = ReadingsNum("WG.Balkonkraftwerk.HUB.global","report_properties_autoRecover",0);
    if($controlBypass == 1){
        fhem("set WG.Balkonkraftwerk.HUB.global Bypass 1") if($passMode != 1);
        fhem("set WG.Balkonkraftwerk.HUB.global autoRecover 0") if($autoRecover != 0);
    }
   
    if($available == 2){

        # Abbruch, wenn das letzte Limmit noch verarbeitet wird
        unless($limit_act =~ m/set limit/){
            # Wenn letztes Limit verarbeitet wurde...
           
            # Zähler zurück auf 0
            fhem("set WG.Balkonkraftwerk.control controlSetCount 0");
           
            # Begrenzung bis Batterie Level einmal erreicht
            if(($electricLevel >= $controlBatteryLevel) && ($controlBatteryLevelReached == 0)){
                fhem("set WG.Balkonkraftwerk.control controlBatteryLevelReached 1");
                $controlBatteryLevelReached = 1;
            }
           
            # Umrechnung in Watt
            my $limit_watt = int(($limit_act * 600) / 100);


            # Grundzustand herstellen???
            # Inverter auf NULL ???
            # Nein, Wechselrichter startet mit 100% und wird dann runtergeregelt
           
            # Erster Start Limit wird auf default gesetzt

            if(($electricLevel < $socSet) || ($controlBatteryFull == 1)){
                # Wenn Batterie nicht voll ist, dann...

                if((ReadingsVal("WG.Balkonkraftwerk.control","state","off") eq "on") && ($controlFirstStart == 1)){
                    # wenn Regelung eingeschaltet und Inverter in Produktion, dann...

                    # Status setzen
                    $controlState = 1;

                    if($total_act_power > $controlLevel){
                        # sind es mehr als Step die ausgeregelt werden können?
                        if(($total_act_power - $controlLevel) > $controlStep){
                            # neuer Wert
                            $limit_watt = $limit_watt + $controlStep;

                            # Grenzen prüfen
                            ($limit_watt = $outputLimit)if($limit_watt > $outputLimit);

                            # Batterie unter Level und noch nicht drüber gewesen, dann begrenzen auf Default
                            if(($limit_watt > $default) && ($controlBatteryLevelReached == 0)){
                                $limit_watt = $default;
                               
                                # Status setzen
                                $controlState = 2;
                               
                            }
                        }

                        # nichts tun, ist gut eingestellt

                    }
                    else{
                        # neuer Wert, *2 um schneller wieder herunterzukommen, um nicht zu stark ins Minus zu kommen
                        $limit_watt = $limit_watt - ($controlStep * 2);

                        # Grenzen prüfen
                        ($limit_watt = 0)if($limit_watt < 0);
                    }
                }
                else{
                    # Wenn keine Regelung, dann Defaultwert aus Dummy
                    $limit_watt = $default;
                   
                    # Erster Start setzen
                    fhem("set WG.Balkonkraftwerk.control controlFirstStart 1") if($controlFirstStart == 0);

                    # Status setzen
                    $controlState = 0;
                }
            }
            else{
                # Wenn Batterie in Standby und voll, dann Limit auf 100% (max Inverterleistung im HUB)
                $limit_watt = $inverseMaxPower;  

                # Status setzen
                $controlState = 5;
            }

            # Umrechnung in Prozent
            my $limit_pct = int(($limit_watt * 100) / 600);

            # Wert setzen, wenn er ungleich zum Aktuellen ist und beim ersten Start
            if(($limit_act != $limit_pct) || ($controlFirstStart == 0)){
                fhem("set WG.Balkonkraftwerk.Inverter limit $limit_pct");
            }
        }
        else{
            # Wenn Limit noch verarbeitet wird nichts weiter tun
           
            # Zähler zu Kontrolle erhöhen
            $controlSetCount++;
            fhem("set WG.Balkonkraftwerk.control controlSetCount $controlSetCount");
            Log3 undef, 1, "Regelung: set limit count = ".$controlSetCount;
           
            # Status setzen
            $controlState = 4;
        }
    }
    else{
        # Wenn der Inverter nicht verfügbar ist nichts weiter tun
       
        # Batterie Level erreicht zurücksetzen
        fhem("set WG.Balkonkraftwerk.control controlBatteryLevelReached 0") if($controlBatteryLevelReached == 1);

        # Erster Start rücksetzen
        fhem("set WG.Balkonkraftwerk.control controlFirstStart 0") if($controlFirstStart == 1);
       
        # Status setzen
        $controlState = 3;
    }
   
    # Status setzen
    fhem("set WG.Balkonkraftwerk.control controlState $controlState");
   
}

Grüße Ralf

rudolfkoenig

Woher weiss das Programm, welche der 3 Phasen belastet wurde?
Tut mir leid, falls mein Unwissen auf dem Gebiet hier zu offensichtlich wird.

schwatter

Der Zähler ist saldierend. Daher kann immer mit der Gesamtsumme gerechnet werden.

Gruß schwatter

RalfP

Hallo Rudolf,

vielen Dank hier auf diesem Wege nochmals, mit dem Tipp betreffs connectFn beim MQTT_CLIENT. Wie du siehst hab ich es erfolgreich umgesetzt, wie die ersten Tests zeigen.

Der Shelly Pro 3EM ist ein 3 Phasen Zähler, den man zentral in seinen Sicherungskasten einbauen muss. Er saldiert die 3 Phasen. Die Abrechnung des Versorgers unterscheidet auch nicht zwischen den Phasen. Der Gesamtwert kommt über 'total_act_power' per MQTT vom Shelly Pro 3EM rein.

Ich erkläre es immer so: Akzeptiere immer die Anzeige deines Stomzählers. Er berechnet dir gerade, was aus deinem Haushalt rausgeht, oder auch reingeht, immer über alle 3 Phasen. Das bezahlst du auch nur an den Versorger. Damit ist es für den Haushalt egal, ob gerade über eine Phase eingespeist wird und über eine andere gerade Strom gezogen wird. Die Summe macht es.

Ist die Summe negativ, schenkt man dem Versorger Energie. Darum mein Versuch die Regelung so zu optimieren, das ich überschüssige Energie im Akku speichere und später nutze.

Hier noch eine Erklärung im Netz dazu:
https://solarblitz.blogspot.com/2018/08/einspeisung-auf-einer-oder-3-phasen.html

Grüße Ralf

blofield

Steuert einer von euch mit RalfP's Modul einen Hyper2000?

Ich vermute ich verstehe es noch nicht ganz oder mache etwas falsch, aber ob ich outputLimit setze oder nicht, da passiert nix.
Fehlermeldungen im Log habe ich keine. Bin auf dem EU Server. Habe das ConfigProposal angenommen...

Grüße
blofield

Shadow3561

#29
Moin,
Ich steuere einen Hyper2000.

Das Output-Limit kann man nur setzen wenn die Energiepläne wie Smart-CT oder Zeitsteuerung ausgeschaltet sind.

Mfg