Skip to content
  • Home
  • Aktuell
  • Tags
  • 0 Ungelesen 0
  • Kategorien
  • Unreplied
  • Beliebt
  • GitHub
  • Docu
  • Hilfe
Skins
  • Light
  • Brite
  • Cerulean
  • Cosmo
  • Flatly
  • Journal
  • Litera
  • Lumen
  • Lux
  • Materia
  • Minty
  • Morph
  • Pulse
  • Sandstone
  • Simplex
  • Sketchy
  • Spacelab
  • United
  • Yeti
  • Zephyr
  • Dark
  • Cyborg
  • Darkly
  • Quartz
  • Slate
  • Solar
  • Superhero
  • Vapor

  • Standard: (Kein Skin)
  • Kein Skin
Einklappen
ioBroker Logo

Community Forum

donate donate
  1. ioBroker Community Home
  2. Deutsch
  3. Skripten / Logik
  4. JavaScript
  5. [TypeSkript] Zendure SolarFlow 2400 AC - EVCC Steuerung

NEWS

  • Jahresrückblick 2025 – unser neuer Blogbeitrag ist online! ✨
    BluefoxB
    Bluefox
    16
    1
    1.4k

  • Neuer Blogbeitrag: Monatsrückblick - Dezember 2025 🎄
    BluefoxB
    Bluefox
    13
    1
    814

  • Weihnachtsangebot 2025! 🎄
    BluefoxB
    Bluefox
    25
    1
    2.0k

[TypeSkript] Zendure SolarFlow 2400 AC - EVCC Steuerung

Geplant Angeheftet Gesperrt Verschoben JavaScript
57 Beiträge 6 Kommentatoren 2.0k Aufrufe 6 Watching
  • Älteste zuerst
  • Neuste zuerst
  • Meiste Stimmen
Antworten
  • In einem neuen Thema antworten
Anmelden zum Antworten
Dieses Thema wurde gelöscht. Nur Nutzer mit entsprechenden Rechten können es sehen.
  • L Offline
    L Offline
    lesiflo
    Most Active
    schrieb am zuletzt editiert von
    #19

    Hallo @Mabbi
    ich habe hier mal mitgelesen und einen ähnlichen Aufbau, allerdings mit 2 Hyper und habe das mit Java-Script gelöst.
    Die Akkus werden bei mir über die angeschlossenen PV-Panels und externer PV geladen. Ich steuere beide Hyper zusammen. Hier mal ein Auszug wie ich die Leistung ermittle. Bin aber in der Cloud.

    const hyper1 = {
        name:           'Hyper1',
        inputLimitDP:   'zendure-solarflow.0.xxxxxx.yyyyyyyy.control.setInputLimit',
        outputLimitDP:  'zendure-solarflow.0.xxxxxx.yyyyyyyy.control.setOutputLimit',
        inputDP:        'zendure-solarflow.0.xxxxxx.yyyyyyyy.gridInputPower',
        outputDP:       'zendure-solarflow.0.xxxxxx.yyyyyyyy.outputHomePower',
        acModeDP:       'zendure-solarflow.0.xxxxxx.yyyyyyyy.control.acMode',
        pvLeistungDP:   'zendure-solarflow.0.xxxxxx.yyyyyyyy.solarInputPower',
        chargePowerDP:  'zendure-solarflow.0.xxxxxx.yyyyyyyy.outputPackPower',
        socDP:          'zendure-solarflow.0.xxxxxx.yyyyyyyy.electricLevel',
        setsocDP:       'zendure-solarflow.0.xxxxxx.yyyyyyyy.control.dischargeLimit',
        ladenGesamtDP:  'zendure-solarflow.0.xxxxxx.yyyyyyyy.calculations.outputPackEnergyTodaykWh',
    };
    
    const hyper2 = {
        name:           'Hyper2',
        inputLimitDP:   'zendure-solarflow.0.xxxxxx.zzzzzzzz.control.setInputLimit',
        outputLimitDP:  'zendure-solarflow.0.xxxxxx.zzzzzzzz.control.setOutputLimit',
        inputDP:        'zendure-solarflow.0.xxxxxx.zzzzzzzz.gridInputPower',
        outputDP:       'zendure-solarflow.0.xxxxxx.zzzzzzzz.outputHomePower',
        acModeDP:       'zendure-solarflow.0.xxxxxx.zzzzzzzz.control.acMode',
        pvLeistungDP:   'zendure-solarflow.0.xxxxxx.zzzzzzzz.solarInputPower',
        chargePowerDP:  'zendure-solarflow.0.xxxxxx.zzzzzzzz.outputPackPower',
        socDP:          'zendure-solarflow.0.xxxxxx.zzzzzzzz.electricLevel',
        setsocDP:       'zendure-solarflow.0.xxxxxx.zzzzzzzz.control.dischargeLimit',
        ladenGesamtDP:  'zendure-solarflow.0.xxxxxx.zzzzzzzz.calculations.outputPackEnergyTodaykWh',
    };
    
    const evuLeistungDP         = 'mqtt.0.openWB.evu.W';
    const aktuelleEinspeisung   = Number(getStateVal(evuLeistungDP, 0));
    
    const hyper1Leistung = Number(getStateVal(hyper1.inputDP, 0)) - Number(getStateVal(hyper1.outputDP, 0));
    const hyper2Leistung = Number(getStateVal(hyper2.inputDP, 0)) - Number(getStateVal(hyper2.outputDP, 0));
    
    const leistungAusAkkus = aktuelleEinspeisung - hyper1Leistung - hyper2Leistung; // wie im Original
    
    // PV-Überschuss-Erkennung: negative leistungAusAkkus = Überschuss? (behalte dein Vorzeichenmodell)
    if (leistungAusAkkus < -ladenStartSchwelle && speicherMinSOC < zielSOC) {
         ladenErlaubt = true;                
         visLog('☀️ PV-Überschuss → Laden erlaubt', 'ok');
    }
    

    Kann gerne mal das gesamte Script posten, ist aber sehr an meine Bedingungen angepasst.

    1 Antwort Letzte Antwort
    0
    • U Offline
      U Offline
      Unterums
      schrieb am zuletzt editiert von
      #20

      Hallo,
      ich suche jemanden der mir (auch gerne gegen Bezahlung) hilft,

      einen Flow einzurichten in IO-Broker. Habe die Zendure Batterie mittels MQTT zum Luafen gebracht und würde nun gerne folgende Werte schreiben können:
      1.) Laden über AC Ein / Aus
      2.) Entladen über AC Ein / Aus
      3.) Die Leistung für 1 oder 2

      Ist hier jemand der Lust darauf hat ?

      1 Antwort Letzte Antwort
      0
      • S Online
        S Online
        Schimi
        schrieb am zuletzt editiert von
        #21

        @unterums wenn ich aus das richtig verstehe, macht mein Javascript genau das... Du musst nur Tibber und evcc auf "false" setzen und deinen Stromzähler angeben (z.B. shelly oder Tibber Pulse.. es gehen auch alle anderen die ein + für bezug und - für einspeisung liefern)

        U 1 Antwort Letzte Antwort
        0
        • S Schimi

          @unterums wenn ich aus das richtig verstehe, macht mein Javascript genau das... Du musst nur Tibber und evcc auf "false" setzen und deinen Stromzähler angeben (z.B. shelly oder Tibber Pulse.. es gehen auch alle anderen die ein + für bezug und - für einspeisung liefern)

          U Offline
          U Offline
          Uberga
          schrieb am zuletzt editiert von
          #22

          @schimi Danke, aber wo "baue" ich das Java Script ein ?
          Sorry, aber ich bin da absolut unbewandert :-)

          M 1 Antwort Letzte Antwort
          0
          • U Uberga

            @schimi Danke, aber wo "baue" ich das Java Script ein ?
            Sorry, aber ich bin da absolut unbewandert :-)

            M Offline
            M Offline
            Mabbi
            schrieb am zuletzt editiert von Mabbi
            #23

            @uberga

            Hi,
            das script von @Schimi kommt im Iobroker hier hin, einfach anclicken:
            7bd0e407-5adb-4d08-a5ff-3dd3e535a135-grafik.png

            Dann clickst du hier auf das PLUS:
            0b2149f3-0f2b-40d1-90d4-63e6b90e7075-grafik.png

            Wählst das hier aus:
            0946d761-da16-42a9-926b-1a694a9930a3-grafik.png

            Vergibst einen Namen (Verzeichnis würde ich erstmal nicht ändern):
            528fe866-598d-4f9a-8511-1ff4736e7eab-grafik.png

            Und fügst den kopierten Text aus dem ersten Post in den Editor ein, dann SPEICHERN

            Sollte dann so aussehen (Name kann abweichen)
            7e7e7982-230d-42ea-b2a5-25deeca43722-grafik.png

            Mit dem Play/Pause startet das script bzw stoppt.

            ABER.... ich würde Dir nicht empfehlen als Anfänger gleich mit Javascripten zu arbeiten.
            Ehrlich, schreib ein paar kleine Blocklys, verstehe wie Datenpunkte angelegt, angesprochen und manipuliert werden und dann kannst du auch Deinen Akku selber steuern mit einem kleinen script und Erfahrung.
            Die Akkus sind keine Spielzeuge. Du solltest wirklich verstehen wie du die scripte überwachst und dann reagierst.

            Schau mal, ich arbeite seit Wochen an einem Akku-Script... es gibt idz. X-Versionen und ganz glücklich bin ich aktuell immer noch nicht mit dem Ergebnis (wobei das nun eher Schönbauerei ist als reale technische Verbesserung idZ.)
            13464734-e50a-4776-8d4a-d0c982e6c69d-grafik.png

            SMA Wechselrichter Probleme seit letztem Update

            1 Antwort Letzte Antwort
            1
            • S Online
              S Online
              Schimi
              schrieb am zuletzt editiert von
              #24

              es kann definitiv nicht schaden sich mal mit blockly auseinander zu setzen...

              Mein Skript braucht zwar nur konfiguriert werden und sollte funktionieren... aber ein grundverständnis schadet nie :-)

              PS: es gab wieder ein Update (siehe ersten post)

              1 Antwort Letzte Antwort
              0
              • L Offline
                L Offline
                lesiflo
                Most Active
                schrieb am zuletzt editiert von lesiflo
                #25

                Hi, noch ein Tip von mir:
                Du solltest, falls du den Wert hast, deinen Rest-Tagesverbrauch in kW/h welcher durch PV gedeckt wird, noch von deinem Tages Rest-PV abziehen. Dann bekommst du einen noch besseren Restwert zum Laden deiner Akkus. Ich verwendet zusätzlich noch den Genauigkeitsfaktor aus PV-Prognose und tatsächlicher PV Energie, den ich dann mit in die Berechnung der restliche PV Leistung mit einfließen lasse. Wenn dann PV nicht reicht wird über Tibber geladen (aber nur falls das wirtschaftlich Sinn macht)

                So z.B.: pvRest_Real = (pvRest - verbrauchRest) * Faktor

                Gerade was die teilweise sehr ungenaue PV-Prognose angeht solltest du den Genauigkeitsfaktor mit einbauen. Hier mal ein Beispiel von heute wie ungenau die Vorhersage ist. War aber auch nur sehr wenig Sonne.

                f56a4563-7630-41d0-8708-00ea0a10c9e8-image.png

                Ich nutze übrigens für die PV-Vorhersage solarprognose.de. Der errechnet die Genauigkeit gleich mit. Gibt auch einen Adapter dafür.

                1 Antwort Letzte Antwort
                1
                • S Online
                  S Online
                  Schimi
                  schrieb am zuletzt editiert von
                  #26

                  Danke für den Tipp... mit der Genauigkeit habe ich auch so "meine probleme"...

                  das mit dem Resttagesverbrauch ist aber schwer vorauszusehen...

                  aber ich mach mir da nochmal gedanken... das mit dem Genauigkeitsfaktor ist definitiv ein guter Tipp

                  1 Antwort Letzte Antwort
                  0
                  • L Offline
                    L Offline
                    lesiflo
                    Most Active
                    schrieb am zuletzt editiert von
                    #27

                    Ich speichere mir den Tagesverbrauch in einer influxDB ab. Daraus errechne ich mir den durchschnittlichen Restverbrauch bis Sonnenuntergang der letzten 5 Tage und ziehe diesen dann von der Rest PV-Leistung ab. Mit Hilfe von ChatGPT war das relativ einfach umzusetzen.

                    1 Antwort Letzte Antwort
                    1
                    • S Online
                      S Online
                      Schimi
                      schrieb am zuletzt editiert von Schimi
                      #28

                      update auf 2.8

                      @lesiflo habe da mal was (auf deinen Hinweis hin) mit de Prognose umgestrickt...

                      Leider ist zur Zeit ja nicht soviel "PV", deshalb dauern die feldtests was länger...
                      Das mit dem Tagesverbrauche schaue ich mir noch an.... das will ich aber ne Zeitlang testen...

                      1 Antwort Letzte Antwort
                      0
                      • M Offline
                        M Offline
                        Mabbi
                        schrieb am zuletzt editiert von Mabbi
                        #29

                        Ich denke der Ertrag und somit die Gnauigkeit variieren stark.
                        Unterm Strich kann man die Werte von solarprognose schon gut nehmen, ich teste aber seit einiger Zeit daran, wie umstehende Objekte wie Bäume etc. Einfluss auf die real produzierte Leistung haben.
                        Ist deutlich kniffeliger als ich dachte, bei Bewölkung sind die Bäume in der Auswirkung eher geringer im Effekt als bei klarem Himmel.
                        Mit fehlen im Augenblick noch Testdaten für einen groben Überblick.
                        Da sich die Verschattung ja auch noch im Jahresverlauf deutlich ändert ist das sehr schwer zu interpolieren.
                        Einen festen Faktor je Tagestunde zu finden ist nicht der richtige Weg, eher anhand von Sonnen-Auf- und Untergang eine mögliche Ertragskurve für die eigene Anlage zu generieren, stundeweise aufzusplitten und in die Solarprognose einzurechnen.

                        Bevor es verwirrend wird... insgesamt ein spannendes Thema.

                        SMA Wechselrichter Probleme seit letztem Update

                        1 Antwort Letzte Antwort
                        0
                        • S Online
                          S Online
                          Schimi
                          schrieb am zuletzt editiert von
                          #30

                          ich bin gerade komplett im flow und habe jetzt eine Prognose auf basis der letzten 14 tage (taggenau)... wonach entschieden wird ob aus dem netz geladen wird oder nicht....

                          Um das aber nun zu testen, muss ich erstmal daten sammeln ;-)
                          also denke ich, nen update wird nicht vor Weihnachten kommen.... (aktuell Version 2.22)

                          Ich habe langsam die "angst" das es zuviel ist.... als Backup habe ich ja das Script vor der PV-Prognose... damit war ich eigentlich schon sehr zufrieden

                          1 Antwort Letzte Antwort
                          0
                          • L Offline
                            L Offline
                            lesiflo
                            Most Active
                            schrieb am zuletzt editiert von lesiflo
                            #31

                            Der solarprognose Adapter rechnet falsch wenn der aktuelle Wert größer ist als die Prognose.

                            a4f0db9e-13af-47a1-ae9d-99d1a4ccbc6d-image.png

                            S 1 Antwort Letzte Antwort
                            0
                            • L lesiflo

                              Der solarprognose Adapter rechnet falsch wenn der aktuelle Wert größer ist als die Prognose.

                              a4f0db9e-13af-47a1-ae9d-99d1a4ccbc6d-image.png

                              S Online
                              S Online
                              Schimi
                              schrieb am zuletzt editiert von
                              #32

                              Habe mal dein issue im GutHub erstellt

                              1 Antwort Letzte Antwort
                              0
                              • M Offline
                                M Offline
                                Mabbi
                                schrieb am zuletzt editiert von Mabbi
                                #33

                                Kleines Update, habe ja schon etwas länger nicht mehr zum eigentlichen thread-topic was gepostet:

                                Man sieht das Diagramm wie die Akus Laden/Entladen und im Hintergrund die SOC der beiden Akkus,
                                darunter die Produktivzeiten der Kimas:
                                grafik.png

                                Mein script läuft, heute war Schietwetter, morgens schon der akku Ebbe (weil gestern auch Schietwetter war).
                                Die Akkus haben dann brav geladen, den Start der ersten Klima (Heizung) unterstützt und über den Tag bis zu 3 Klimas parallel am Leben gehalten.
                                Abends war dann wieder Ebbe in den Akkus.

                                Ergo... ich brauche wohl mehr PV... lol

                                Habe nun eine schöne Balance mit wenig Relaisgeglacker.

                                Realisiert über eine:

                                • Totzone um das Einspeiseziel rum -> weniger Steuerregelungen
                                • AC-Mode Switch Delay das dynamisch ist. Prüfe off-Loop ob die Zendure beide geschaltet haben und das jeweilige Powersetting annehmen und gebe dann erst die eigentliche Steuerung wieder frei
                                • eine permanente Überwachung von Soll- und Istwerten beim Laden/Entladen der Akkus -> weniger Steuereingriffe
                                • Sollten die Akkus mehrere Zyklen von den geforderten Werten abweichen werden Sie per Brute Force in die Spur gebracht während die Dynamik pausiert und dann mit verifizierten Start-Werten anstartet. Ist noch ein alter fail-safe, wird faktich aber nicht mehr getriggert.
                                • etc.

                                Integration der Ladesäule und Klimageräte ist in die Akkusteuerung abgeschlossen, dort bin ich auch sehr zufrieden mit der Funktionalitätund Stabilität/Vorhersagbarkeit des Systems.

                                Todo/Vison:
                                Das eigentliche Ziel fürs kommende WE ist vollkommen aus einem Timed Loop bei der Steuerung auszusteigen.
                                DIe Überwachung wird dann auch nur dynamisch nach reinem Steuerbefehl aktiviert werden.
                                Ziel ist es (Ich kann ja sicher nur ca. alle 15 Sekunden zu regeln(kurze Zwischentakte mit ca. 8 Sekunden sind möglich aber nicht bulletproof in der Ausführung), bei Netzbezug im optimalen Fall schon mehr als 15 Sekunden seit der letzten Regelung zu haben und dann instant und nicht erst im Timer reagieren zu können.
                                Und bei steigendem LADEN-Potenzial reichen mir eigentlich 50 oder 100 Watt Schritte, ich muss da nicht jedem Elektron hinterhecheln denke ich.

                                Wird aber tricky denke ich, das ganze System dann synchron und ohne flattern zu halten.
                                Prio sollte sein: Schnell regeln zu können, wenn es anfängt Geld zu Kosten (Netzbezug) und entspannt wenn man lädt.

                                Idz. ist das schon ein relativ üppiges Blockly geworden:
                                882f3968-5ef3-4e87-af58-cc5bf1a15454-grafik.png

                                Und das hier beeinhaltet nicht die Änderungen an den Ansteuerungen der Klimas und WB sowie die ganzen Delta/Zeit Wert Berechnungen im Hintergrund.

                                SMA Wechselrichter Probleme seit letztem Update

                                S 1 Antwort Letzte Antwort
                                1
                                • M Mabbi

                                  Kleines Update, habe ja schon etwas länger nicht mehr zum eigentlichen thread-topic was gepostet:

                                  Man sieht das Diagramm wie die Akus Laden/Entladen und im Hintergrund die SOC der beiden Akkus,
                                  darunter die Produktivzeiten der Kimas:
                                  grafik.png

                                  Mein script läuft, heute war Schietwetter, morgens schon der akku Ebbe (weil gestern auch Schietwetter war).
                                  Die Akkus haben dann brav geladen, den Start der ersten Klima (Heizung) unterstützt und über den Tag bis zu 3 Klimas parallel am Leben gehalten.
                                  Abends war dann wieder Ebbe in den Akkus.

                                  Ergo... ich brauche wohl mehr PV... lol

                                  Habe nun eine schöne Balance mit wenig Relaisgeglacker.

                                  Realisiert über eine:

                                  • Totzone um das Einspeiseziel rum -> weniger Steuerregelungen
                                  • AC-Mode Switch Delay das dynamisch ist. Prüfe off-Loop ob die Zendure beide geschaltet haben und das jeweilige Powersetting annehmen und gebe dann erst die eigentliche Steuerung wieder frei
                                  • eine permanente Überwachung von Soll- und Istwerten beim Laden/Entladen der Akkus -> weniger Steuereingriffe
                                  • Sollten die Akkus mehrere Zyklen von den geforderten Werten abweichen werden Sie per Brute Force in die Spur gebracht während die Dynamik pausiert und dann mit verifizierten Start-Werten anstartet. Ist noch ein alter fail-safe, wird faktich aber nicht mehr getriggert.
                                  • etc.

                                  Integration der Ladesäule und Klimageräte ist in die Akkusteuerung abgeschlossen, dort bin ich auch sehr zufrieden mit der Funktionalitätund Stabilität/Vorhersagbarkeit des Systems.

                                  Todo/Vison:
                                  Das eigentliche Ziel fürs kommende WE ist vollkommen aus einem Timed Loop bei der Steuerung auszusteigen.
                                  DIe Überwachung wird dann auch nur dynamisch nach reinem Steuerbefehl aktiviert werden.
                                  Ziel ist es (Ich kann ja sicher nur ca. alle 15 Sekunden zu regeln(kurze Zwischentakte mit ca. 8 Sekunden sind möglich aber nicht bulletproof in der Ausführung), bei Netzbezug im optimalen Fall schon mehr als 15 Sekunden seit der letzten Regelung zu haben und dann instant und nicht erst im Timer reagieren zu können.
                                  Und bei steigendem LADEN-Potenzial reichen mir eigentlich 50 oder 100 Watt Schritte, ich muss da nicht jedem Elektron hinterhecheln denke ich.

                                  Wird aber tricky denke ich, das ganze System dann synchron und ohne flattern zu halten.
                                  Prio sollte sein: Schnell regeln zu können, wenn es anfängt Geld zu Kosten (Netzbezug) und entspannt wenn man lädt.

                                  Idz. ist das schon ein relativ üppiges Blockly geworden:
                                  882f3968-5ef3-4e87-af58-cc5bf1a15454-grafik.png

                                  Und das hier beeinhaltet nicht die Änderungen an den Ansteuerungen der Klimas und WB sowie die ganzen Delta/Zeit Wert Berechnungen im Hintergrund.

                                  S Online
                                  S Online
                                  Schimi
                                  schrieb am zuletzt editiert von
                                  #34

                                  @Mabbi mega... 💪🏼

                                  1 Antwort Letzte Antwort
                                  0
                                  • R Offline
                                    R Offline
                                    Rand
                                    schrieb am zuletzt editiert von Rand
                                    #35

                                    @schimi
                                    So, erste Versuche mit dem Skript,

                                    1. Eine kleine Verbesserung (zu faul an 10 Stellen zu ändern):
                                    var basePath= "mqtt.0";
                                    var baseID= "ID";
                                    
                                    const IDs = {
                                        // --- HAUPTZÄHLER (Pflicht) ------------------------------------------------------
                                        // Muss ein Number-Wert sein. Positiv = Netzbezug (Kauf), Negativ = Einspeisung (Verkauf).
                                        netz: "Zaehler, 
                                            
                                        // --- ZENDURE MQTT (Pflicht) -----------------------------------------------------
                                        // Diese Pfade finden Sie im MQTT-Adapter unter Ihrem Zendure-Topic.
                                        acMode: `${basePath}.Zendure.select.${baseID}.acMode`,              // Liest Modus
                                        acModeSet: `${basePath}.Zendure.select.${baseID}.acMode.set`,      // Schaltet Modus
                                        currentInput: `${basePath}.Zendure.number.${baseID}.inputLimit`,   // Liest Ladelimit
                                        inputSet: `${basePath}.Zendure.number.${baseID}.inputLimit.set`,   // Setzt Ladelimit
                                        currentOutput: `{basePath}.Zendure.number.${baseID}.outputLimit`, // Liest Entladelimit
                                        outputSet: `${basePath}.Zendure.number.${baseID}.outputLimit.set`, // Setzt Entladelimit
                                        soc: `${basePath}.Zendure.sensor.${baseID}.electricLevel`,         // Akkustand in %
                                    
                                    1. Multiple Konverter sind noch nicht unterstützt?
                                      Hab die ja um die 2400W max Lade und Entladeleistung zu umgehen, da ist dann eine Nacheinander Lösung doof, das sollte schon parallel laufen
                                      (Wobei ich noch schauen muss wie das läuft da ich noch keinen Elektriker da hatte unterschreibt das ich es sauber auf 2 Phasen einspeise) - mag ggf eine Fehlersituation sein die auftaucht - wobei ja pro Konverter 2400 gehen sollte und das System die Gesamt Upload Rate von 4800 nicht sieht...

                                    2. Muss mal logging aktivieren damit ich nachvollziehen kann was es gerade tut - es schwankt gerade immer um den Eigenverbrauch rum, zu diesig...

                                    3. Hat jemand eigentlich ein Möglichkeit gefunden die Dinger sauber auszuschalten wenn man sie nicht braucht (Akku auf minimum)? Die idle Entladung ist ja nicht gerade wenig (1% ohne Heizung), da wäre es sicherlich besser die Dinger einfach aus zu machen, aber stromlos funktionier irgendwie nur manchmal...
                                      Hab da Shelly's Sv3 dran gepackt, die könnte ich schön einbinden/einschalten wenn Solar den Eigenverbrauch übersteigt...

                                    1 Antwort Letzte Antwort
                                    0
                                    • S Online
                                      S Online
                                      Schimi
                                      schrieb am zuletzt editiert von
                                      #36

                                      @rand

                                      1. Das ist eine sehr gute Idee (habe ich angepasst)
                                      2. ne, ich hab nur einen, somit wäre das testen recht schwierig

                                      keine anung ob es so funktioniert.

                                      Funktionsweise: Ist Batterie A leer (0%), liefert sie 0W. Der PI-Regler merkt "Oh, reicht nicht" und erhöht die globale Anforderung, wodurch Batterie B&C mehr liefern müssen. Das System regelt sich also selbst aus.

                                      //-------------------------------------------------------------------------------------
                                      // ### ioBroker Skript: Multi-Batteriesteuerung (Zendure) mit PI-Regler ###
                                      //
                                      // v4.2 - 14.12.2025
                                      // - FEATURE: Soft-Start Rampe implementiert (Leistung steigt sanft, fällt schnell).
                                      // - LOGIC:   Unterstützt 3 Hubs + Watchdog (ignoriert Offline-Geräte).
                                      // - CLEAN:   Keine visuellen Datenpunkte, reiner Regelcode.
                                      //
                                      //-------------------------------------------------------------------------------------
                                      
                                      // ====================================================================================
                                      // 1. KONFIGURATION
                                      // ====================================================================================
                                      
                                      // --- ZENDURE GERÄTE -----------------------------------------------------------------
                                      const ZENDURE_DEVICES = [
                                         { 
                                             id: "HOAXXXXXXXXX",          // ID Hub 1
                                             mqttPath: "mqtt.0.Zendure1",     // Instanz Hub 1
                                             active: true 
                                         },
                                         { 
                                             id: "HOB2XXXXXXXXXXX",          // ID Hub 2
                                             mqttPath: "mqtt.0.Zendure2",     // Instanz Hub 2
                                             active: true 
                                         },
                                         { 
                                             id: "HOC3YYYYYYYYYYY",          // ID Hub 3
                                             mqttPath: "mqtt.0.Zendure3",     // Instanz Hub 3
                                             active: true 
                                         }
                                      ];
                                      
                                      // ------------------------------------------------------------------------------------
                                      
                                      const CONFIG = {
                                         // --- FUNKTIONSUMFANG ---
                                         ENABLE_TIBBER: true,             // [true/false] Tibber Preis-Logik nutzen
                                         ENABLE_EVCC: true,               // [true/false] EVCC Auto-Logik nutzen
                                         ENABLE_PV_FORECAST: true,        // [true/false] Solarprognose nutzen
                                      
                                         // --- LOGGING ---
                                         INFO_LOGS: true,                 // [true] Wichtige Infos im Log anzeigen
                                         DEBUG: false,                    // [false] Technische Details (nur zur Fehlersuche)
                                      
                                         // --- BATTERIE & SYSTEM (PRO GERÄT!) ---
                                         BATTERY_CAPACITY_KWH: 8.64,      // Akkugröße PRO TURM in kWh
                                         MAX_CHARGE_W_PER_DEVICE: 2400,   // Max Ladeleistung PRO GERÄT
                                         MAX_DISCHARGE_W_PER_DEVICE: 2400,// Max Entladeleistung PRO GERÄT
                                         
                                         PV_FORECAST_SAFETY_FACTOR: 2.0,  // Sicherheitsfaktor für Wetterprognose
                                      
                                         // --- SICHERHEIT & RAMPE ---
                                         CONNECTION_TIMEOUT_MS: 300000,   // 5 Min. Wenn Daten älter sind -> Hub offline.
                                         RAMP_STEP_W: 50,                 // Soft-Start: Max. 50W Änderung pro Intervall (nur beim Erhöhen)
                                      
                                         // --- PI-REGLER (GLOBAL) ---
                                         KP: 0.7,                         // P-Anteil
                                         KI: 0.08,                        // I-Anteil
                                         DEADBAND_W: 15,                  // Totzone
                                         TARGET_W: 0,                     // Zielwert
                                         
                                         INTEGRAL_MAX: 30000,             // Interner Speicher Max
                                         INTEGRAL_MIN: -30000,            // Interner Speicher Min
                                      
                                         // --- ZEIT ---
                                         REGEL_INTERVALL_MS: 2000,        // Regelung alle 2 Sekunden
                                         AC_MODE_COOLDOWN_MS: 5000,       // 5 Sek. Pause bei Moduswechsel
                                      };
                                      
                                      
                                      // ====================================================================================
                                      // 2. DATENPUNKTE (IDs)
                                      // ====================================================================================
                                      
                                      const IDs = {
                                         // --- HAUPTZÄHLER (Pflicht) ---
                                         netz: "shelly.0.SHEM-3#8CAAB5619A05#1.Total.InstantPower", 
                                         
                                         // --- EXTERNE LOGIK ---
                                         evccModus:   "0_userdata.0.zendure.EVCC_Modus",                        
                                         tibberLaden: "0_userdata.0.Tibber.01_Automatisierungs-Kanaele.Batterie_laden", 
                                         
                                         // --- WETTER ---
                                         pvForecastRest:  "solarprognose.0.forecast.00.energy_from_now",      
                                         pvForecastAcc:   "solarprognose.0.forecast.00.accuracy",               
                                         pvForecastTotal: "solarprognose.0.forecast.00.energy",               
                                         pvForecastSoFar: "solarprognose.0.forecast.00.energy_now"            
                                      };
                                      
                                      // Generiere dynamische IDs für alle Hubs
                                      let HUB_IDS = [];
                                      
                                      function generateHubIDs() {
                                         HUB_IDS = [];
                                         ZENDURE_DEVICES.forEach((device, index) => {
                                             if (!device.active) return;
                                             const base = device.mqttPath;
                                             const id = device.id;
                                             
                                             HUB_IDS.push({
                                                 index: index,
                                                 id: id,
                                                 acMode:        `${base}.select.${id}.acMode`,
                                                 acModeSet:     `${base}.select.${id}.acMode.set`,
                                                 currentInput:  `${base}.number.${id}.inputLimit`,
                                                 inputSet:      `${base}.number.${id}.inputLimit.set`,
                                                 currentOutput: `${base}.number.${id}.outputLimit`,
                                                 outputSet:     `${base}.number.${id}.outputLimit.set`,
                                                 soc:           `${base}.sensor.${id}.electricLevel`,
                                                 // Interner Status
                                                 lastSwitch:    0,
                                                 lastWatts:     0,    // Für die Rampe
                                                 isOnline:      true 
                                             });
                                         });
                                         if (CONFIG.DEBUG) log(`[Init] ${HUB_IDS.length} Hubs konfiguriert.`, 'info');
                                      }
                                      
                                      // ====================================================================================
                                      // AB HIER NICHTS MEHR ÄNDERN (Code-Logik)
                                      // ====================================================================================
                                      
                                      const AC_MODES = {
                                         INPUT: "Input mode",    
                                         OUTPUT: "Output mode"   
                                      };
                                      
                                      let integral = 0.0;                    
                                      
                                      // --- 4. HILFSFUNKTIONEN ---
                                      
                                      function logInfo(message) {
                                         if (CONFIG.INFO_LOGS) log(`[Info] ${message}`, 'info');
                                      }
                                      
                                      function logDebug(message) {
                                         if (CONFIG.DEBUG) log(`[Debug] ${message}`, 'info');
                                      }
                                      
                                      function getStateSafe(id) {
                                         if (!id) return 0; 
                                         try {
                                             if (!existsState(id)) return 0;
                                             const val = getState(id).val;
                                             if (val === null || val === undefined) return 0;
                                             const num = parseFloat(val);
                                             return isNaN(num) ? 0 : num;
                                         } catch (e) {
                                             return 0;
                                         }
                                      }
                                      
                                      function getStateString(id) {
                                         if (!id) return ""; 
                                         try {
                                             if (!existsState(id)) return "";
                                             const val = getState(id).val;
                                             return val ? val.toString() : "";
                                         } catch (e) {
                                             return "";
                                         }
                                      }
                                      
                                      /**
                                      * Prüft MQTT Zeitstempel (Watchdog).
                                      */
                                      function checkHubConnectivity() {
                                         const now = Date.now();
                                         let onlineCount = 0;
                                      
                                         HUB_IDS.forEach(hub => {
                                             let isOnline = false;
                                             try {
                                                 if (existsState(hub.soc)) {
                                                     const state = getState(hub.soc);
                                                     const age = now - state.ts;
                                                     if (age < CONFIG.CONNECTION_TIMEOUT_MS) {
                                                         isOnline = true;
                                                     } else {
                                                         if (hub.isOnline) {
                                                             logInfo(`WARNUNG: Hub ${hub.id} ist OFFLINE! Keine Daten seit ${(age/1000/60).toFixed(1)} Min.`);
                                                         }
                                                     }
                                                 }
                                             } catch(e) { isOnline = false; }
                                      
                                             hub.isOnline = isOnline;
                                             if (isOnline) onlineCount++;
                                         });
                                      
                                         return onlineCount;
                                      }
                                      
                                      // --- 5. INITIALISIERUNG ---
                                      
                                      function initialize() {
                                         logInfo("--- Multi-Batteriesteuerung (v4.2) gestartet ---");
                                         generateHubIDs();
                                         
                                         if (HUB_IDS.length === 0) {
                                             log("KEINE aktiven Hubs konfiguriert! Skript stoppt.", 'warn');
                                             return;
                                         }
                                      
                                         logInfo(`Konfiguriert: ${HUB_IDS.length} Hubs.`);
                                         logInfo(`Feature Konfig: Watchdog=${CONFIG.CONNECTION_TIMEOUT_MS/1000}s, Rampe=${CONFIG.RAMP_STEP_W}W`);
                                      
                                         integral = 0;
                                         const now = Date.now();
                                         HUB_IDS.forEach(h => {
                                             h.lastSwitch = now;
                                             h.lastWatts = 0;
                                         });
                                      
                                         if (CONFIG.REGEL_INTERVALL_MS < 1000) CONFIG.REGEL_INTERVALL_MS = 1000;
                                         setInterval(mainControlLoop, CONFIG.REGEL_INTERVALL_MS); 
                                         
                                         checkOverridesAndAct(); 
                                      }
                                      
                                      // --- 6. TRIGGER ---
                                      
                                      if (CONFIG.ENABLE_EVCC && IDs.evccModus) {
                                         on({ id: IDs.evccModus, change: "any" }, (obj) => {
                                             logInfo(`Trigger: EVCC Modus auf ${obj.state.val} geändert.`);
                                             checkOverridesAndAct();
                                         });
                                      }
                                      
                                      if (CONFIG.ENABLE_TIBBER && IDs.tibberLaden) {
                                         on({ id: IDs.tibberLaden, change: "any" }, (obj) => {
                                             logInfo(`Trigger: Tibber Laden auf ${obj.state.val} geändert.`);
                                             checkOverridesAndAct();
                                         });
                                      }
                                      
                                      if (CONFIG.ENABLE_TIBBER && CONFIG.ENABLE_PV_FORECAST) {
                                         if (IDs.pvForecastRest) on({ id: IDs.pvForecastRest, change: "any" }, () => checkOverridesAndAct());
                                         if (IDs.pvForecastAcc)  on({ id: IDs.pvForecastAcc, change: "any" }, () => checkOverridesAndAct());
                                      }
                                      
                                      // --- 7. LOGIK & REGELUNG ---
                                      
                                      function checkOverridesAndAct() {
                                         checkHubConnectivity(); 
                                         const onlineHubs = HUB_IDS.filter(h => h.isOnline);
                                      
                                         if (onlineHubs.length === 0) return false;
                                      
                                         // 7.1 Tibber Logik
                                         if (CONFIG.ENABLE_TIBBER && IDs.tibberLaden) {
                                             let tibberActive = false;
                                             try {
                                                 tibberActive = getState(IDs.tibberLaden).val === true;
                                             } catch(e) { tibberActive = false; }
                                             
                                             if (tibberActive) {
                                                 let chargeAllowed = false;
                                                 if (isNight()) {
                                                     chargeAllowed = true;
                                                 } else {
                                                     if (CONFIG.ENABLE_PV_FORECAST) {
                                                         if (checkPvForecastAllowsDayChargeGlobal(onlineHubs)) {
                                                             chargeAllowed = true;
                                                         }
                                                     }
                                                 }
                                                 
                                                 if (chargeAllowed) {
                                                     applyPowerToHubs(onlineHubs, AC_MODES.INPUT, CONFIG.MAX_CHARGE_W_PER_DEVICE);
                                                     integral = CONFIG.INTEGRAL_MIN; 
                                                     return true; 
                                                 }
                                             }
                                         }
                                      
                                         // 7.2 EVCC Logik
                                         if (CONFIG.ENABLE_EVCC && IDs.evccModus) {
                                             const evccMode = getStateSafe(IDs.evccModus); 
                                             if (evccMode === 2) {
                                                 applyPowerToHubs(onlineHubs, AC_MODES.OUTPUT, 0); 
                                                 integral = CONFIG.INTEGRAL_MAX; 
                                                 return true; 
                                             } else if (evccMode === 3) {
                                                 applyPowerToHubs(onlineHubs, AC_MODES.INPUT, CONFIG.MAX_CHARGE_W_PER_DEVICE);
                                                 integral = CONFIG.INTEGRAL_MIN; 
                                                 return true; 
                                             }
                                         }
                                      
                                         return false;
                                      }
                                      
                                      function mainControlLoop() {
                                         try {
                                             if (checkOverridesAndAct()) return;
                                      
                                             const onlineCount = checkHubConnectivity();
                                             if (onlineCount === 0) return;
                                      
                                             const dt = CONFIG.REGEL_INTERVALL_MS / 1000.0; 
                                             const gridPower = getStateSafe(IDs.netz); 
                                             
                                             if (!IDs.netz) return;
                                      
                                             let error = CONFIG.TARGET_W - gridPower;
                                      
                                             // Deadband
                                             if (Math.abs(gridPower) <= CONFIG.DEADBAND_W) {
                                                 error = 0.0;
                                             }
                                      
                                             // I-Anteil
                                             integral += error * dt;
                                             integral = Math.max(CONFIG.INTEGRAL_MIN, Math.min(CONFIG.INTEGRAL_MAX, integral));
                                      
                                             // P-Anteil
                                             const pShare = CONFIG.KP * error;
                                             const iShare = CONFIG.KI * integral;
                                      
                                             let totalOutputPower = pShare + iShare;
                                      
                                             // --- SPLIT LOGIC ---
                                             let powerPerDevice = totalOutputPower / onlineCount;
                                      
                                             // Clamping (Limitierung)
                                             if (powerPerDevice > 0) { 
                                                 powerPerDevice = Math.min(powerPerDevice, CONFIG.MAX_CHARGE_W_PER_DEVICE);
                                             } else if (powerPerDevice < 0) { 
                                                 powerPerDevice = Math.max(powerPerDevice, -CONFIG.MAX_DISCHARGE_W_PER_DEVICE);
                                             }
                                      
                                             const onlineHubs = HUB_IDS.filter(h => h.isOnline);
                                             applyPowerToHubs(onlineHubs, null, powerPerDevice);
                                      
                                         } catch (e) {
                                             log(`Error in MainLoop: ${e}`, 'error');
                                         }
                                      }
                                      
                                      // --- 8. HELPER ---
                                      
                                      function applyPowerToHubs(hubList, modeOverride, targetPower) {
                                         hubList.forEach(hub => {
                                             setHubPower(hub, modeOverride, targetPower);
                                         });
                                      }
                                      
                                      function setHubPower(hubConfig, modeOverride, power) {
                                         if (!hubConfig.acModeSet) return;
                                      
                                         let targetMode = AC_MODES.OUTPUT;
                                         let targetVal = 0;
                                      
                                         // 1. Ziel-Modus bestimmen
                                         if (modeOverride) {
                                             targetMode = modeOverride;
                                             targetVal = Math.abs(power);
                                         } else {
                                             power = Math.round(power);
                                             targetVal = Math.abs(power);
                                             targetMode = (power > 0) ? AC_MODES.INPUT : AC_MODES.OUTPUT;
                                         }
                                      
                                         const currentMode = getStateString(hubConfig.acMode);
                                         
                                         // 2. RAMPE IMPLEMENTIERUNG
                                         // Wir rampen den Wert nur, wenn wir im gleichen Modus bleiben.
                                         // Bei Moduswechsel wird der Rampen-Speicher zurückgesetzt.
                                         
                                         if (currentMode !== targetMode) {
                                             // Bei Moduswechsel: Zielwert sofort akzeptieren (startet meist bei 0 oder niedrig)
                                             // und Rampe Reset
                                             hubConfig.lastWatts = 0; 
                                         } else {
                                             // Im gleichen Modus: Soft Start prüfen
                                             const lastVal = hubConfig.lastWatts;
                                             const diff = targetVal - lastVal;
                                      
                                             // Rampe nur anwenden, wenn wir die LEISTUNG ERHÖHEN (diff > 0).
                                             // Wenn wir reduzieren (diff < 0), machen wir das sofort (Sicherheit/Netzbezug vermeiden).
                                             if (diff > CONFIG.RAMP_STEP_W) {
                                                 targetVal = lastVal + CONFIG.RAMP_STEP_W;
                                                 // Wenn wir rampen, loggen wir das kurz im Debug, damit man es sieht
                                                 logDebug(`[${hubConfig.id}] Rampe aktiv: ${lastVal}W -> ${targetVal}W (Ziel war ${Math.abs(power)}W)`);
                                             }
                                         }
                                         
                                         // Wert für nächsten Durchlauf speichern
                                         hubConfig.lastWatts = targetVal;
                                      
                                      
                                         // 3. Moduswechsel und Senden
                                         if (currentMode !== targetMode) {
                                             const now = Date.now();
                                             
                                             if (now - hubConfig.lastSwitch < CONFIG.AC_MODE_COOLDOWN_MS) {
                                                 const currentSetID = (currentMode === AC_MODES.INPUT) ? hubConfig.inputSet : hubConfig.outputSet;
                                                 if (getStateSafe(currentSetID) !== 0) setState(currentSetID, "0", false);
                                                 return;
                                             }
                                      
                                             logInfo(`[${hubConfig.id}] Wechsel: ${currentMode} -> ${targetMode} (${targetVal}W)`);
                                             hubConfig.lastSwitch = now;
                                             
                                             setState(hubConfig.acModeSet, targetMode, false);
                                             
                                             if (targetMode === AC_MODES.INPUT) {
                                                 setState(hubConfig.outputSet, "0", false);
                                                 setState(hubConfig.inputSet, String(targetVal), false);
                                             } else {
                                                 setState(hubConfig.inputSet, "0", false);
                                                 setState(hubConfig.outputSet, String(targetVal), false);
                                             }
                                      
                                         } else {
                                             // Gleicher Modus, nur Wert senden
                                             const targetStr = String(targetVal);
                                             if (targetMode === AC_MODES.INPUT) {
                                                 if (getState(hubConfig.inputSet).val != targetStr) setState(hubConfig.inputSet, targetStr, false);
                                             } else {
                                                 if (getState(hubConfig.outputSet).val != targetStr) setState(hubConfig.outputSet, targetStr, false);
                                             }
                                         }
                                      }
                                      
                                      /**
                                      * Forecast Berechnung basierend auf den Online-Hubs.
                                      */
                                      function checkPvForecastAllowsDayChargeGlobal(activeHubs) {
                                         try {
                                             let totalSoC = 0;
                                             let count = 0;
                                             
                                             activeHubs.forEach(h => {
                                                 totalSoC += getStateSafe(h.soc);
                                                 count++;
                                             });
                                             if (count === 0) return false;
                                             
                                             const avgSoc = totalSoC / count;
                                             const forecastRest = getStateSafe(IDs.pvForecastRest);
                                             let accuracy = getStateSafe(IDs.pvForecastAcc);
                                             if (!IDs.pvForecastAcc || accuracy <= 0) accuracy = 50; 
                                             
                                             const effectiveRest = forecastRest * (accuracy / 100.0);
                                             const totalCapacity = CONFIG.BATTERY_CAPACITY_KWH * count; 
                                             const missingCap = totalCapacity * (100 - avgSoc) / 100.0;
                                             const threshold = missingCap * CONFIG.PV_FORECAST_SAFETY_FACTOR;
                                      
                                             return effectiveRest < threshold;
                                         } catch (e) {
                                             return false; 
                                         }
                                      }
                                      
                                      function isNight() {
                                         try {
                                             const sunrise = getAstroDate('sunrise');
                                             const sunset = getAstroDate('sunset');
                                             const now = new Date();
                                             if (!sunrise || !sunset) return false; 
                                             return (now > sunset || now < sunrise);
                                         } catch (e) {
                                             return false;
                                         }
                                      }
                                      
                                      // --- Start ---
                                      initialize();
                                      

                                      1 Antwort Letzte Antwort
                                      1
                                      • R Offline
                                        R Offline
                                        Rand
                                        schrieb am zuletzt editiert von
                                        #37

                                        @schimi

                                        Das widerspricht aber der Idee das man 2/3 Geräte hat um die maximale Einspeise/Abgabe Leistung zu erhöhen. Eigentlich müssten beide/alle 3 immer als Gruppe geregelt werden (also 50%/33%) der Gesamt Ladung/Ausgabe pro Gerät.
                                        Eine gruppenbasierte Aufteilung sollte auch "trocken" relativ gut entwickelbar sein, hast halt ne Gruppe mit einem Gerät die dann 100%/1 der Eingabe/Ausgabe-Anforderung abdeckt.

                                        Theoretisch mag es auch Leute mit mehr als 3 Geräten geben (was ja technisch kein Problem ist solange sie an unterschiedlichen Sicherungen hängen), aber das wäre denke ich erstmal nur ein relativer kleiner Teil.

                                        Andere Frage: warum ist denn SetSmartmode nicht bei Dir mit drin? Ich meine ja man kann auch das andere Skript nehmen, aber du pollst / setzt ja eh schon regelmäßig da wäre das doch kein Thema...

                                        Und hast Du mal den idle Stromverbrauch betrachtet? Hatte ja im Adapterthread über Shelly's gesprochen aber habe natürlich keine Ahnung ob das gut oder schlecht für den Akku ist, wenn man versucht, ihn per Stromweg auszuschalten - und wann er denn wirklich aus geht (minSoc?) Hab Zendure gefragt aber noch keine Rückmeldung bekommen...

                                        1 Antwort Letzte Antwort
                                        0
                                        • S Online
                                          S Online
                                          Schimi
                                          schrieb am zuletzt editiert von
                                          #38

                                          schau mal in den Spoiler... das müsste funktionieren...

                                          ich will dieses Script so einfach wie moglich halten...

                                          ich lasse das von maxclaudi auch seperat laufen, eine Fehlerauelle weniger und wenn er was optimiert, kann man es direkt nutzen.

                                          Ich habe noch was "größeres" in der Pipeline (ist gerade im test).... das konnte entweder total speziell auf mich zugeschnitten oder richtig gut fur alle sein, sobald es veröffentlicht ist.

                                          Würde dann mit dem Zendure ZENKO konkurieren....

                                          Die aktuellen tests sind schonmal positiv

                                          1 Antwort Letzte Antwort
                                          0
                                          Antworten
                                          • In einem neuen Thema antworten
                                          Anmelden zum Antworten
                                          • Älteste zuerst
                                          • Neuste zuerst
                                          • Meiste Stimmen


                                          Support us

                                          ioBroker
                                          Community Adapters
                                          Donate

                                          696

                                          Online

                                          32.6k

                                          Benutzer

                                          82.0k

                                          Themen

                                          1.3m

                                          Beiträge
                                          Community
                                          Impressum | Datenschutz-Bestimmungen | Nutzungsbedingungen | Einwilligungseinstellungen
                                          ioBroker Community 2014-2025
                                          logo
                                          • Anmelden

                                          • Du hast noch kein Konto? Registrieren

                                          • Anmelden oder registrieren, um zu suchen
                                          • Erster Beitrag
                                            Letzter Beitrag
                                          0
                                          • Home
                                          • Aktuell
                                          • Tags
                                          • Ungelesen 0
                                          • Kategorien
                                          • Unreplied
                                          • Beliebt
                                          • GitHub
                                          • Docu
                                          • Hilfe