Weiter zum Inhalt
  • Home
  • Aktuell
  • 0 Ungelesen 0
  • Kategorien
  • Unreplied
  • Beliebt
  • GitHub
  • Docu
  • Hilfe
Skins
  • Hell
  • Brite
  • Cerulean
  • Cosmo
  • Flatly
  • Journal
  • Litera
  • Lumen
  • Lux
  • Materia
  • Minty
  • Morph
  • Pulse
  • Sandstone
  • Simplex
  • Sketchy
  • Spacelab
  • United
  • Yeti
  • Zephyr
  • Dunkel
  • 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. Zendure zenSDK Lokal API, SmartMode, SolarFlow AC 800 Pro 2

NEWS

  • Monatsrückblick Oktober 2026 ist online!
    BluefoxB
    Bluefox
    11
    1
    282

  • Mitmachen: Sprich „ioBroker“ ein – für ein lokales Aktivierungswort
    BluefoxB
    Bluefox
    6
    1
    474

  • Neuer Blogbeitrag: KI in ioBroker, vom Helfer im Admin bis zum eigenen Smart Assistant
    BluefoxB
    Bluefox
    4
    1
    524

Zendure zenSDK Lokal API, SmartMode, SolarFlow AC 800 Pro 2

Geplant Angeheftet Gesperrt Verschoben JavaScript
699 Beiträge 25 Kommentatoren 128.8k Aufrufe 23 Beobachtet
  • Ä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.
  • maxclaudiM maxclaudi

    @MP_Trixi sagte:
    Mal eine vielleicht doofe Frage zwischendurch.

    M. M. n. gibt es keine doofen Fragen.
    Wenn überhaupt, dann gibt es höchstens themenfremde Fragen.
    Das ist Deine Frage nicht.

    Ich sehe es gerne und hoffe sehr, dass alle, die Fragen haben, auch fragen und alle, die sie beantworten können, auch antworten.
    Nur so lebt ein Projekt.

    Ich wollte eigentlich, wenn die Haupt-PV-Anlage zu viel Strom liefert und deren große Batterie schon voll ist, die Batterien meiner SolarFlow800 Pro2 mit Überschuss laden.

    Funktioniert.

    Dann stelle ich ...control.auto_in_out_Limit auf z.B. -1000W (das ist das Maximum das in der App eingestellt werden kann, wenn weniger vorhanden ist auch weniger). In der App sieht man dass er die "Einstellungen für den Netzanschluss" von "Netzausgangsmodus" auf "Netzeingangsmodus" mit der entsprechenden W-Zahl übernimmt.

    Richtig, so soll es sein.

    Er lädt aber nicht (zusätzlich) vom Netz sondern nur was von der PV kommt (aktuell 665W bei 57% SoC).

    Jetzt wird es interessant, denn hier stimmt etwas nicht.
    Normalerweise priorisiert die Firmware die PV-Leistung.
    Das bedeutet: Wenn 1000 W Ladeleistung eingestellt sind und z. B. 665 W von der PV kommen, sollten die restlichen 335 W bzw. 330 W zusätzlich aus dem Netz geladen werden.

    Voraussetzungen – bitte über die Datenpunkte verifizieren:

    • acMode: 1

    • chargeMaxLimit: 1000 • (bzw. größer als die aktuelle PV-Leistung. Wichtig: Diesen Wert würde ich nach Möglichkeit nicht ständig ändern, da er in den Flash-Speicher / NVS geschrieben wird).

    • inputLimit: 1000 (bzw. größer als die aktuelle PV-Leistung)

    • aiState: 0

    • electricLevel: < socSet und electricLevel < socSet - ca. 10 %

    • hyperTmp: nicht zu hoch (ca. < 52 °C)

    • pass: 0

    • socStatus: 0

    • Temperaturen der Batterien (maxTemp): nicht zu hoch (ca. < 32 °C, optimal über 15–20 °C)

    • HEMS deaktiviert (bei Steuerung per Skript)

    • Keine Automatikprogramme der APP aktiviert?

    Wenn über das Netz geladen wird, sollte der Datenpunkt gridInputPower != 0 sein.

    Idealerweise, aber nicht zwingend als Bedingung:
    • heatState: 0
    • faultLevel: 0

    Kann das Gerät überhaupt parallel von der PV und vom Netz laden?

    Ja
    edit:
    Meines Wissens ist es gar nicht anders möglich: Wenn an einem Zendure-Gerät – wie deinem SolarFlow 800 Pro 2 – an den physischen MC4-Eingängen PV-Module angeschlossen sind und Leistung produziert wird, wird diese vorhandene PV-Leistung immer priorisiert.
    Überschüssige PV-Leistung wird – je nach Einstellung – eingespeist oder eben nicht verwertet. Reicht die PV-Leistung hingegen nicht aus, wird die Differenz zur eingestellten Ladeleistung automatisch aus dem Hausnetz bezogen.

    Anders sieht das aus, wenn über einen Mikrowechselrichter an der gridOff-Steckdose eingespeist wird.
    Dieser Strom zählt im System nicht als direkte PV-Leistung.

    Oder geht, so wie es aussieht, nur entweder oder?

    Nein.

    Die Firmware-Version wäre für mich interessant, falls es trotzdem nicht funktioniert.

    M
    M
    MP_Trixi
    schrieb am zuletzt editiert von MP_Trixi
    #582

    @maxclaudi

    gut, dass Du das so siehst - ab und an wird man ja heute gesteinigt wenn man für andere offensichtliche Dinge frägt.
    Danke für Deinen Input, denke damit habe ich es gefunden. chargeMaxLimit stand auf 0. Wenn ich das auf 1000 setze lädt er sogar 1000W von AC Plus PV-Eingang (aktuell sind das dann in Summe rund 1500W). Wenn ich das richtig interpretiere lasse ich chargeMaxLimit auf 1000 und steuere den tatsächlichen Wert (so wie der Überschuss verfügbar ist) über ...control.auto_in_out_Limit (FW habe ich noch V1.0.2 - da bin ich mir immer noch unschlüssig ob ich auf V2.0.2 gehen soll).

    maxclaudiM 1 Antwort Letzte Antwort
    0
    • M MP_Trixi

      @maxclaudi

      gut, dass Du das so siehst - ab und an wird man ja heute gesteinigt wenn man für andere offensichtliche Dinge frägt.
      Danke für Deinen Input, denke damit habe ich es gefunden. chargeMaxLimit stand auf 0. Wenn ich das auf 1000 setze lädt er sogar 1000W von AC Plus PV-Eingang (aktuell sind das dann in Summe rund 1500W). Wenn ich das richtig interpretiere lasse ich chargeMaxLimit auf 1000 und steuere den tatsächlichen Wert (so wie der Überschuss verfügbar ist) über ...control.auto_in_out_Limit (FW habe ich noch V1.0.2 - da bin ich mir immer noch unschlüssig ob ich auf V2.0.2 gehen soll).

      maxclaudiM
      maxclaudiM
      maxclaudi
      schrieb am zuletzt editiert von
      #583

      @MP_Trixi sagte:

      @maxclaudi

      gut, dass Du das so siehst - ab und an wird man ja heute gesteinigt wenn man für andere offensichtliche Dinge frägt.

      Mach dir nichts daraus! ;-)
      Ich werde hier manchmal sogar für belegbare Aussagen angezweifelt.
      Da kommen dann oft nur unbegründete, unbelegbare Behauptungen, Aufforderungen zu Beweisen (die die Gegenseite selbst nicht liefern kann) oder suggestive Fragen.

      Danke für Deinen Input, denke damit habe ich es gefunden. chargeMaxLimit stand auf 0. Wenn ich das auf 1000 setze lädt er sogar 1000W von AC Plus PV-Eingang (aktuell sind das dann in Summe rund 1500W). Wenn ich das richtig interpretiere lasse ich chargeMaxLimit auf 1000 und steuere den tatsächlichen Wert (so wie der Überschuss verfügbar ist) über ...control.auto_in_out_Limit .

      Richtig. Bei einer aktiven Regelung würde ich dir zudem unbedingt empfehlen, smartMode: 1 zu setzen.

      Bei mir steht der Wert dauerhaft auf 1, damit die Batterien nicht ständig in den Standby gehen.
      Zwar verbleiben sie dann ab und zu für einige Zeit im aktiven Zustand, aber das schadet dem System deutlich weniger, als wenn permanent neue Werte in den Flash-Speicher (NVS) geschrieben werden.

      Ansonsten – wenn sie voll sind und es für mehrere Stunden nichts zu regeln gibt (z. B. im Modus pass: 2 / Bypass) – kannst du (wenn du möchtest) per Skript oder manuell smartMode: 0 setzen.
      Das spart ein paar Wh, ist aber wirklich nur sinnvoll, wenn es sich um mehrere Stunden handelt.
      Bitte nicht zu oft wechseln, denn sobald smartMode geändert wird, wird die Konfiguration sofort wieder in den NVS geschrieben.

      (FW habe ich noch V1.0.2 - da bin ich mir immer noch unschlüssig ob ich auf V2.0.2 gehen soll).

      Wenn aktuell alles zu deiner Zufriedenheit läuft, kannst du vorerst bei dieser Firmware bleiben.

      Allerdings bringt die V2.0.2 einige Verbesserungen mit sich.
      Die Firmware ist eigentlich gut – ich muss allerdings dazusagen, dass ich selbst keinen SolarFlow Pro 2 nutze und dein genaues Setup nicht kenne.

      Meiner Meinung nach ist das Update auf V2.0.2 bedenkenlos.
      Ich würde es vor allem dann empfehlen, wenn die aktuell installierte Version bereits bei V1.6.x oder höher liegt bei neuen Zendure-Geräten bis 2400AC plus/pro

      Zur V1.0.2 fehlen mir die Vergleichswerte für eine Analyse – so weit möchte ich auch nicht mehr zurückgehen.
      Alles ist mir jetzt schon zu viel Arbeit.
      Versionen, die neuer als V2.0.2 sind, kann ich derzeit nicht beurteilen.

      Zu bedenken gibt es nur, dass man später nicht auf V2.0.2 updaten kann, wenn schon eine neuere, höhere Version angeboten wird.
      Aber wie gesagt, wenn dein System voll zu deiner Zufriedenheit läuft ...

      Es bleibt deine Entscheidung ;-)

      Weiter möchte ich mich zu Firmware-Versionen nicht äußern.

      Zendure zenSDK Lokal API, SmartMode, SolarFlow AC 800 Pro 2

      neue Zendure Geräte Cloudless: zURLtool V2.0

      M maxclaudiM 2 Antworten Letzte Antwort
      0
      • maxclaudiM maxclaudi

        @MP_Trixi sagte:

        @maxclaudi

        gut, dass Du das so siehst - ab und an wird man ja heute gesteinigt wenn man für andere offensichtliche Dinge frägt.

        Mach dir nichts daraus! ;-)
        Ich werde hier manchmal sogar für belegbare Aussagen angezweifelt.
        Da kommen dann oft nur unbegründete, unbelegbare Behauptungen, Aufforderungen zu Beweisen (die die Gegenseite selbst nicht liefern kann) oder suggestive Fragen.

        Danke für Deinen Input, denke damit habe ich es gefunden. chargeMaxLimit stand auf 0. Wenn ich das auf 1000 setze lädt er sogar 1000W von AC Plus PV-Eingang (aktuell sind das dann in Summe rund 1500W). Wenn ich das richtig interpretiere lasse ich chargeMaxLimit auf 1000 und steuere den tatsächlichen Wert (so wie der Überschuss verfügbar ist) über ...control.auto_in_out_Limit .

        Richtig. Bei einer aktiven Regelung würde ich dir zudem unbedingt empfehlen, smartMode: 1 zu setzen.

        Bei mir steht der Wert dauerhaft auf 1, damit die Batterien nicht ständig in den Standby gehen.
        Zwar verbleiben sie dann ab und zu für einige Zeit im aktiven Zustand, aber das schadet dem System deutlich weniger, als wenn permanent neue Werte in den Flash-Speicher (NVS) geschrieben werden.

        Ansonsten – wenn sie voll sind und es für mehrere Stunden nichts zu regeln gibt (z. B. im Modus pass: 2 / Bypass) – kannst du (wenn du möchtest) per Skript oder manuell smartMode: 0 setzen.
        Das spart ein paar Wh, ist aber wirklich nur sinnvoll, wenn es sich um mehrere Stunden handelt.
        Bitte nicht zu oft wechseln, denn sobald smartMode geändert wird, wird die Konfiguration sofort wieder in den NVS geschrieben.

        (FW habe ich noch V1.0.2 - da bin ich mir immer noch unschlüssig ob ich auf V2.0.2 gehen soll).

        Wenn aktuell alles zu deiner Zufriedenheit läuft, kannst du vorerst bei dieser Firmware bleiben.

        Allerdings bringt die V2.0.2 einige Verbesserungen mit sich.
        Die Firmware ist eigentlich gut – ich muss allerdings dazusagen, dass ich selbst keinen SolarFlow Pro 2 nutze und dein genaues Setup nicht kenne.

        Meiner Meinung nach ist das Update auf V2.0.2 bedenkenlos.
        Ich würde es vor allem dann empfehlen, wenn die aktuell installierte Version bereits bei V1.6.x oder höher liegt bei neuen Zendure-Geräten bis 2400AC plus/pro

        Zur V1.0.2 fehlen mir die Vergleichswerte für eine Analyse – so weit möchte ich auch nicht mehr zurückgehen.
        Alles ist mir jetzt schon zu viel Arbeit.
        Versionen, die neuer als V2.0.2 sind, kann ich derzeit nicht beurteilen.

        Zu bedenken gibt es nur, dass man später nicht auf V2.0.2 updaten kann, wenn schon eine neuere, höhere Version angeboten wird.
        Aber wie gesagt, wenn dein System voll zu deiner Zufriedenheit läuft ...

        Es bleibt deine Entscheidung ;-)

        Weiter möchte ich mich zu Firmware-Versionen nicht äußern.

        M
        M
        MP_Trixi
        schrieb am zuletzt editiert von MP_Trixi
        #584

        @maxclaudi
        gut, habe Input/Output Limit Mode SmartMode RAM und Smart Mode Watcher auf true. SmartMode steht damit (aktuell) bei 1. FW Update läuft ;)

        maxclaudiM 1 Antwort Letzte Antwort
        1
        • M MP_Trixi

          @maxclaudi
          gut, habe Input/Output Limit Mode SmartMode RAM und Smart Mode Watcher auf true. SmartMode steht damit (aktuell) bei 1. FW Update läuft ;)

          maxclaudiM
          maxclaudiM
          maxclaudi
          schrieb am zuletzt editiert von maxclaudi
          #585

          @MP_Trixi und Alle:

          Steuerungs-Script:
          update 06.10.26 02:00h neu set batCalTime, fix acMode/auto_in_out_Limit

          Beschreibung:
          neu set batCalTime

          Zendure zenSDK Lokal API, SmartMode, SolarFlow AC 800 Pro 2

          neue Zendure Geräte Cloudless: zURLtool V2.0

          T M 2 Antworten Letzte Antwort
          2
          • maxclaudiM maxclaudi

            @MP_Trixi und Alle:

            Steuerungs-Script:
            update 06.10.26 02:00h neu set batCalTime, fix acMode/auto_in_out_Limit

            Beschreibung:
            neu set batCalTime

            T
            T
            T-147
            schrieb am zuletzt editiert von
            #586

            Guten Morgen zusammen,

            gestern Morgen mal das FW-Update auf 2.0.2 gemacht und das aktuelle Blockly aktiviert. SoC stand zu dem Zeitpunkt auf 10%. Das Blockly hat umgehend die Entladesperre aktiviert und erstmal bis ca 16% aufgeladen, bevor wieder entladen werden durfte.
            Damit wäre eines meiner Probleme erledigt 👍

            Steuerung lief damit gestern auch super, bis die Batterie heute Nacht bei 24% aufgehört hat zu entladen, weil die Entladesperre gegriffen hat (laut Log). Ich verstehe nur noch nicht, warum so hoch? BatteryLockMinCellVoltage steht auf 3.2V.

            Heute Morgen hab ich direkt das neue ZenSDK-Script eingebunden und aktiviert, am Blockly hab ich nichts mehr verändert.
            Der SF800 Pro2 hat umgehend auf Entladen geschaltet und der minVol ist aktuell bei 3,14V und die Batterie entlädt fleißig weiter.
            Kurz darauf ein kurzer Peak im Stromverbrauch, seitdem entlädt der SF800 Pro2 fleißig mit vollen 800W und ballert 600W davon ins Netz, ohne wieder zurückzuregeln.
            Hab das Entladen jetzt manuell auf 200W runtergesetzt, das hat auch funktioniert. Kurz darauf hat die Entladesperre wieder gegriffen bei ca 15% und minVol 3,19V (hatte das eben wieder auf Ausgangswerte gestellt, war also korrekt so).
            Zwischendurch hat der Javascript-Adapter noch folgende Warnungen ausgeworfen:

            grafik.jpeg

            372:210 ist ein <next> im Blockly, das "AC1600plus" kann eignetlich nur ein Rest aus dem Original-Script sein, den ich übersehen und nicht geändert hab, finde aber ehrlich gesagt nichts mehr dazu.
            Mit dem Rest kann ich erstmal nichts anfangen an Infos. Hab beide Scripte mal neu gestartet, danach kam keine Warnmeldung mehr im Log und die Entladesperre hat gegriffen.

            Mir ist jetzt nur aufgefallen, dass die minVol bei mir im ioBroker mit einem Komma geschrieben wird und im Blockly mit einem Punkt als Trennzeichen. Das muss so sein oder kommt der damit evtl durcheinander?

            Bin da grad ein wenig verwirrt, aber ich hoffe da kommt bald Licht ins Dunkel...

            Achso, aktuell liegt minVol bei 3,2V und der SoC bei 15%, während das System ein wenig durch die Morgensonne geladen wird. Gestern lag der minVol bei 15% SoC bei 3,24V.
            Woher kommen diese Unterschiede bei fast identischen Bedingungen gestern und heute?

            Beste Grüße

            maxclaudiM 2 Antworten Letzte Antwort
            0
            • maxclaudiM maxclaudi

              @MP_Trixi sagte:

              @maxclaudi

              gut, dass Du das so siehst - ab und an wird man ja heute gesteinigt wenn man für andere offensichtliche Dinge frägt.

              Mach dir nichts daraus! ;-)
              Ich werde hier manchmal sogar für belegbare Aussagen angezweifelt.
              Da kommen dann oft nur unbegründete, unbelegbare Behauptungen, Aufforderungen zu Beweisen (die die Gegenseite selbst nicht liefern kann) oder suggestive Fragen.

              Danke für Deinen Input, denke damit habe ich es gefunden. chargeMaxLimit stand auf 0. Wenn ich das auf 1000 setze lädt er sogar 1000W von AC Plus PV-Eingang (aktuell sind das dann in Summe rund 1500W). Wenn ich das richtig interpretiere lasse ich chargeMaxLimit auf 1000 und steuere den tatsächlichen Wert (so wie der Überschuss verfügbar ist) über ...control.auto_in_out_Limit .

              Richtig. Bei einer aktiven Regelung würde ich dir zudem unbedingt empfehlen, smartMode: 1 zu setzen.

              Bei mir steht der Wert dauerhaft auf 1, damit die Batterien nicht ständig in den Standby gehen.
              Zwar verbleiben sie dann ab und zu für einige Zeit im aktiven Zustand, aber das schadet dem System deutlich weniger, als wenn permanent neue Werte in den Flash-Speicher (NVS) geschrieben werden.

              Ansonsten – wenn sie voll sind und es für mehrere Stunden nichts zu regeln gibt (z. B. im Modus pass: 2 / Bypass) – kannst du (wenn du möchtest) per Skript oder manuell smartMode: 0 setzen.
              Das spart ein paar Wh, ist aber wirklich nur sinnvoll, wenn es sich um mehrere Stunden handelt.
              Bitte nicht zu oft wechseln, denn sobald smartMode geändert wird, wird die Konfiguration sofort wieder in den NVS geschrieben.

              (FW habe ich noch V1.0.2 - da bin ich mir immer noch unschlüssig ob ich auf V2.0.2 gehen soll).

              Wenn aktuell alles zu deiner Zufriedenheit läuft, kannst du vorerst bei dieser Firmware bleiben.

              Allerdings bringt die V2.0.2 einige Verbesserungen mit sich.
              Die Firmware ist eigentlich gut – ich muss allerdings dazusagen, dass ich selbst keinen SolarFlow Pro 2 nutze und dein genaues Setup nicht kenne.

              Meiner Meinung nach ist das Update auf V2.0.2 bedenkenlos.
              Ich würde es vor allem dann empfehlen, wenn die aktuell installierte Version bereits bei V1.6.x oder höher liegt bei neuen Zendure-Geräten bis 2400AC plus/pro

              Zur V1.0.2 fehlen mir die Vergleichswerte für eine Analyse – so weit möchte ich auch nicht mehr zurückgehen.
              Alles ist mir jetzt schon zu viel Arbeit.
              Versionen, die neuer als V2.0.2 sind, kann ich derzeit nicht beurteilen.

              Zu bedenken gibt es nur, dass man später nicht auf V2.0.2 updaten kann, wenn schon eine neuere, höhere Version angeboten wird.
              Aber wie gesagt, wenn dein System voll zu deiner Zufriedenheit läuft ...

              Es bleibt deine Entscheidung ;-)

              Weiter möchte ich mich zu Firmware-Versionen nicht äußern.

              maxclaudiM
              maxclaudiM
              maxclaudi
              schrieb am zuletzt editiert von
              #587

              Nach (für mich vorläufigem) Abschluss bin ich dankbar, aber zugleich auch etwas enttäuscht.
              Die Firmware V2.0.2 ist – um es untertrieben zu formulieren – gut.
              Das Gute ist, dass in der V2.0.4 anscheinend einiges berücksichtigt wurde.
              Schade ist hingegen, dass nun (V2.0.4) ein ungefährlicher, werkseitig vorhandener Zugang verwehrt zu sein scheint, der zumindest für Diagnosen, Logs, Debugging und Tests mit viel mehr Features gut gewesen wäre.
              Es ist allerdings verständlich, da diese Schnittstellen für den Normalbetrieb nicht gedacht sind und wahrscheinlich nur dem Service oder der Diagnose dienen.

              Zendure zenSDK Lokal API, SmartMode, SolarFlow AC 800 Pro 2

              neue Zendure Geräte Cloudless: zURLtool V2.0

              1 Antwort Letzte Antwort
              1
              • T T-147

                Guten Morgen zusammen,

                gestern Morgen mal das FW-Update auf 2.0.2 gemacht und das aktuelle Blockly aktiviert. SoC stand zu dem Zeitpunkt auf 10%. Das Blockly hat umgehend die Entladesperre aktiviert und erstmal bis ca 16% aufgeladen, bevor wieder entladen werden durfte.
                Damit wäre eines meiner Probleme erledigt 👍

                Steuerung lief damit gestern auch super, bis die Batterie heute Nacht bei 24% aufgehört hat zu entladen, weil die Entladesperre gegriffen hat (laut Log). Ich verstehe nur noch nicht, warum so hoch? BatteryLockMinCellVoltage steht auf 3.2V.

                Heute Morgen hab ich direkt das neue ZenSDK-Script eingebunden und aktiviert, am Blockly hab ich nichts mehr verändert.
                Der SF800 Pro2 hat umgehend auf Entladen geschaltet und der minVol ist aktuell bei 3,14V und die Batterie entlädt fleißig weiter.
                Kurz darauf ein kurzer Peak im Stromverbrauch, seitdem entlädt der SF800 Pro2 fleißig mit vollen 800W und ballert 600W davon ins Netz, ohne wieder zurückzuregeln.
                Hab das Entladen jetzt manuell auf 200W runtergesetzt, das hat auch funktioniert. Kurz darauf hat die Entladesperre wieder gegriffen bei ca 15% und minVol 3,19V (hatte das eben wieder auf Ausgangswerte gestellt, war also korrekt so).
                Zwischendurch hat der Javascript-Adapter noch folgende Warnungen ausgeworfen:

                grafik.jpeg

                372:210 ist ein <next> im Blockly, das "AC1600plus" kann eignetlich nur ein Rest aus dem Original-Script sein, den ich übersehen und nicht geändert hab, finde aber ehrlich gesagt nichts mehr dazu.
                Mit dem Rest kann ich erstmal nichts anfangen an Infos. Hab beide Scripte mal neu gestartet, danach kam keine Warnmeldung mehr im Log und die Entladesperre hat gegriffen.

                Mir ist jetzt nur aufgefallen, dass die minVol bei mir im ioBroker mit einem Komma geschrieben wird und im Blockly mit einem Punkt als Trennzeichen. Das muss so sein oder kommt der damit evtl durcheinander?

                Bin da grad ein wenig verwirrt, aber ich hoffe da kommt bald Licht ins Dunkel...

                Achso, aktuell liegt minVol bei 3,2V und der SoC bei 15%, während das System ein wenig durch die Morgensonne geladen wird. Gestern lag der minVol bei 15% SoC bei 3,24V.
                Woher kommen diese Unterschiede bei fast identischen Bedingungen gestern und heute?

                Beste Grüße

                maxclaudiM
                maxclaudiM
                maxclaudi
                schrieb am zuletzt editiert von maxclaudi
                #588

                @T-147
                welches Blockly verwendest Du?
                das hier Post #412?

                Bitte wenn möglich, das nächte mal nicht als Bild. Besser:

                javascript.0 2026-10-07 08:41:05.642 info script.js.Energieversorgung.Zendure.SF800_Pro_2_-_Nulleinspeisung_2026_09: Es gibt nichts zu tun, theoretisches positives newLimit: 250; oldLimit: 250
                javascript.0 2026-10-07 08:40:45.263 info script.js.Energieversorgung.Zendure.SF800_Pro_2_-_Nulleinspeisung_2026_09: Es gibt nichts zu tun, theoretisches negatives newLimit: 0; oldLimit: 250
                javascript.0 2026-10-07 08:40:45.262 warn at processImmediate (node:internal/timers:541:9)
                javascript.0 2026-10-07 08:40:45.261 warn at runNextTicks (node:internal/process/task_queues:65:5)
                javascript.0 2026-10-07 08:40:45.260 warn at Object.<anonymous> (script.js.Energieversorgung.Zendure.SF800_Pro_2_-_Nulleinspeisung_2026_09:372:210)
                javascript.0 2026-10-07 08:40:45.252 warn getState "0_userdata.0.zendure.1600ACplus.properties.socSet" not found (3)
                javascript.0 2026-10-07 08:40:35.335 info script.js.Energieversorgung.Zendure.SF800_Pro_2_-_Nulleinspeisung_2026_09: auto_In_out_Limit ist positiv oder 0: 250
                javascript.0 2026-10-07 08:40:35.334 info script.js.Energieversorgung.Zendure.SF800_Pro_2_-_Nulleinspeisung_2026_09: newLimit positiv: 250 ist > oldLimit: 0 Es wird mehr entladen als zuvor.
                
                

                properties.socSet Datenpunkt ist falsch und muss bei Dir auf Deinen properties sco.Set Datenpunkt gesetzt werden.
                Entweder hier:

                image.jpeg

                und/oder hier etwas weiter darunter:

                image.jpeg

                Zendure zenSDK Lokal API, SmartMode, SolarFlow AC 800 Pro 2

                neue Zendure Geräte Cloudless: zURLtool V2.0

                T 1 Antwort Letzte Antwort
                0
                • maxclaudiM maxclaudi

                  @T-147
                  welches Blockly verwendest Du?
                  das hier Post #412?

                  Bitte wenn möglich, das nächte mal nicht als Bild. Besser:

                  javascript.0 2026-10-07 08:41:05.642 info script.js.Energieversorgung.Zendure.SF800_Pro_2_-_Nulleinspeisung_2026_09: Es gibt nichts zu tun, theoretisches positives newLimit: 250; oldLimit: 250
                  javascript.0 2026-10-07 08:40:45.263 info script.js.Energieversorgung.Zendure.SF800_Pro_2_-_Nulleinspeisung_2026_09: Es gibt nichts zu tun, theoretisches negatives newLimit: 0; oldLimit: 250
                  javascript.0 2026-10-07 08:40:45.262 warn at processImmediate (node:internal/timers:541:9)
                  javascript.0 2026-10-07 08:40:45.261 warn at runNextTicks (node:internal/process/task_queues:65:5)
                  javascript.0 2026-10-07 08:40:45.260 warn at Object.<anonymous> (script.js.Energieversorgung.Zendure.SF800_Pro_2_-_Nulleinspeisung_2026_09:372:210)
                  javascript.0 2026-10-07 08:40:45.252 warn getState "0_userdata.0.zendure.1600ACplus.properties.socSet" not found (3)
                  javascript.0 2026-10-07 08:40:35.335 info script.js.Energieversorgung.Zendure.SF800_Pro_2_-_Nulleinspeisung_2026_09: auto_In_out_Limit ist positiv oder 0: 250
                  javascript.0 2026-10-07 08:40:35.334 info script.js.Energieversorgung.Zendure.SF800_Pro_2_-_Nulleinspeisung_2026_09: newLimit positiv: 250 ist > oldLimit: 0 Es wird mehr entladen als zuvor.
                  
                  

                  properties.socSet Datenpunkt ist falsch und muss bei Dir auf Deinen properties sco.Set Datenpunkt gesetzt werden.
                  Entweder hier:

                  image.jpeg

                  und/oder hier etwas weiter darunter:

                  image.jpeg

                  T
                  T
                  T-147
                  schrieb am zuletzt editiert von
                  #589

                  @maxclaudi sagte:

                  @T-147
                  welches Blockly verwendest Du?
                  das hier Post #412?

                  Ja, genau das. Habs nur auf meinen SF800 Pro2 angepasst. Und natürlich meinen Zähler...

                  Bitte wenn möglich, das nächte mal nicht als Bild. Besser:

                  Sorry, ich vergess das immer wieder

                  properties.socSet ist einmal falsch. wahrscheinlich hier:
                  image.jpeg

                  oder

                  VOLLTREFFER! 👍
                  Ich danke Dir. Habs
                  bestimmt 20x überlesen... 😬

                  Kann den "Fehler" mit der zu hohen Einspeisung von heute Morgen bisher leider nicht reproduzieren. Ist keiner Zuhause, der nen großen Verbraucher einschalten könnte. Ich berichte, sobald sich was in die Richtung tut.

                  1 Antwort Letzte Antwort
                  1
                  • T T-147

                    Guten Morgen zusammen,

                    gestern Morgen mal das FW-Update auf 2.0.2 gemacht und das aktuelle Blockly aktiviert. SoC stand zu dem Zeitpunkt auf 10%. Das Blockly hat umgehend die Entladesperre aktiviert und erstmal bis ca 16% aufgeladen, bevor wieder entladen werden durfte.
                    Damit wäre eines meiner Probleme erledigt 👍

                    Steuerung lief damit gestern auch super, bis die Batterie heute Nacht bei 24% aufgehört hat zu entladen, weil die Entladesperre gegriffen hat (laut Log). Ich verstehe nur noch nicht, warum so hoch? BatteryLockMinCellVoltage steht auf 3.2V.

                    Heute Morgen hab ich direkt das neue ZenSDK-Script eingebunden und aktiviert, am Blockly hab ich nichts mehr verändert.
                    Der SF800 Pro2 hat umgehend auf Entladen geschaltet und der minVol ist aktuell bei 3,14V und die Batterie entlädt fleißig weiter.
                    Kurz darauf ein kurzer Peak im Stromverbrauch, seitdem entlädt der SF800 Pro2 fleißig mit vollen 800W und ballert 600W davon ins Netz, ohne wieder zurückzuregeln.
                    Hab das Entladen jetzt manuell auf 200W runtergesetzt, das hat auch funktioniert. Kurz darauf hat die Entladesperre wieder gegriffen bei ca 15% und minVol 3,19V (hatte das eben wieder auf Ausgangswerte gestellt, war also korrekt so).
                    Zwischendurch hat der Javascript-Adapter noch folgende Warnungen ausgeworfen:

                    grafik.jpeg

                    372:210 ist ein <next> im Blockly, das "AC1600plus" kann eignetlich nur ein Rest aus dem Original-Script sein, den ich übersehen und nicht geändert hab, finde aber ehrlich gesagt nichts mehr dazu.
                    Mit dem Rest kann ich erstmal nichts anfangen an Infos. Hab beide Scripte mal neu gestartet, danach kam keine Warnmeldung mehr im Log und die Entladesperre hat gegriffen.

                    Mir ist jetzt nur aufgefallen, dass die minVol bei mir im ioBroker mit einem Komma geschrieben wird und im Blockly mit einem Punkt als Trennzeichen. Das muss so sein oder kommt der damit evtl durcheinander?

                    Bin da grad ein wenig verwirrt, aber ich hoffe da kommt bald Licht ins Dunkel...

                    Achso, aktuell liegt minVol bei 3,2V und der SoC bei 15%, während das System ein wenig durch die Morgensonne geladen wird. Gestern lag der minVol bei 15% SoC bei 3,24V.
                    Woher kommen diese Unterschiede bei fast identischen Bedingungen gestern und heute?

                    Beste Grüße

                    maxclaudiM
                    maxclaudiM
                    maxclaudi
                    schrieb am zuletzt editiert von
                    #590

                    @T-147 sagte:
                    Steuerung lief damit gestern auch super, bis die Batterie heute Nacht bei 24% aufgehört hat zu entladen, weil die Entladesperre gegriffen hat (laut Log). Ich verstehe nur noch nicht, warum so hoch? BatteryLockMinCellVoltage steht auf 3.2V.

                    Da hat die Entladesperre bei 3,2 V absolut richtig gegriffen. Nachdem die Batterien nicht mehr entladen wurden, haben sie sich im Ruhezustand (unbelasteter Zustand) etwas erholt, wodurch die Zellspannung wieder leicht angestiegen ist.
                    Warum das in Prozent (SoC) so hoch wirkt? Die Entladesperre greift, sobald die schwächste Zelle einer einzelnen Batterie den Mindestwert (Datenpunkt minVol) erreicht – und zwar über alle vorhandenen Batterien hinweg.
                    Wenn keine Last mehr anliegt, kommen die Zellen in eine Ruhephase, die Spannung steigt an, aber der SoC-Wert bleibt auf dem geschätzten Stand stehen.
                    Der SoC ist ohnehin nur ein errechneter/geschätzter Wert.

                    Wenn die Batterien nicht im kalten Außenbereich stehen, kannst du die Entladesperre testweise etwas niedriger ansetzen, z. B. auf 3,16 V. Zu niedrig würde ich allerdings nicht gehen, um die Zellen zu schonen. Taste dich da einfach heran.

                    Mir ist jetzt nur aufgefallen, dass die minVol bei mir im ioBroker mit einem Komma geschrieben wird und im Blockly mit einem Punkt als Trennzeichen. Das muss so sein oder kommt der damit evtl durcheinander?

                    Im Blockly-Code bitte zwingend den Punkt als Dezimaltrenner verwenden. Der Punkt ist in JavaScript Pflicht. Selbst wenn ioBroker ein Komma akzeptieren würde, bitte trotzdem den Punkt als Dezimaltrenner verwenden.

                    Achso, aktuell liegt minVol bei 3,2V und der SoC bei 15%, während das System ein wenig durch die Morgensonne geladen wird. Gestern lag der minVol bei 15% SoC bei 3,24V.
                    Woher kommen diese Unterschiede bei fast identischen Bedingungen gestern und heute?

                    Wie oben schon kurz angerissen: Die Spannung (Volt) ist ein harter, exakter Messwert.
                    Der SoC ist ein vom BMS mathematisch geschätzter und abgeleiteter Wert.
                    Diese Abweichungen sind völlig normal.
                    Je nachdem, wie hoch die Last kurz vor dem Abschalten war, bricht die Spannung unter Last unterschiedlich stark ein.
                    Bei hoher Last werden die 3,2 V Zellspannung viel früher (also bei einem höheren SoC) erreicht als bei einer sehr schwachen, langsamen Entladung.

                    Zendure zenSDK Lokal API, SmartMode, SolarFlow AC 800 Pro 2

                    neue Zendure Geräte Cloudless: zURLtool V2.0

                    T 1 Antwort Letzte Antwort
                    0
                    • Murphy 0M
                      Murphy 0M
                      Murphy 0
                      schrieb am zuletzt editiert von Murphy 0
                      #591

                      Zu der Zellspannung und SOC Wert hier das Diagram.

                      Akku paar Tage nicht voll gewesen, sonst sind die 3,17 minVolt so um die 15% SOC wenn entladen wird.

                      Entladen wird bei 3,17 bei mir gestoppt.

                      Da ich alle Werte vom Zendure Adapter, von den Hoymiles WR, von der Daikin Klimaanlage und vom Mercedes.me Adapter in eine Datenbank schreibe kann ich immer nachvollziehen was war.

                      Der relativ starke Einbruch der Spannung um 7:30 ist entstanden weil die Temperatursteuerung der Daikinanlage angefangen hat die Küche zu heizen.

                      IMG_4182.png

                      1 Antwort Letzte Antwort
                      1
                      • maxclaudiM maxclaudi

                        @MP_Trixi und Alle:

                        Steuerungs-Script:
                        update 06.10.26 02:00h neu set batCalTime, fix acMode/auto_in_out_Limit

                        Beschreibung:
                        neu set batCalTime

                        M
                        M
                        MP_Trixi
                        schrieb am zuletzt editiert von
                        #592

                        @maxclaudi sagte:

                        Steuerungs-Script:
                        update 06.10.26 02:00h neu set batCalTime, fix acMode/auto_in_out_Limit

                        Danke für Deine Arbeit - hab meins aktualisiert...

                        1 Antwort Letzte Antwort
                        1
                        • maxclaudiM maxclaudi

                          maxclaudi sagte:
                          Das ist der Moment, um es live zu testen. ...

                          Funktionierte nicht.
                          <Datenschutz> benötigt 'zuvor' ein zusätzliches Flag <Datenschutz>
                          Gefunden - ausgeführt.

                          H E U R E K A !

                          Ergebnis stabil seit 03.10.2026 14:01 Uhr.

                          1600AC_verbunden_1887-forum.png


                          edit/update 07.10.26; 19:50h:
                          1600AC+ Port 1887 (kein TLS) läuft schnell, stabil - ohne reconnect - seit dem 03.10.2026 um 14:01 Uhr.

                          .... Bilder sagen mehr als Worte

                          socSet_6_Prozent.png

                          maxclaudiM
                          maxclaudiM
                          maxclaudi
                          schrieb am zuletzt editiert von maxclaudi
                          #593

                          07.10.2026_21:20h

                          Da es dazu weltweit quasi null Infos gibt, dachte ich, ich teile meine bisherigen Erkenntnisse mal auszugsweise als kleinen Bericht mit euch.

                          Wer möchte kann den Bericht im spoiler lesen, für alle anderen ist der Beitrag dann kürzer und kann ignoriert werden :-)

                          Wie ich bereits geschrieben habe, gibt es einen Weg, um sich über Port: 1883 (ohne TLS) per MQTT zu verbinden.

                          Wenn die Verbindung steht, ist die MQTT-Struktur identisch.

                          Es gibt jedoch zwei entscheidende Unterschiede:

                          1. Fehlende automatische Reports
                          Sie finden weder über einen Timer alle x Sekunden statt, noch werden sie bei geräteinternen Wertänderungen gesendet.
                          Ein Report wird vom Gerät hier nicht automatisch alle X Sekunden abgesetzt – auch nicht, wenn sich intern im Gerät das energyLevel oder die Werte der daran angeschlossenen PV-Module und Batterien (wie solarInputPower, minVol etc.) ändern.

                          Das ist aktuell (noch) ein großes Manko für mich.

                          Es wird nur dann sehr schnell und sofort ein Report vom Gerät gesendet, wenn explizit vom Broker oder von einem anderen Gerät per MQTT ein externer Befehl beim Gerät eingeht.

                          Das funktioniert beispielsweise über ein getAll als Lesebefehl über das herkömmliche Topic:

                          iot/productId/deviceId/properties/read
                          

                          oder über einen Schreibbefehl bei den (un)bekannten Topics.

                          Von "allein" informiert das Gerät hier nicht über das gewohnte Topic:

                          /productId/deviceId/properties/report
                          

                          Ich hoffe einen Weg zu finden, das zu automatisieren bzw. einen Aufrufer zu finden, der den Report extern auslöst.

                          Vielleicht gibt es ein Command etc., um ein Flag zu setzen, damit bei dieser Verbindung über MQTT vom Gerät sofort ein Report-Payload gesendet wird, wenn sich intern ein Parameter ändert (vorzugsweise die Parameter der Batterien, der PV-Leistung, Spannungen, Energielevel etc.).

                          Was regulär vom Gerät automatisch, öfter und schneller aktualisiert wird, ist das Log-Topic:

                          /productId/deviceId/log
                          

                          Das wird bei einer geräteinternen Wertänderung schneller automatisch gesendet, aber nicht sofort wie bei einem Report.

                          Ansonsten wird log durch einen Timer alle 7700ms gesendet.
                          Genauso wie bei TLS-Verbindungen, wobei bei TLS-Verbindungen häufig Abweichungen festzustellen sind.
                          Bei TLS 8883 sind es auch 7700ms, jedoch immer wieder benötigt ein weiteres log dann auch mal 55000~ 67000ms.
                          8883 liefert, auch wenn es keine große Änderungen gibt durch einen Timer alle 5000ms ein report.
                          Das ist bei 1883 - bei mir bisher - nicht so.

                          Das Log liefert eigentlich alle relevanten Daten.
                          Für das Log fehlt mir noch das vollständige Mapping und unterscheidet sich etwas zu den älteren Gerätegenerationen.
                          Es ist ein CSV-Format, allerdings ohne Keys.
                          Dieses CSV ist bei allen neueren Geräten mit einer Firmware-Version größer als 1.6.x identisch bzw. ab api 3.

                          Das Log wird auch bei Eurer Verbindung übertragen.
                          Vielleicht möchte jemand bei einem Mapping behilflich sein.

                          Die Suche nach dem Aufrufer, der den Report extern auslöst ist, sehr, sehr harte Arbeit.
                          Ich versuche gerade, mögliche Callbacks auszuwerten und zu vergleichen, wie sich die Wege unterscheiden.

                          Ein aktueller Workaround ist Polling:
                          Ein Trigger auf log, der ein Publish mit getAll auslöst.

                          Das funktioniert einwandfrei und extrem schnell.
                          Eigentlich das was zenSDK macht und m. M. n. für MQTT nicht üblich ist.

                          ABER: Das ist nicht das Ziel.
                          Ich möchte unbedingt, dass ein MQTT-Publish sofort als Report gesendet wird – nicht wie bisher nur auf externen Befehl, sondern auch sofort, wenn intern eine Wertänderung erkannt wird (wie gewohnt bei z. B. solarInputPower).

                          Abgesehen davon funktioniert alles besser und viel schneller.
                          Es gab keinen einzigen Reconnect seit der ersten Verbindung.

                          Der TLS-Ballast kommt im RAM nicht zum Einsatz, und einiges andere ebenfalls nicht.

                          Das System läuft ruhiger und reagiert deutlich schneller.
                          Subjektiv denke ich, dass WiFi-Verbindung besser ist.

                          Mosquitto agiert als Bridge und ist zur Cloud verbunden.
                          HEMS ist deaktiviert und localAPIEnable steht auf true.

                          Bisher hat die Cloud nichts weiter getan, als die Zeit zu synchronisieren.

                          2. Erweiterte Limits im devMode
                          (ich nenne ihn jetzt einfach "devMode" was nicht exakt stimmt, aber lesbarer ist)

                          Vorweg: Ich werde keine Firmware modifizieren. Sie wird immer original bleiben.
                          Ich analysiere nur und versuche Wege zu finden, die herstellerseitig vorgesehen sind und einfach genutzt werden können.
                          Quasi einfache Konfiguration - Möglichkeiten.
                          Es handelt sich hierbei um eine Verbindung, die vermutlich nur für eine Werksverbindung oder Service-Verbindung verwendet wird.

                          Anscheinend werden im devMode einige Sperren ausgehebelt - zumindest großzügiger gemacht - oder Limits erweitert.

                          Beispiele hierfür:

                          • minSoc: Kann gefahrlos von 5 bis 95 % gesetzt werden (theoretisch bis 100%, was ich vorsichtshalber aber nicht teste).

                          • socSet: Kann gefahrlos von 5 bis 100 % gesetzt werden, solange socSet > minSoc ist (theoretisch von 0 bis 100 %, aber auch das teste ich sicherheitshalber nicht).

                          siehe Bild

                          image.jpeg

                          Alles läuft stabil, schnell und - mit Ausnahme der beschriebenen automatischen Reports, dem ich intensiv nachgehe - zu meiner vollen Zufriedenheit.
                          In einem Timer habe ich bereits eine viel versprechende Variable gefunden. Mal weiter verfolgen.

                          Sollte es keinen Weg geben oder nicht gefunden werden, dann werde ich - wie bei zenSDK - pollen aber über MQTT.

                          Warum nicht zenSDK verwenden?

                          Würde ich ehrlich gesagt sofort. Vielleicht wird das auch so werden.
                          Aber aktuell - um nur ein Beispiel zu nennen:
                          Über devMode kann socSet tiefer gesetzt werden als 70%.
                          Dadurch kann ich indirekt bestimmen, wann das Gerät in den Bypass geht oder nicht.

                          Für mich sind jetzt hauptsächlich zwei Dinge interessant:

                          1. Das Log einem korrekten Mapping zuzuordnen.

                          2. Die unglaublich vielen Stellen im Code zu analysieren, um eventuell ein zugängliches Flag etc. zu finden, welches dafür sorgt, dass künftig bei einer internen Wertänderung ein automatisches Publish ausgeführt wird.
                            Es ist unglaublich schwer zu finden, da es mehrere Verbindungswege gibt, die unterschiedlich abzweigen und teils identische, teils verschiedene Callbacks ausführen.

                          Sollte ich erreichen, dass report automatisch bei Änderung eines Wertes ein publish auslöst und der 5000ms Timer auch anspringt, dann wäre ich vorerst wunschlos glücklich - mit V2.0.2

                          Wer sich bedanken möchte, weiß ja, wo der Voting-Button ist. Ein extra Post dafür muss nicht sein. ;-)

                          Zendure zenSDK Lokal API, SmartMode, SolarFlow AC 800 Pro 2

                          neue Zendure Geräte Cloudless: zURLtool V2.0

                          maxclaudiM 2 Antworten Letzte Antwort
                          4
                          • D
                            D
                            Daniel 8
                            schrieb am zuletzt editiert von
                            #594

                            @maxclaudi

                            kann ich denn das DPL Blockly auch für meinen Solarflolw 800 Pro verwenden? Du hast das ja auf deine AC geräte ausgelegt oder? Ich brauche ja kein Laden aus dem Netz, weil ich ja nur über PV auflade.

                            Solarflow 800 Pro mit 1,3 Kwp / Iobroker / Homematic / Shellys / Mediola / Intertechno

                            maxclaudiM 1 Antwort Letzte Antwort
                            0
                            • maxclaudiM maxclaudi

                              @T-147 sagte:
                              Steuerung lief damit gestern auch super, bis die Batterie heute Nacht bei 24% aufgehört hat zu entladen, weil die Entladesperre gegriffen hat (laut Log). Ich verstehe nur noch nicht, warum so hoch? BatteryLockMinCellVoltage steht auf 3.2V.

                              Da hat die Entladesperre bei 3,2 V absolut richtig gegriffen. Nachdem die Batterien nicht mehr entladen wurden, haben sie sich im Ruhezustand (unbelasteter Zustand) etwas erholt, wodurch die Zellspannung wieder leicht angestiegen ist.
                              Warum das in Prozent (SoC) so hoch wirkt? Die Entladesperre greift, sobald die schwächste Zelle einer einzelnen Batterie den Mindestwert (Datenpunkt minVol) erreicht – und zwar über alle vorhandenen Batterien hinweg.
                              Wenn keine Last mehr anliegt, kommen die Zellen in eine Ruhephase, die Spannung steigt an, aber der SoC-Wert bleibt auf dem geschätzten Stand stehen.
                              Der SoC ist ohnehin nur ein errechneter/geschätzter Wert.

                              Wenn die Batterien nicht im kalten Außenbereich stehen, kannst du die Entladesperre testweise etwas niedriger ansetzen, z. B. auf 3,16 V. Zu niedrig würde ich allerdings nicht gehen, um die Zellen zu schonen. Taste dich da einfach heran.

                              Mir ist jetzt nur aufgefallen, dass die minVol bei mir im ioBroker mit einem Komma geschrieben wird und im Blockly mit einem Punkt als Trennzeichen. Das muss so sein oder kommt der damit evtl durcheinander?

                              Im Blockly-Code bitte zwingend den Punkt als Dezimaltrenner verwenden. Der Punkt ist in JavaScript Pflicht. Selbst wenn ioBroker ein Komma akzeptieren würde, bitte trotzdem den Punkt als Dezimaltrenner verwenden.

                              Achso, aktuell liegt minVol bei 3,2V und der SoC bei 15%, während das System ein wenig durch die Morgensonne geladen wird. Gestern lag der minVol bei 15% SoC bei 3,24V.
                              Woher kommen diese Unterschiede bei fast identischen Bedingungen gestern und heute?

                              Wie oben schon kurz angerissen: Die Spannung (Volt) ist ein harter, exakter Messwert.
                              Der SoC ist ein vom BMS mathematisch geschätzter und abgeleiteter Wert.
                              Diese Abweichungen sind völlig normal.
                              Je nachdem, wie hoch die Last kurz vor dem Abschalten war, bricht die Spannung unter Last unterschiedlich stark ein.
                              Bei hoher Last werden die 3,2 V Zellspannung viel früher (also bei einem höheren SoC) erreicht als bei einer sehr schwachen, langsamen Entladung.

                              T
                              T
                              T-147
                              schrieb am zuletzt editiert von
                              #595

                              @maxclaudi sagte:

                              @T-147 sagte:
                              Steuerung lief damit gestern auch super, bis die Batterie heute Nacht bei 24% aufgehört hat zu entladen, weil die Entladesperre gegriffen hat (laut Log). Ich verstehe nur noch nicht, warum so hoch? BatteryLockMinCellVoltage steht auf 3.2V.

                              Da hat die Entladesperre bei 3,2 V absolut richtig gegriffen. Nachdem die Batterien nicht mehr entladen wurden, haben sie sich im Ruhezustand (unbelasteter Zustand) etwas erholt, wodurch die Zellspannung wieder leicht angestiegen ist.
                              Warum das in Prozent (SoC) so hoch wirkt? Die Entladesperre greift, sobald die schwächste Zelle einer einzelnen Batterie den Mindestwert (Datenpunkt minVol) erreicht – und zwar über alle vorhandenen Batterien hinweg.
                              Wenn keine Last mehr anliegt, kommen die Zellen in eine Ruhephase, die Spannung steigt an, aber der SoC-Wert bleibt auf dem geschätzten Stand stehen.
                              Der SoC ist ohnehin nur ein errechneter/geschätzter Wert.

                              Vielen Dank für die Erklärung. Ich bin zwar elektrotechnisch und was Elektronik angeht relativ Fit, aber für solche Details reicht es dann bisher nicht (hatte auch nie wirklich Kontakt mit Batterie- und Akku-Technik)

                              Wenn die Batterien nicht im kalten Außenbereich stehen, kannst du die Entladesperre testweise etwas niedriger ansetzen, z. B. auf 3,16 V. Zu niedrig würde ich allerdings nicht gehen, um die Zellen zu schonen. Taste dich da einfach heran.

                              Mein SF800 steht auf dem Dachboden. Unbeheizt, aber im letzten Winter war er nie unter 14°. Dann taste ich mich da mal Stückweise heran. Jetzt im Herbst wird die Batterie eh nicht mehr ganz voll, da stört das experimentieren mit den Settings ja nicht so.

                              Im Blockly-Code bitte zwingend den Punkt als Dezimaltrenner verwenden. Der Punkt ist in JavaScript Pflicht. Selbst wenn ioBroker ein Komma akzeptieren würde, bitte trotzdem den Punkt als Dezimaltrenner verwenden.

                              👍

                              Ich hab dann gestern mal angefangen, dass alle Werte parallel in der InfluxDB landen: Lade- und Entladewerte, SoC, minVol von beiden Akkus und natürllich die Wirkleistung am Zähler.

                              Folgendes ist jezt heute Nacht passiert:

                              Line  819: 2026-10-08 01:59:50.891 - info: javascript.0 (276) script.js.Energieversorgung.Zendure.SF800_Pro_2_-_Nulleinspeisung_2026_09: Es gibt nichts zu tun, theoretisches positives newLimit: 180; oldLimit: 180
                              Line  820: 2026-10-08 02:00:00.879 - info: javascript.0 (276) script.js.Energieversorgung.Zendure.SF800_Pro_2_-_Nulleinspeisung_2026_09: newLimit positiv: 230 ist > oldLimit: 180 Es wird mehr entladen als zuvor.
                              Line  821: 2026-10-08 02:00:00.879 - info: javascript.0 (276) script.js.Energieversorgung.Zendure.SF800_Pro_2_-_Nulleinspeisung_2026_09: auto_in_out_Limit ist positiv oder 0: 230
                              Line  823: 2026-10-08 02:00:20.892 - info: javascript.0 (276) script.js.Energieversorgung.Zendure.SF800_Pro_2_-_Nulleinspeisung_2026_09: newLimit positiv: 250 ist > oldLimit: 230 Es wird mehr entladen als zuvor.
                              Line  824: 2026-10-08 02:00:20.893 - info: javascript.0 (276) script.js.Energieversorgung.Zendure.SF800_Pro_2_-_Nulleinspeisung_2026_09: auto_in_out_Limit ist positiv oder 0: 250
                              Line  827: 2026-10-08 02:00:40.925 - info: javascript.0 (276) script.js.Energieversorgung.Zendure.SF800_Pro_2_-_Nulleinspeisung_2026_09: newLimit positiv: 240 ist < oldLimit: 250 Es wird weniger entladen als zuvor.
                              Line  828: 2026-10-08 02:00:40.925 - info: javascript.0 (276) script.js.Energieversorgung.Zendure.SF800_Pro_2_-_Nulleinspeisung_2026_09: auto_in_out_Limit ist positiv oder 0: 240
                              Line  830: 2026-10-08 02:01:00.912 - info: javascript.0 (276) script.js.Energieversorgung.Zendure.SF800_Pro_2_-_Nulleinspeisung_2026_09: Es gibt nichts zu tun, theoretisches positives newLimit: 240; oldLimit: 240
                              Line  832: 2026-10-08 02:01:10.962 - info: javascript.0 (276) script.js.Energieversorgung.Zendure.SF800_Pro_2_-_Nulleinspeisung_2026_09: Es gibt nichts zu tun, theoretisches positives newLimit: 240; oldLimit: 240
                              Line  834: 2026-10-08 02:01:20.909 - info: javascript.0 (276) script.js.Energieversorgung.Zendure.SF800_Pro_2_-_Nulleinspeisung_2026_09: Es gibt nichts zu tun, theoretisches positives newLimit: 240; oldLimit: 240
                              Line  836: 2026-10-08 02:01:40.864 - info: javascript.0 (276) script.js.Energieversorgung.Zendure.SF800_Pro_2_-_Nulleinspeisung_2026_09: Es gibt nichts zu tun, theoretisches positives newLimit: 240; oldLimit: 240
                              Line  837: 2026-10-08 02:01:50.878 - info: javascript.0 (276) script.js.Energieversorgung.Zendure.SF800_Pro_2_-_Nulleinspeisung_2026_09: Es gibt nichts zu tun, theoretisches positives newLimit: 240; oldLimit: 240
                              Line  838: 2026-10-08 02:02:00.853 - info: javascript.0 (276) script.js.Energieversorgung.Zendure.SF800_Pro_2_-_Nulleinspeisung_2026_09: newLimit positiv: 230 ist < oldLimit: 240 Es wird weniger entladen als zuvor.
                              Line  839: 2026-10-08 02:02:00.854 - info: javascript.0 (276) script.js.Energieversorgung.Zendure.SF800_Pro_2_-_Nulleinspeisung_2026_09: auto_in_out_Limit ist positiv oder 0: 230
                              Line  841: 2026-10-08 02:02:20.894 - info: javascript.0 (276) script.js.Energieversorgung.Zendure.SF800_Pro_2_-_Nulleinspeisung_2026_09: Es gibt nichts zu tun, theoretisches positives newLimit: 230; oldLimit: 230
                              Line  842: 2026-10-08 02:02:30.902 - info: javascript.0 (276) script.js.Energieversorgung.Zendure.SF800_Pro_2_-_Nulleinspeisung_2026_09: newLimit positiv: 240 ist > oldLimit: 230 Es wird mehr entladen als zuvor.
                              Line  843: 2026-10-08 02:02:30.903 - info: javascript.0 (276) script.js.Energieversorgung.Zendure.SF800_Pro_2_-_Nulleinspeisung_2026_09: auto_in_out_Limit ist positiv oder 0: 240
                              Line  845: 2026-10-08 02:02:50.854 - info: javascript.0 (276) script.js.Energieversorgung.Zendure.SF800_Pro_2_-
                              ...
                              Line  870: 2026-10-08 02:06:30.883 - info: javascript.0 (276) script.js.Energieversorgung.Zendure.SF800_Pro_2_-_Nulleinspeisung_2026_09: Es gibt nichts zu tun, theoretisches positives newLimit: 240; oldLimit: 240
                              Line  872: 2026-10-08 02:06:40.879 - info: javascript.0 (276) script.js.Energieversorgung.Zendure.SF800_Pro_2_-_Nulleinspeisung_2026_09: Es gibt nichts zu tun, theoretisches positives newLimit: 240; oldLimit: 240
                              **Line  873: 2026-10-08 02:06:50.916 - info: javascript.0 (276) script.js.Energieversorgung.Zendure.SF800_Pro_2_-_Nulleinspeisung_2026_09: auto_in_out_Limit 0: 0Entladen gesperrt (Batterieschutz aktiv / MinSoC erreicht)
                              Line 1525: 2026-10-08 05:06:41.039 - info: javascript.0 (276) script.js.Energieversorgung.Zendure.SF800_Pro_2_-_Nulleinspeisung_2026_09: auto_in_out_Limit ist positiv, hoher Leistungsabruf: 800
                              Line 1957: 2026-10-08 07:17:21.231 - info: javascript.0 (276) script.js.Energieversorgung.Zendure.SF800_Pro_2_-_Nulleinspeisung_2026_09: auto_in_out_Limit 0: 0Entladen gesperrt (Batterieschutz aktiv / MinSoC erreicht)**
                              
                              


                              Um 2:06 Uhr hat die Entladesperre gegriffen, weil minVol bei einem Akku bei 3,19V war. Soweit alles richtig gemacht.
                              Aber der SF800 ist dann auf dem letzten Entladewert von 240W stehen geblieben und hat fleißig weiter entladen, bis um der minSoc von 10% (vermutlich aus der App) gegriffen hat. Das war um 4:33 Uhr, da war der minVol schon bei 2,84V. In den folgenden 4 Minuten ist der SoC auf 5% gefallen (ja, ich weiß, ist eh nur nen Schätzwert), während der minVol sich etwas "erholt" hat und stetig wieder anstieg.
                              Um 5:48 Uhr fing der SF800 an, mit 1000W zu laden, wohl wegen des niedrigen Batteriestandes. Um 5:06 Uhr wechselte der SF800 von 1000W laden zu 800W entladen (offensichtlich erkennt er sein eigenes Aufladen als hohen Leistungsabruf vom Netz). Das Entladen mit vollen 800W dauerte etwa 7 Minuten, wobei über 600W ins Stromnetz gingen. Dann war der SoC nach dem kurzen Aufladen wieder auf 10% und es kehrte Ruhe ein.
                              Aktuell lädt der SF800 gemächlich in die Batterie und seit 7:17 Uhr keinerlei Einträge mehr im Log.
                              Update: 10:31 Uhr ist das System in den Bypass gewechselt.

                              Läuft also alles noch ein wenig unrund, zumindest bei mir mit dem neueren Blockly.
                              Ich beobachte das mal weiter und schaue, ob ich ein Schema oder die Ursache finde.

                              maxclaudiM 1 Antwort Letzte Antwort
                              0
                              • D Daniel 8

                                @maxclaudi

                                kann ich denn das DPL Blockly auch für meinen Solarflolw 800 Pro verwenden? Du hast das ja auf deine AC geräte ausgelegt oder? Ich brauche ja kein Laden aus dem Netz, weil ich ja nur über PV auflade.

                                maxclaudiM
                                maxclaudiM
                                maxclaudi
                                schrieb am zuletzt editiert von
                                #596

                                @Daniel-8 sagte:

                                @maxclaudi

                                kann ich denn das DPL Blockly auch für meinen Solarflolw 800 Pro verwenden? Du hast das ja auf deine AC geräte ausgelegt oder? Ich brauche ja kein Laden aus dem Netz, weil ich ja nur über PV auflade.

                                Ja.
                                Noch einmal ein Hinweis dazu:

                                Es ist und soll nur eine Vorlage als Basis sein, die man selbst bearbeiten und auf sein Setup anpassen sollte.
                                Ich habe es nicht auf reine AC-Geräte ausgelegt, aber auf Geräte, die über AC laden können. Berücksichtigt ist dabei, dass wenn das Gridmeter negative Werte meldet, dann Richtung 0 die Batterie(n) geladen wird/werden.
                                Z. B. falls ein autarker, weiterer Wechselrichter mit PV-Modulen vorhanden ist und einspeist.
                                Das Blockly sollte nicht blind übernommen werden.
                                Es ist kein „Rundum-sorglos-Paket zum blind Kopieren“, sondern eine Vorlage, die Eigeninitiative erfordert.
                                Fehler können immer vorhanden sein oder es funktioniert für das eigene Setup / den eigenen Verwendungszweck nicht direkt aus dem Stand heraus fehlerfrei und perfekt.


                                08.10.2026 14:26h
                                Weitere Erkenntnis und wahrscheinlich weltweit der erste Hinweis zum LOG-ARRAY:
                                Das params-Array im Log-Topic umfasst zwar stets exakt 200 Werte, es handelt sich dabei jedoch nicht um ein statisches Layout!
                                Die Struktur ist dynamisch und verschiebt sich nach hinten, je nachdem, wie viele Batterien (packNum) im System aktiv sind.
                                Es werden im vorderen Block keine leeren Null-Plätze für optionale Akkus reserviert, sondern staucht oder dehnt die Batterie-Datenfelder sequentiell.
                                Das bedeutet: ein starrer Zugriff auf feste Indizes (z. B. für PV-Leistung oder AC-Laden) funktioniert nur so lange, wie sich die Anzahl der Akkus nicht ändert.
                                Für ein universelles Skript muss die Verschiebung anhand des Akku-Zählers auf Index [9] dynamisch berechnet werden!

                                Zendure zenSDK Lokal API, SmartMode, SolarFlow AC 800 Pro 2

                                neue Zendure Geräte Cloudless: zURLtool V2.0

                                D 1 Antwort Letzte Antwort
                                1
                                • T T-147

                                  @maxclaudi sagte:

                                  @T-147 sagte:
                                  Steuerung lief damit gestern auch super, bis die Batterie heute Nacht bei 24% aufgehört hat zu entladen, weil die Entladesperre gegriffen hat (laut Log). Ich verstehe nur noch nicht, warum so hoch? BatteryLockMinCellVoltage steht auf 3.2V.

                                  Da hat die Entladesperre bei 3,2 V absolut richtig gegriffen. Nachdem die Batterien nicht mehr entladen wurden, haben sie sich im Ruhezustand (unbelasteter Zustand) etwas erholt, wodurch die Zellspannung wieder leicht angestiegen ist.
                                  Warum das in Prozent (SoC) so hoch wirkt? Die Entladesperre greift, sobald die schwächste Zelle einer einzelnen Batterie den Mindestwert (Datenpunkt minVol) erreicht – und zwar über alle vorhandenen Batterien hinweg.
                                  Wenn keine Last mehr anliegt, kommen die Zellen in eine Ruhephase, die Spannung steigt an, aber der SoC-Wert bleibt auf dem geschätzten Stand stehen.
                                  Der SoC ist ohnehin nur ein errechneter/geschätzter Wert.

                                  Vielen Dank für die Erklärung. Ich bin zwar elektrotechnisch und was Elektronik angeht relativ Fit, aber für solche Details reicht es dann bisher nicht (hatte auch nie wirklich Kontakt mit Batterie- und Akku-Technik)

                                  Wenn die Batterien nicht im kalten Außenbereich stehen, kannst du die Entladesperre testweise etwas niedriger ansetzen, z. B. auf 3,16 V. Zu niedrig würde ich allerdings nicht gehen, um die Zellen zu schonen. Taste dich da einfach heran.

                                  Mein SF800 steht auf dem Dachboden. Unbeheizt, aber im letzten Winter war er nie unter 14°. Dann taste ich mich da mal Stückweise heran. Jetzt im Herbst wird die Batterie eh nicht mehr ganz voll, da stört das experimentieren mit den Settings ja nicht so.

                                  Im Blockly-Code bitte zwingend den Punkt als Dezimaltrenner verwenden. Der Punkt ist in JavaScript Pflicht. Selbst wenn ioBroker ein Komma akzeptieren würde, bitte trotzdem den Punkt als Dezimaltrenner verwenden.

                                  👍

                                  Ich hab dann gestern mal angefangen, dass alle Werte parallel in der InfluxDB landen: Lade- und Entladewerte, SoC, minVol von beiden Akkus und natürllich die Wirkleistung am Zähler.

                                  Folgendes ist jezt heute Nacht passiert:

                                  Line  819: 2026-10-08 01:59:50.891 - info: javascript.0 (276) script.js.Energieversorgung.Zendure.SF800_Pro_2_-_Nulleinspeisung_2026_09: Es gibt nichts zu tun, theoretisches positives newLimit: 180; oldLimit: 180
                                  Line  820: 2026-10-08 02:00:00.879 - info: javascript.0 (276) script.js.Energieversorgung.Zendure.SF800_Pro_2_-_Nulleinspeisung_2026_09: newLimit positiv: 230 ist > oldLimit: 180 Es wird mehr entladen als zuvor.
                                  Line  821: 2026-10-08 02:00:00.879 - info: javascript.0 (276) script.js.Energieversorgung.Zendure.SF800_Pro_2_-_Nulleinspeisung_2026_09: auto_in_out_Limit ist positiv oder 0: 230
                                  Line  823: 2026-10-08 02:00:20.892 - info: javascript.0 (276) script.js.Energieversorgung.Zendure.SF800_Pro_2_-_Nulleinspeisung_2026_09: newLimit positiv: 250 ist > oldLimit: 230 Es wird mehr entladen als zuvor.
                                  Line  824: 2026-10-08 02:00:20.893 - info: javascript.0 (276) script.js.Energieversorgung.Zendure.SF800_Pro_2_-_Nulleinspeisung_2026_09: auto_in_out_Limit ist positiv oder 0: 250
                                  Line  827: 2026-10-08 02:00:40.925 - info: javascript.0 (276) script.js.Energieversorgung.Zendure.SF800_Pro_2_-_Nulleinspeisung_2026_09: newLimit positiv: 240 ist < oldLimit: 250 Es wird weniger entladen als zuvor.
                                  Line  828: 2026-10-08 02:00:40.925 - info: javascript.0 (276) script.js.Energieversorgung.Zendure.SF800_Pro_2_-_Nulleinspeisung_2026_09: auto_in_out_Limit ist positiv oder 0: 240
                                  Line  830: 2026-10-08 02:01:00.912 - info: javascript.0 (276) script.js.Energieversorgung.Zendure.SF800_Pro_2_-_Nulleinspeisung_2026_09: Es gibt nichts zu tun, theoretisches positives newLimit: 240; oldLimit: 240
                                  Line  832: 2026-10-08 02:01:10.962 - info: javascript.0 (276) script.js.Energieversorgung.Zendure.SF800_Pro_2_-_Nulleinspeisung_2026_09: Es gibt nichts zu tun, theoretisches positives newLimit: 240; oldLimit: 240
                                  Line  834: 2026-10-08 02:01:20.909 - info: javascript.0 (276) script.js.Energieversorgung.Zendure.SF800_Pro_2_-_Nulleinspeisung_2026_09: Es gibt nichts zu tun, theoretisches positives newLimit: 240; oldLimit: 240
                                  Line  836: 2026-10-08 02:01:40.864 - info: javascript.0 (276) script.js.Energieversorgung.Zendure.SF800_Pro_2_-_Nulleinspeisung_2026_09: Es gibt nichts zu tun, theoretisches positives newLimit: 240; oldLimit: 240
                                  Line  837: 2026-10-08 02:01:50.878 - info: javascript.0 (276) script.js.Energieversorgung.Zendure.SF800_Pro_2_-_Nulleinspeisung_2026_09: Es gibt nichts zu tun, theoretisches positives newLimit: 240; oldLimit: 240
                                  Line  838: 2026-10-08 02:02:00.853 - info: javascript.0 (276) script.js.Energieversorgung.Zendure.SF800_Pro_2_-_Nulleinspeisung_2026_09: newLimit positiv: 230 ist < oldLimit: 240 Es wird weniger entladen als zuvor.
                                  Line  839: 2026-10-08 02:02:00.854 - info: javascript.0 (276) script.js.Energieversorgung.Zendure.SF800_Pro_2_-_Nulleinspeisung_2026_09: auto_in_out_Limit ist positiv oder 0: 230
                                  Line  841: 2026-10-08 02:02:20.894 - info: javascript.0 (276) script.js.Energieversorgung.Zendure.SF800_Pro_2_-_Nulleinspeisung_2026_09: Es gibt nichts zu tun, theoretisches positives newLimit: 230; oldLimit: 230
                                  Line  842: 2026-10-08 02:02:30.902 - info: javascript.0 (276) script.js.Energieversorgung.Zendure.SF800_Pro_2_-_Nulleinspeisung_2026_09: newLimit positiv: 240 ist > oldLimit: 230 Es wird mehr entladen als zuvor.
                                  Line  843: 2026-10-08 02:02:30.903 - info: javascript.0 (276) script.js.Energieversorgung.Zendure.SF800_Pro_2_-_Nulleinspeisung_2026_09: auto_in_out_Limit ist positiv oder 0: 240
                                  Line  845: 2026-10-08 02:02:50.854 - info: javascript.0 (276) script.js.Energieversorgung.Zendure.SF800_Pro_2_-
                                  ...
                                  Line  870: 2026-10-08 02:06:30.883 - info: javascript.0 (276) script.js.Energieversorgung.Zendure.SF800_Pro_2_-_Nulleinspeisung_2026_09: Es gibt nichts zu tun, theoretisches positives newLimit: 240; oldLimit: 240
                                  Line  872: 2026-10-08 02:06:40.879 - info: javascript.0 (276) script.js.Energieversorgung.Zendure.SF800_Pro_2_-_Nulleinspeisung_2026_09: Es gibt nichts zu tun, theoretisches positives newLimit: 240; oldLimit: 240
                                  **Line  873: 2026-10-08 02:06:50.916 - info: javascript.0 (276) script.js.Energieversorgung.Zendure.SF800_Pro_2_-_Nulleinspeisung_2026_09: auto_in_out_Limit 0: 0Entladen gesperrt (Batterieschutz aktiv / MinSoC erreicht)
                                  Line 1525: 2026-10-08 05:06:41.039 - info: javascript.0 (276) script.js.Energieversorgung.Zendure.SF800_Pro_2_-_Nulleinspeisung_2026_09: auto_in_out_Limit ist positiv, hoher Leistungsabruf: 800
                                  Line 1957: 2026-10-08 07:17:21.231 - info: javascript.0 (276) script.js.Energieversorgung.Zendure.SF800_Pro_2_-_Nulleinspeisung_2026_09: auto_in_out_Limit 0: 0Entladen gesperrt (Batterieschutz aktiv / MinSoC erreicht)**
                                  
                                  


                                  Um 2:06 Uhr hat die Entladesperre gegriffen, weil minVol bei einem Akku bei 3,19V war. Soweit alles richtig gemacht.
                                  Aber der SF800 ist dann auf dem letzten Entladewert von 240W stehen geblieben und hat fleißig weiter entladen, bis um der minSoc von 10% (vermutlich aus der App) gegriffen hat. Das war um 4:33 Uhr, da war der minVol schon bei 2,84V. In den folgenden 4 Minuten ist der SoC auf 5% gefallen (ja, ich weiß, ist eh nur nen Schätzwert), während der minVol sich etwas "erholt" hat und stetig wieder anstieg.
                                  Um 5:48 Uhr fing der SF800 an, mit 1000W zu laden, wohl wegen des niedrigen Batteriestandes. Um 5:06 Uhr wechselte der SF800 von 1000W laden zu 800W entladen (offensichtlich erkennt er sein eigenes Aufladen als hohen Leistungsabruf vom Netz). Das Entladen mit vollen 800W dauerte etwa 7 Minuten, wobei über 600W ins Stromnetz gingen. Dann war der SoC nach dem kurzen Aufladen wieder auf 10% und es kehrte Ruhe ein.
                                  Aktuell lädt der SF800 gemächlich in die Batterie und seit 7:17 Uhr keinerlei Einträge mehr im Log.
                                  Update: 10:31 Uhr ist das System in den Bypass gewechselt.

                                  Läuft also alles noch ein wenig unrund, zumindest bei mir mit dem neueren Blockly.
                                  Ich beobachte das mal weiter und schaue, ob ich ein Schema oder die Ursache finde.

                                  maxclaudiM
                                  maxclaudiM
                                  maxclaudi
                                  schrieb am zuletzt editiert von
                                  #597

                                  @T-147
                                  genau für solche Rückmeldungen ist die Vorlage da!
                                  Danke für das genaue Log.
                                  Du hast hier einen extremen Ausnahmefall aufgedeckt, den das Skript in dieser Basis-Version so noch nicht abfangen konnte.

                                  Was ist passiert?
                                  Deine Batterie ist in den hardwareseitigen Notlademodus gegangen und hat sich mit 1000W aus dem Netz gerettet.
                                  Das Skript hat diesen plötzlichen Netzbezug über das Gridmeter registriert und dachte wegen des criticalMode, im Haus sei ein riesiger Verbraucher angesprungen.
                                  Daraufhin hat es die Entladung wieder freigegeben und gegen die eigene Notladung des Hubs angekämpft.
                                  Ein klassischer Logik-Teufelskreis.

                                  Der criticalMode und der normale Regelkreis müssen sofort blockiert werden, wenn der Batterieschutz aktiv ist.
                                  Ich schaue mir das noch genauer an, damit der batteryLock immer die oberste Priorität hat und nicht mehr vom eigenen Laden ausgetrickst werden kann.

                                  Möchtest Du bitte dieses Blockly testen:

                                  updateBlockly-Test.xml

                                  Zendure zenSDK Lokal API, SmartMode, SolarFlow AC 800 Pro 2

                                  neue Zendure Geräte Cloudless: zURLtool V2.0

                                  T 2 Antworten Letzte Antwort
                                  0
                                  • maxclaudiM maxclaudi

                                    @Daniel-8 sagte:

                                    @maxclaudi

                                    kann ich denn das DPL Blockly auch für meinen Solarflolw 800 Pro verwenden? Du hast das ja auf deine AC geräte ausgelegt oder? Ich brauche ja kein Laden aus dem Netz, weil ich ja nur über PV auflade.

                                    Ja.
                                    Noch einmal ein Hinweis dazu:

                                    Es ist und soll nur eine Vorlage als Basis sein, die man selbst bearbeiten und auf sein Setup anpassen sollte.
                                    Ich habe es nicht auf reine AC-Geräte ausgelegt, aber auf Geräte, die über AC laden können. Berücksichtigt ist dabei, dass wenn das Gridmeter negative Werte meldet, dann Richtung 0 die Batterie(n) geladen wird/werden.
                                    Z. B. falls ein autarker, weiterer Wechselrichter mit PV-Modulen vorhanden ist und einspeist.
                                    Das Blockly sollte nicht blind übernommen werden.
                                    Es ist kein „Rundum-sorglos-Paket zum blind Kopieren“, sondern eine Vorlage, die Eigeninitiative erfordert.
                                    Fehler können immer vorhanden sein oder es funktioniert für das eigene Setup / den eigenen Verwendungszweck nicht direkt aus dem Stand heraus fehlerfrei und perfekt.


                                    08.10.2026 14:26h
                                    Weitere Erkenntnis und wahrscheinlich weltweit der erste Hinweis zum LOG-ARRAY:
                                    Das params-Array im Log-Topic umfasst zwar stets exakt 200 Werte, es handelt sich dabei jedoch nicht um ein statisches Layout!
                                    Die Struktur ist dynamisch und verschiebt sich nach hinten, je nachdem, wie viele Batterien (packNum) im System aktiv sind.
                                    Es werden im vorderen Block keine leeren Null-Plätze für optionale Akkus reserviert, sondern staucht oder dehnt die Batterie-Datenfelder sequentiell.
                                    Das bedeutet: ein starrer Zugriff auf feste Indizes (z. B. für PV-Leistung oder AC-Laden) funktioniert nur so lange, wie sich die Anzahl der Akkus nicht ändert.
                                    Für ein universelles Skript muss die Verschiebung anhand des Akku-Zählers auf Index [9] dynamisch berechnet werden!

                                    D
                                    D
                                    Daniel 8
                                    schrieb am zuletzt editiert von
                                    #598

                                    @maxclaudi sagte:

                                    Das Blockly sollte nicht blind übernommen werden.
                                    Es ist kein „Rundum-sorglos-Paket zum blind Kopieren“, sondern eine Vorlage, die Eigeninitiative erfordert.

                                    Das hatte ich auch nicht vor es blind zu übernehmen. Wenn ich mal ne lange ruhige Minute habe schaue ich es mir genauer an.

                                    Meine Steuerung läuft ja aktuell. Vielleicht nicht so perfekt aufgebaut wie dein blockly aber es tut.

                                    Danke für deine ganze Mühe

                                    Solarflow 800 Pro mit 1,3 Kwp / Iobroker / Homematic / Shellys / Mediola / Intertechno

                                    1 Antwort Letzte Antwort
                                    1
                                    • maxclaudiM maxclaudi

                                      NEU
                                      Zendure-Geräte Webserver: Steuerung über das zenSDK (HTTP)
                                      2026.05.02_00.39h für das ioBroker-Forum; Update 24.05.26 00:15h globale Typsicherung
                                      update axios 21.06.26 00:10h. Danke paul53 für die Idee und das Mitwirken.

                                      Update 22.06.26 20:00h zenSDK API 3
                                      API 2 funktioniert weiterhin.
                                      Bitte Beschreibung im Script beachten.
                                      update 25.06.26 10.30h control FAN

                                      update 06.10.26 02:00h neu set batCalTime, fix acMode/auto_in_out_Limit - Übernehmer von Logiken etc. könnten sich auch mal "wenigstens" bedanken!

                                      update 09.10.26 11:30h fix acMode/auto_in_out_Limit, Standardwert Intervall
                                      Danke Danitz und petman für Hinweise.

                                      Wer bisher das Script bereits nutzt: Bitte vor Ausführung des Scripts den Datenpunkt:

                                      control.acMode
                                      

                                      löschen.


                                      update 16.10.26 21:00h
                                      batCalTime zusätzliche Type-Prüfung; fallback 01:00h / control.socCompSwitch ohne Funktion, entfernt

                                      Wer bisher das Script bereits nutzt: Bitte vor Ausführung des Scripts den Datenpunkt:

                                      control.socCompSwitch
                                      

                                      löschen.


                                      In memory of Daisy 02.05.24 – miss you.

                                      Viel Spaß!


                                      // ioBroker JavaScript: Zendure zenSDK Adapter-Ersatz für ein Zendure-Gerät.
                                      // Für alle Geräte ab 2025/2026, die zenSDK unterstützen, wie z. B.:
                                      // SF800, SF800 PLUS, 800Pro (2), 1600AC, SF2400AC(+) usw.
                                      // (c) maxclaudi 2026.05.02_00.39h für das ioBroker-Forum; update 24.05.26 00:15h globale Typsicherung.
                                      // update axios 21.06.26 00:10h. Danke paul53 für die Idee und das Mitwirken.																			  
                                      // update 22.06.26 20:00h API3 und API2 Unterstützung; update 25.06.26 10.30h control FAN
                                      // update 06.10.26 02:00h neu set batCalTime, fix acMode/auto_in_out_Limit - Übernehmer von Logiken etc. könnten sich auch mal "wenigstens" bedanken!
                                      // update 09.10.26 11:30h acMode/auto_in_out_Limit Standardwert Intervall Danke Danitz und petman für die Hinweise.
                                      // update 16.10.26 21:00h batCalTime zusätzliche Type-Prüfung; fallback 01:00h / control.socCompSwitch ohne Funktion, entfernt
                                      // 
                                      // In memory of Daisy 02.05.24 – miss you.
                                      //
                                      //
                                      // 
                                      // EIN WORT IN EIGENER SACHE:
                                      // Dieses Skript basiert auf vielen Stunden Hardware-Tests und Analysen (besonders zum Flash-Schutz).
                                      // In der Vergangenheit wurden meine Erkenntnisse oft ohne Erwähnung in andere Projekte übernommen.
                                      // Ich teile diesen Code gerne. Wer diese Logik in öffentliche Adapter integriert, ist herzlich 
                                      // eingeladen – ich bitte jedoch um die Fairness, die Quelle zu nennen. 
                                      // Das ist der "Lohn" für meine Zeit und Forschung.
                                      //----------------------------------------------------------------------------------------------------
                                      //----------------------------------------------------------------------------------------------------
                                      // Konfiguration
                                      //
                                      // Hinweis bei mehreren Zendure-Geräten:
                                      //   -> Für jedes Gerät ein eigenes Skript mit individueller Konfiguration verwenden!
                                      //   -> IP-Adresse anpassen.
                                      //   -> maxInputLimit / maxOutputLimit (abhängig vom Gerätetyp) einstellen.
                                      //   -> Intervall: getIntervalMs = 5000 ms (Standard). 
                                      //      Bei Verbindungsproblemen oder WLAN-Lags wird ein Wert von mindestens 8000 ms empfohlen.
                                      //
                                      // Empfehlung zur Geräteanzahl:
                                      //   • Bis zu 3 Geräte: völlig unkritisch.
                                      //   • 4 Geräte: problemlos möglich.
                                      //   • Mehr als 4 Geräte: nicht empfohlen. Aber abhängig von Systemleistung und Intervall prüfen.
                                      //
                                      // Das offizielle MQTT ist mit einer Aktualisierungsrate von bis zu 90 Sek. zu langsam.
                                      // Ich empfehle die Nutzung des zenSDK.
                                      // Hinweis: Das (De-)Aktivieren von MQTT sowie die MQTT-Verbindungsüberwachung werden nicht unterstützt.
                                      //
                                      // Damit neue Datenpunkte automatisch aus dem JSON erstellt werden können:
                                      // -> Instanzen -> JavaScript-Adapter -> Allgemeine Einstellungen -> "Kommando 'setObject' erlauben" aktivieren!
                                      //
                                      // Bei kurzem Abfrageintervall:
                                      // -> Instanzen -> JavaScript-Adapter -> Allgemeine Einstellungen -> 
                                      //    "Maximale setState-Anfragen pro Minute pro Skript" auf 5000 erhöhen.
                                      //    (Testweise reicht ein geringerer Wert wie 2000; das Skript wurde optimiert).
                                      //
                                      // Datenpunkte im Ordner "control" werden automatisch aktualisiert.
                                      //----------------------------------------------------------------------------------------------------
                                      //----------------------------------------------------------------------------------------------------
                                      // CONFIG
                                      //----------------------------------------------------------------------------------------------------
                                       
                                      // IP Zendure Gerät
                                      //Beispiel: const IP = "192.168.40.20";
                                      //const IP = "192.168.40.20";  // IP des Zendure Geräts
                                      const IP = "192.168.40.20";  // IP des Zendure Geräts
                                       
                                      // Bitte sicherstellen:
                                      // richtige IP verwendet?
                                      // Im Router dem Zendure-Gerät eine feste, dauerhafte IP zuweisen!
                                       
                                      //maximum inputLimit -> Maximal mögliche (unterstützte) Ladeleistung des Zendure-Geräts 
                                      //Beispiele: SF800 800W / SF800 PRO: 1000W / 1600AC: 1600 / SF2400AC: 2400W
                                      const maxInputLimit = 2400;
                                       
                                      //maximum outputLimit -> Maximal möglich (unterstützte) Entladeleistung des Zendure-Geräts
                                      //Beispiele: SF800 800W / SF800 PRO: 800W / 1600AC: 1600 / SF2400AC: 2400W
                                      const maxOutputLimit = 2400;
                                       
                                      // Haupt-Verzeichnis für das Zendure-Gerät
                                      // Der Name für das Hauptverzeichnis kann frei gewählt werden.
                                      // Bitte kein "+", keine Sonderzeichen und keine Umlaute etc. verwenden!
                                      // Muss aber bei mehreren Skripten und Zendure-Geräte unterschiedlich sein.
                                      // Beispiel für "2400ACproZENsdk":
                                      //const folderZendureApi = '0_userdata.0.zendure.' + "2400ACproZENsdk";
                                      const folderZendureApi = '0_userdata.0.zendure.' + "2400ACproZENsdk";
                                       
                                      // Timout Handler HTTP GET /POST
                                      // Wichtig:
                                      // getTimeoutMs < getIntervalMs
                                      // postTimeoutMs < getIntervalMs
                                      // Bei instabiler Verbindung ggf. erhöhen (z. B. 3000–5000 ms).
                                      const getTimeoutMs = 1500;  // Empfehlung: 2000 ms.
                                      const postTimeoutMs = 1500; // Empfehlung: 2000 ms.
                                       
                                      // GET intervall
                                      // Empfehlung: >= 5000 ms.
                                      // Kleinere Werte möglich, aber kleiner 4000 m. M. nach sinnfrei und nicht empfohlen (Last / Stabilität).
                                      // HINWEIS: Original Firmwarecode verifiziert, dass z.B. bei MQTT Standard alle 5000ms ein report published wird.
                                      // wenn noch schneller abgefragt wird, kann es vorkommen, dass alte/identische Werte (hardwarebedingt) empfangen werden.
                                      const getIntervalMs = 5000; // Intervall für GET 5000ms. Bei Problemen erhöhen. NICHT < 5000!
                                       
                                      //----------------------------------------------------------------------------------------------------
                                      // END CONFIG
                                      //----------------------------------------------------------------------------------------------------
                                       
                                      let rxNewRam = "";
                                      let rxOldRam = "";
                                      let rxDiffRam = "";
                                      let txRam = "";
                                       
                                      const dpSmartRAM = folderZendureApi + ".control.automaticKonfig.input_output_LimitMode_smartMode_RAM";
                                      const dpSmartWatcher = folderZendureApi + ".control.automaticKonfig.smartModeWatcher";
                                       
                                      // helper
                                      function getRxNew() { return rxNewRam; }
                                      function setRxNew(val) {
                                          rxNewRam = val;
                                      }
                                       
                                      function getRxOld() { return rxOldRam; }
                                      function setRxOld(val) {
                                          rxOldRam = val;
                                      }
                                       
                                      function getRxDiff() { return rxDiffRam; }
                                      function setRxDiff(val) {
                                          rxDiffRam = val;
                                      }
                                       
                                      function setTx(val) {
                                          txRam = val;
                                      }
                                       
                                      const axios = require('axios'); 
                                      const postCooldownMs = 2000;
                                      const batchWindowMs = 300;
                                       
                                      let postActive = false;
                                      let postQueue = {};
                                      let batchTimer = null;
                                      let getTimer = null;
                                       
                                      let retryCount = 0; 
                                      const maxRetries = 2; 
                                      let getErrorCount = 0; 
                                      let SN = '';
                                       
                                      // Struktur sicherstellen
                                      setTimeout(() => {
                                          ensureStructure();
                                      }, 100);
                                       
                                      function ensureStructure() {
                                          const folders = [
                                              folderZendureApi,
                                              folderZendureApi + ".properties",
                                              folderZendureApi + ".packData",
                                              folderZendureApi + ".control",
                                              folderZendureApi + ".control.automaticKonfig"
                                          ];
                                          folders.forEach(path => {
                                              if (!existsObject(path)) {
                                                  setObject(path, {
                                                      type: "folder",
                                                      common: { name: path.split(".").pop() },
                                                      native: {}
                                                  });
                                              }
                                          });
                                          if (!existsState(dpSmartRAM)) {
                                              createState(dpSmartRAM, false, { name: "Input/Output Limit Mode SmartMode RAM", type: "boolean", role: "switch", read: true, write: true });
                                          }
                                          if (!existsState(dpSmartWatcher)) {
                                              createState(dpSmartWatcher, false, { name: "Smart Mode Watcher", type: "boolean", role: "switch", read: true, write: true });
                                          }
                                      	if (existsState(dpSmartRAM)) setState(dpSmartRAM, false, true);
                                      	if (existsState(dpSmartWatcher)) setState(dpSmartWatcher, false, true);
                                      }
                                       
                                      function hhmmToMinutes(str) {
                                          const m = /^(\d{1,2}):(\d{2})$/.exec(String(str).trim());
                                          if (!m) return 60;
                                          const total = (parseInt(m[1], 10) * 60) + parseInt(m[2], 10);
                                          return (total > 1439) ? 60 : total;
                                      }
                                      function minutesToHhmm(min) {
                                          const n = Math.round(Number(min));
                                          if (!Number.isFinite(n) || n < 0 || n > 1439) return String(min);
                                          return String(Math.floor(n / 60)).padStart(2, "0") + ":" + String(n % 60).padStart(2, "0");
                                      }
                                       
                                      // Control
                                      const schemaControl = {
                                      	acMode: {
                                              name: "set acMode; 1: input charge, 2: output discharge",
                                              role: "level",
                                              type: "number",
                                              write: true,
                                      		def: 2,
                                              min: 1,
                                              max: 2,
                                              apiKey: "acMode",
                                          },
                                      	batCalTime: {
                                              name: "set Calibration-Start-Time: 00:00 to 23:59 - on error: 01:00h (HH:MM)",
                                              role: "text",
                                      		unit: "h",
                                              type: "string",
                                              write: true,
                                      		def: "01:00",
                                      		pattern: /^([01][0-9]|2[0-3]):[0-5][0-9]$/,
                                              apiKey: "batCalTime",
                                      		transformWrite: (val) => hhmmToMinutes(val),
                                      		transformRead: (val) => minutesToHhmm(val),
                                          },
                                      	chargeMaxLimit: {
                                              name: "set Charge Max Limit",
                                              unit: "W",
                                              role: "level",
                                              type: "number",
                                              write: true,
                                      		def: maxInputLimit,
                                              min: 0,
                                              max: maxInputLimit,
                                              apiKey: "chargeMaxLimit",
                                          },	
                                      	Fanmode: {
                                      		name: "set Fanmode; 0: Fan OFF, 1: Fan ON",
                                              role: "level",
                                              type: "number",
                                              write: true,
                                      		def: 1,
                                              min: 0,
                                              max: 1,
                                              apiKey: "Fanmode",
                                          },
                                      	Fanspeed: {
                                      		name: "set Fanspeed; 0: Automatic, 1: 1st gear, 2: 2nd gear",
                                              role: "level",
                                              type: "number",
                                              write: true,
                                      		def: 0,
                                              min: 0,
                                              max: 2,
                                              apiKey: "Fanspeed",
                                          },
                                      	gridOffMode: {
                                              name: "set gridOffMode; 0: Normal mode, 1: Economic mode, 2: OFF",
                                              role: "level",
                                              type: "number",
                                              write: true,
                                      		def: 2,
                                              min: 0,
                                              max: 2,
                                              apiKey: "gridOffMode",
                                          },
                                      	gridReverse: {
                                              name: "set gridReverse; 0: Disabled, 1: Allowed reverse flow, 2: Forbidden reverse flow",
                                              role: "level",
                                              type: "number",
                                              write: true,
                                      		def: 0,
                                              min: 0,
                                              max: 2,
                                              apiKey: "gridReverse",
                                          },
                                      	gridStandard: {
                                              name: "set gridStandard; 0: Germany 1: France 2: Austria 3: Switzerland 4: Netherlands 5: Spain 6: Belgium 7: Greece 8: Denmark 9: Italy",
                                              role: "level",
                                              type: "number",
                                              write: true,
                                      		def: 0,
                                              min: 0,
                                              max: 9,
                                              apiKey: "gridStandard",
                                          },
                                      	inputLimit: {
                                              name: "set inputLimit",
                                              unit: "W",
                                              role: "level",
                                              type: "number",
                                              write: true,
                                      		def: 0,
                                              min: 0,
                                              max: maxInputLimit,
                                              apiKey: "inputLimit",
                                          },
                                      	inverseMaxPower: {
                                              name: "set inverseMaxPower; Max inverter output Power Limit",
                                              unit: "W",
                                              role: "level",
                                              type: "number",
                                              write: true,
                                      		def: maxOutputLimit,
                                              min: 200,
                                              max: maxOutputLimit,
                                      		step: 100,
                                              apiKey: "inverseMaxPower",
                                          },
                                      	minSoc: {
                                              name: "set minSoc; minimum SOC 0-50",
                                              unit: "%",
                                              role: "level",
                                              type: "number",
                                              write: true,
                                      		def: 10,
                                              min: 0,
                                              max: 50,
                                              apiKey: "minSoc",
                                              transformWrite: (val) => val * 10
                                          },
                                      	outputLimit: {
                                              name: "set outputLimit",
                                              unit: "W",
                                              role: "level",
                                              type: "number",
                                              write: true,
                                      		def: 0,
                                              min: 0,
                                              max: maxOutputLimit,
                                              apiKey: "outputLimit",
                                          },
                                      	smartMode: {
                                              name: "set smartMode; parameter are written to: 1: RAM / 0: Flash",
                                              role: "level",
                                              type: "number",
                                              write: true,
                                      		def: 0,
                                              min: 0,
                                              max: 1,
                                              apiKey: "smartMode",
                                          },
                                      	socSet: {
                                              name: "set socSet; SOC Target 70%-100%",
                                              unit: "%",
                                              role: "level",
                                              type: "number",
                                              write: true,
                                      		def: 100,
                                              min: 70,
                                              max: 100,
                                              apiKey: "socSet",
                                              transformWrite: (val) => val * 10
                                          },
                                      	lampSwitch: {
                                              name: "set lamp switch; 0: lamp off / 1: lamp on",
                                              role: "level",
                                              type: "number",
                                              write: true,
                                      		def: 1,
                                              min: 0,
                                              max: 1,
                                              apiKey: "lampSwitch",
                                          }
                                      };
                                       
                                      createControlStates();
                                       
                                      function createControlStates() {
                                          const base = folderZendureApi + ".control";
                                          Object.keys(schemaControl).forEach(key => {
                                              const def = schemaControl[key];
                                              const dp = `${base}.${key}`;
                                              if (!existsState(dp)) {
                                                  createState(dp, def.type === "string" ? def.def : 0, {
                                                      name: def.name, type: def.type, role: def.role, unit: def.unit,
                                                      read: true, write: true, def: def.def, min: def.min, max: def.max
                                                  });
                                              }
                                          });
                                      	
                                      	const extraStates = [
                                      		{ id: `${base}.auto_inputLimitMode`, name: "Set InputLimit (Automatic)", unit: "W", min: 0, max: maxInputLimit },
                                      		{ id: `${base}.auto_outputLimitMode`, name: "Set OutputLimit (Automatic)", unit: "W", min: 0, max: maxOutputLimit },
                                      		{ id: `${base}.auto_in_out_Limit`, name: "Set In/Out-Limit-Automatic: Negative: Charging; Positive: Discharging", unit: "W", min: (maxInputLimit * -1), max: maxOutputLimit }
                                      	];
                                      	extraStates.forEach(def => {
                                      		if (!existsState(def.id)) {
                                      			createState(def.id, 0, {
                                      				name: def.name, type: "number", role: "level", unit: def.unit,
                                      				read: true, write: true, min: def.min, max: def.max
                                      			});
                                      		}
                                      	});
                                      }
                                       
                                      on({ id: new RegExp(`^${folderZendureApi}\\.control\\.`), change: "ne" }, obj => {
                                          if (obj.state.ack) return;
                                          const key = obj.id.split('.').pop();
                                          if (key === "auto_inputLimitMode") {
                                              let val = Math.round(Number(obj.state.val)) || 0;
                                              if (val < 0) val = 0;
                                              if (val > maxInputLimit) val = maxInputLimit;
                                              handleInputLimitMode(val);
                                              setState(obj.id, val, true);
                                              return;
                                          }
                                          if (key === "auto_outputLimitMode") {
                                              let val = Math.round(Number(obj.state.val)) || 0;
                                              if (val < 0) val = 0;
                                              if (val > maxOutputLimit) val = maxOutputLimit;
                                              handleOutputLimitMode(val);
                                              setState(obj.id, val, true);
                                              return;
                                          }
                                      	if (key === "auto_in_out_Limit") {
                                      		let val = Math.round(Number(obj.state.val)) || 0;
                                      		if (val > maxOutputLimit) val = maxOutputLimit;
                                      		if ((-val) > maxInputLimit) val = -maxInputLimit;
                                      		handleAuto_in_out_Limit(val);
                                      		setState(obj.id, val, true);
                                      		return;
                                      	}
                                          const def = schemaControl[key];
                                          if (!def) return;
                                          let val = obj.state.val;
                                          if (def.type === "number") {
                                              val = Math.round(Number(val)) || 0;
                                          }
                                          if (def.type === "string" && def.pattern) {
                                              val = String(val).trim();
                                              if (!def.pattern.test(val)) {
                                                  log(`${key}: Fehlerhafte Eingabe "${val}"; Standard ${def.def} wird verwendet.`, "info");
                                                  val = def.def;
                                              }
                                          }
                                          if (def.min !== undefined && val < def.min) val = def.min;
                                          if (def.max !== undefined && val > def.max) val = def.max;
                                      	let sendVal = val;
                                      	if (def.transformWrite) { sendVal = def.transformWrite(val); }
                                          if (key === "batCalTime" && getState(folderZendureApi + ".properties.batCalTime")?.val === sendVal) {
                                              setState(obj.id, val, true); // Gerät hat den Wert schon -> nichts senden
                                              return;
                                          }
                                          sendToDevice(def.apiKey, sendVal);
                                          setState(obj.id, val, true);
                                      });
                                       
                                      function sendToDevice(apiKey, value) {
                                      	if (!SN || SN === "null" || SN === "") return;
                                          const payload = { sn: SN, properties: {} };
                                          payload.properties[apiKey] = value;
                                          setTx(JSON.stringify(payload));
                                          queuePost({ [apiKey]: value });
                                      }
                                       
                                      function queuePost(obj) {
                                          Object.assign(postQueue, obj);
                                          if (batchTimer) return;
                                          batchTimer = setTimeout(() => {
                                              batchTimer = null;
                                              executePost();
                                          }, batchWindowMs);
                                      }
                                       
                                      function executePost() {
                                          if (postActive) return;
                                          if (Object.keys(postQueue).length === 0) return;
                                          postActive = true;
                                          stopGetLoop();  
                                          const payload = { sn: SN, properties: { ...postQueue } };
                                          postQueue = {};
                                          setTx(JSON.stringify(payload));
                                          axios
                                          .post(`http://${IP}/properties/write`, payload, { 
                                              timeout: postTimeoutMs, 
                                              responseType: "json" 
                                          })
                                          .then((response) => {
                                              const data = response.data;
                                              if (data && data.success === true && data.code === 200) {
                                                  retryCount = 0;
                                                  finishPost(true);
                                              } else {
                                                  throw new Error("Zendure success/code invalid"); 
                                              }
                                          })
                                          .catch((err) => {
                                              retryCount++;
                                              if (retryCount <= maxRetries) {
                                                  log(`POST Retry ${retryCount}/${maxRetries} (${err.message || 'Zendure Error'})`, "warn");
                                                  setTimeout(() => {
                                                      postQueue = { ...payload.properties };
                                                      postActive = false;
                                                      executePost();
                                                  }, postCooldownMs);
                                                  return;
                                              }
                                              log("POST endgültig fehlgeschlagen", "error");
                                              retryCount = 0;
                                              finishPost(false);
                                          });
                                      }
                                       
                                      function finishPost(success) {
                                          if (!success) { syncControlFromProperties(); }
                                      	setTimeout(() => { postActive = false; startGetLoop(); }, postCooldownMs);
                                      	}
                                       
                                      function syncAutoInOutLimit(diffProps) {
                                          const prop = folderZendureApi + ".properties.";
                                          const pick = (k) => (diffProps && diffProps[k] !== undefined) ? diffProps[k] : getState(prop + k)?.val;
                                          const acMode = pick("acMode");
                                          const inp = pick("inputLimit");
                                          const out = pick("outputLimit");
                                          let val;
                                          if (acMode === 2 && out !== undefined) val = out;
                                          else if (acMode === 1 && inp !== undefined) val = -inp;
                                          else if (acMode === 0) val = 0;
                                          else return;
                                          val = val || 0; // -0 vermeiden
                                          const id = folderZendureApi + ".control.auto_in_out_Limit";
                                          if (getState(id)?.val !== val) setState(id, val, true);
                                      }
                                       
                                      function syncControlFromProperties() {
                                          const baseCtrl = folderZendureApi + ".control";
                                          const baseProp = folderZendureApi + ".properties";
                                          Object.keys(schemaControl).forEach(ctrlKey => {
                                              const def = schemaControl[ctrlKey];
                                              const apiKey = def.apiKey || ctrlKey;
                                              const propState = getState(`${baseProp}.${apiKey}`);
                                              if (!propState || propState.val === undefined) return;
                                              const ctrlId = `${baseCtrl}.${ctrlKey}`;
                                      		const ctrlState = getState(ctrlId);
                                      		const ctrlVal = ctrlState ? ctrlState.val : undefined;
                                              const propVal = def.transformRead ? def.transformRead(propState.val) : propState.val;
                                              if (ctrlVal !== propVal) { setState(ctrlId, propVal, true); }
                                          });
                                          const inputProp = getState(`${baseProp}.inputLimit`)?.val;
                                          if (inputProp !== undefined) {
                                              const id = `${baseCtrl}.auto_inputLimitMode`;
                                              if (getState(id)?.val !== inputProp) {
                                                  setState(id, inputProp, true);
                                              }
                                          }
                                          const outputProp = getState(`${baseProp}.outputLimit`)?.val;
                                          if (outputProp !== undefined) {
                                              const id = `${baseCtrl}.auto_outputLimitMode`;
                                              if (getState(id)?.val !== outputProp) {
                                                  setState(id, outputProp, true);
                                              }
                                          }
                                      	syncAutoInOutLimit(null);
                                      }
                                       
                                      function startGetLoop() {
                                          if (getTimer) { clearInterval(getTimer); }
                                          getTimer = setInterval(() => {
                                              if (postActive) return;
                                              const url = `http://${IP}/properties/report`;
                                              axios
                                              .get(url, { timeout: getTimeoutMs, responseType: 'text' })
                                              .then(function (response) {
                                                  if (getErrorCount > 0) log("Verbindung wieder OK", "info");
                                                  getErrorCount = 0;
                                                  if (!response.data) return;
                                                  setRxNew(response.data);
                                                  handleRxNewUpdate(response.data);
                                              })
                                              .catch(function (err) {
                                                  const errorMsg = err.response ? err.response.status : err.message;
                                                  getErrorCount++;
                                                  if (getErrorCount <= 3) log(`GET Fehler (${getErrorCount}): ${errorMsg}`, "info");
                                                  if (getErrorCount === 4) log("Keine Verbindung möglich. Zendure-Geräte IP prüfen!", "error");
                                              });
                                          }, getIntervalMs);
                                      }
                                       
                                      function stopGetLoop() { if (getTimer) { clearInterval(getTimer); getTimer = null; } }
                                       
                                      function handleInputLimitMode(val) {
                                          const currentOutput = Number(getState(folderZendureApi + ".properties.outputLimit")?.val);
                                          const currentSmartMode = getState(folderZendureApi + ".properties.smartMode")?.val;
                                          const smartRAMActive = getState(dpSmartRAM)?.val;
                                          const payload = {};
                                      
                                          payload.acMode = 1;
                                          if (currentOutput !== 0) payload.outputLimit = 0;
                                          if (smartRAMActive === true && currentSmartMode !== 1) payload.smartMode = 1;
                                          payload.inputLimit = val;
                                          sendBatch(payload);
                                      }
                                       
                                      function handleOutputLimitMode(val) {
                                          const currentInput = Number(getState(folderZendureApi + ".properties.inputLimit")?.val);
                                          const currentSmartMode = getState(folderZendureApi + ".properties.smartMode")?.val;
                                          const smartRAMActive = getState(dpSmartRAM)?.val;
                                          const payload = {};
                                       
                                          payload.acMode = 2;
                                          if (currentInput !== 0) payload.inputLimit = 0;
                                          if (smartRAMActive === true && currentSmartMode !== 1) payload.smartMode = 1;
                                          payload.outputLimit = val;
                                          sendBatch(payload);
                                      }
                                       
                                      function handleAuto_in_out_Limit(val) {
                                          const currentInput = Number(getState(folderZendureApi + ".properties.inputLimit")?.val);
                                          const currentOutput = Number(getState(folderZendureApi + ".properties.outputLimit")?.val);
                                          const currentSmartMode = getState(folderZendureApi + ".properties.smartMode")?.val;
                                          const smartRAMActive = getState(dpSmartRAM)?.val;
                                          const payload = {};
                                       
                                          if (val === 0) {
                                              if (currentInput !== 0) payload.inputLimit = 0;
                                              if (currentOutput !== 0) payload.outputLimit = 0;
                                          } else if (val > 0) {
                                              payload.acMode = 2;
                                              if (currentInput !== 0) payload.inputLimit = 0;
                                              payload.outputLimit = val;
                                          } else if (val < 0) {
                                              payload.acMode = 1;
                                              if (currentOutput !== 0) payload.outputLimit = 0;
                                              payload.inputLimit = Math.abs(val);
                                          }
                                          if (smartRAMActive === true && currentSmartMode !== 1) payload.smartMode = 1;
                                          if (Object.keys(payload).length > 0) sendBatch(payload);
                                      }
                                       
                                      function sendBatch(properties) {
                                          if (!SN || SN === "null" || SN === "") return;
                                          const payload = { sn: SN, properties: properties };
                                          setTx(JSON.stringify(payload)); 
                                      	queuePost(properties); 
                                      }
                                       
                                      const schema = {
                                          properties: {
                                              BatVolt: {
                                                  name: "BatVolt - Average Battery Voltage of all Batteries",
                                                  unit: "V",
                                                  role: "value.voltage",
                                                  type: "number"
                                              },
                                      		Fanmode: {
                                                  name: "Fanmode - 0: Fan off, 1: Fan on",
                                                  role: "value",
                                                  type: "number"
                                              },	
                                      		Fanspeed: {
                                                  name: "Fanspeed - 0: Auto, 1: 1st gear, 2: 2nd gear",
                                                  role: "value",
                                                  type: "number"
                                              },
                                      		acMode: {
                                                  name: "acMode - 1: input charge / 2: output discharge",
                                                  role: "value",
                                                  type: "number"
                                              },
                                      		acStatus: {
                                                  name: "AC state; 0: idle, 1: AC Flow out, 2: AC Flow in",
                                                  role: "value",
                                                  type: "number"
                                              },
                                      		batCalTime: {
                                                  name: "batCalTime - Battery Calibration Start-Time",
                                      			unit: "min",
                                                  role: "value",
                                                  type: "number"
                                              },
                                      		chargeMaxLimit: {
                                                  name: "chargeMaxLimit - Max charge power",
                                      			unit: "W",
                                                  role: "value.power",
                                                  type: "number"
                                              },
                                      		dataReady: {
                                                  name: "Data ready flag - 0: Data not ready, 1: Data ready",
                                                  role: "value",
                                                  type: "number"
                                              },
                                      		dcStatus: {
                                                  name: "DC State; 0: idle, 1: DC Flow out, 2: DC Flow in",
                                                  role: "value",
                                                  type: "number"
                                              },
                                      		electricLevel: {
                                                  name: "electricLevel - Average SOC of all Batterys",
                                                  unit: "%",
                                                  role: "value.battery",
                                                  type: "number"
                                              },
                                      		gridInputPower: {
                                                  name: "gridInputPower - Grid Input Power to Battery",
                                      			unit: "W",
                                                  role: "value.power",
                                                  type: "number"
                                              },
                                      		gridOffMode: {
                                                  name: "gridOffMode - 0: Normal mode, 1: Economic mode, 2: OFF",
                                                  role: "value",
                                                  type: "number"
                                              },
                                      		gridOffPower: {
                                                  name: "gridOffPower - Off-grid power",
                                      			unit: "W",
                                                  role: "value.power",
                                                  type: "number"
                                              },
                                      		gridReverse: {
                                                  name: "gridReverse - 0: Disabled, 1: Allowed reverse flow, 2: Forbidden reverse flow",
                                                  role: "value",
                                                  type: "number"
                                              },
                                      		gridStandard: {
                                                  name: "gridStandard - 0: Germany 1: France 2: Austria 3: Switzerland 4: Netherlands 5: Spain 6: Belgium 7: Greece 8: Denmark 9: Italy",
                                                  role: "value",
                                                  type: "number"
                                              },
                                      		gridState: {
                                                  name: "gridState - Grid connection state, 0: Not connected, 1: Connected",
                                                  role: "value",
                                                  type: "number"
                                              },
                                      		heatState: {
                                                  name: "heatState - Battery Heat State, 0: Not heating, 1: heating",
                                                  role: "value",
                                                  type: "number"
                                              },
                                      		hyperTmp: {
                                                  name: "Temperature (Zendure-Device)",
                                                  unit: "°C",
                                                  role: "value.temperature",
                                                  type: "number"
                                              },
                                      		IOTState: {
                                                  name: "Network 0: disconnected, 1: is being configured, 2: Connection successful",
                                                  role: "value",
                                                  type: "number"
                                              },
                                      		inputLimit: {
                                                  name: "inputLimit - AC charging power limit to Battery",
                                      			unit: "W",
                                                  role: "value.power",
                                                  type: "number"
                                              },
                                      		inverseMaxPower: {
                                                  name: "inverseMaxPower - Max inverter output Power Limit",
                                      			unit: "W",
                                                  role: "value.power",
                                                  type: "number"
                                              },
                                      		lampSwitch: {
                                                  name: "lampSwitch - Lamp state, 0: lamp off / 1: lamp on",
                                                  role: "value",
                                                  type: "number"
                                              },
                                      		minSoc: {
                                                  name: "minSoc - Minimum SOC 0%-50%",
                                                  unit: "%",
                                                  role: "value",
                                                  type: "number"
                                              },
                                      		outputHomePower: {
                                                  name: "outputHomePower - Output to home",
                                      			unit: "W",
                                                  role: "value.power",
                                                  type: "number"
                                              },
                                      		outputLimit: {
                                                  name: "outputLimit - Output power limit",
                                      			unit: "W",
                                                  role: "value.power",
                                                  type: "number"
                                              },
                                      		outputPackPower: {
                                                  name: "outputPackPower - Battery charge power",
                                      			unit: "W",
                                                  role: "value.power",
                                                  type: "number"
                                              },
                                      		packInputPower: {
                                                  name: "packInputPower - Battery discharge power",
                                      			unit: "W",
                                                  role: "value.power",
                                                  type: "number"
                                              },
                                      		packNum: {
                                                  name: "packNum - Number of batteries",
                                                  role: "value",
                                                  type: "number"
                                              },	
                                      		packState: {
                                                  name: "packState - Battery State, 0: Standby, 1: Charging, 2: Discharging",
                                                  role: "value",
                                                  type: "number"
                                              },
                                      		pass: {
                                                  name: "pass - 0: Bypass Automatic, 1: Bypass OFF, 2: Bypass ON, 3: unknown",
                                                  role: "value",
                                                  type: "number"
                                              },
                                      		pvStatus: {
                                                  name: "pvStatus - PV State producing, 0: Stopped, 1: Running",
                                                  role: "value",
                                                  type: "number"
                                              },
                                      		remainOutTime: {
                                                  name: "remainOutTime - Estimated discharge time in minutes, if not predictable: 59940",
                                      			unit: "min",
                                                  role: "value",
                                                  type: "number"
                                              },
                                      		reverseState: {
                                                  name: "reverseState - Reverse flow, 0: No, 1: Reverse flow",
                                                  role: "value",
                                                  type: "number"
                                              },
                                      		rssi: {
                                                  name: "rssi - Received Signal Strength Indicator",
                                      			unit: "dBm",
                                                  role: "value",
                                                  type: "number"
                                              },				  
                                      		smartMode: {
                                                  name: "smartMode - 1: writes to RAM / 0: writes to Flash",
                                                  role: "value",
                                                  type: "number"
                                              },
                                      		socCompSwitch: { //api:3
                                                  name: "socCompSwitch; Unauthorized modifications are not recommended",
                                                  role: "value",
                                                  type: "number"
                                              },					
                                      		socLimit: {
                                                  name: "socLimit - 0: normal, 1: Charge limit reached, 2: Discharge limit reached, 3: unknown",
                                                  role: "value",
                                                  type: "number"
                                              },		
                                              socSet: {
                                                  name: "socSet - SOC Target 70%-100%",
                                                  unit: "%",
                                                  role: "value",
                                                  type: "number"
                                              },
                                      		socStatus: {
                                                  name: "socStatus - SOC calibration Info, 0: No Calibrating / 1: Calibrating",
                                                  role: "value",
                                                  type: "number"
                                              },
                                      		solarInputPower: {
                                                  name: "solarInputPower - Total Solar Input Power",
                                      			unit: "W",
                                                  role: "value.power",
                                                  type: "number"
                                              },
                                      		solarPower1: {
                                                  name: "solarPower1 - Solar line 1 input power",
                                      			unit: "W",
                                                  role: "value.power",
                                                  type: "number"
                                              },
                                      		solarPower2: {
                                                  name: "solarPower2 - Solar line 2 input power",
                                      			unit: "W",
                                                  role: "value.power",
                                                  type: "number"
                                              },
                                      		solarPower3: {
                                                  name: "solarPower3 - Solar line 3 input power",
                                      			unit: "W",
                                                  role: "value.power",
                                                  type: "number"
                                              },
                                      		solarPower4: {
                                                  name: "solarPower4 - Solar line 4 input power",
                                      			unit: "W",
                                                  role: "value.power",
                                                  type: "number"
                                              },
                                      		solarPower5: {
                                                  name: "solarPower5 - Solar line 5 input power",
                                      			unit: "W",
                                                  role: "value.power",
                                                  type: "number"
                                              },
                                      		solarPower6: {
                                                  name: "solarPower6 - Solar line 6 input power",
                                      			unit: "W",
                                                  role: "value.power",
                                                  type: "number"
                                              }
                                          },
                                          packData: {
                                              batcur: {
                                                  name: "Battery Current flow - negativ: discharge / positiv: charge",
                                                  unit: "A",
                                                  role: "value.current",
                                                  type: "number"
                                              },
                                      		maxTemp: {
                                                  name: "Battery - max. Stored temperature value",
                                                  unit: "°C",
                                                  role: "value.temperature",
                                                  type: "number"
                                              },
                                      		maxVol: {
                                                  name: "maxVol - Max cell voltage",
                                                  unit: "V",
                                                  role: "value.voltage",
                                                  type: "number"
                                              },
                                      		minVol: {
                                                  name: "minVol - Min cell voltage",
                                                  unit: "V",
                                                  role: "value.voltage",
                                                  type: "number"
                                              },
                                      		packType: {
                                                  name: "Pack Type",
                                                  role: "indicator",
                                                  type: "number"
                                              },
                                      		power: {
                                                  name: "Battery pack power",
                                                  unit: "W",
                                                  role: "value.power",
                                                  type: "number"
                                              },
                                      		sn: {
                                                  name: "Battery pack serial number",
                                                  role: "indicator",
                                                  type: "string"
                                              },
                                      		socLevel: {
                                                  name: "socLevel - State of Charge in Percent",
                                      			unit: "%",
                                                  role: "value.battery",
                                                  type: "number"
                                              },
                                      		state: {
                                      			name: "Battery state, 0: Standby, 1: Charging, 2: Discharging",
                                                  role: "indicator",
                                                  type: "number"
                                              },
                                      		totalVol: {
                                      			name: "Battery Total Voltage",
                                                  unit: "V",
                                                  role: "value.voltage",
                                                  type: "number"
                                              }
                                          }
                                      };
                                       
                                      function formatTime(unix) {
                                          if (unix === undefined || unix === null) return "";
                                          const d = new Date(unix * 1000);
                                          const timeOptions = { timeZone: "Europe/Berlin", hour: "2-digit", minute: "2-digit", second: "2-digit", hour12: false };
                                          const dateOptions = { timeZone: "Europe/Berlin", day: "2-digit", month: "2-digit", year: "2-digit" };
                                          return `${d.toLocaleTimeString("de-DE", timeOptions)}, ${d.toLocaleDateString("de-DE", dateOptions)}`;
                                      }
                                       
                                      function getBatteryType(sn, model) {
                                          let batType = "";
                                          if (!sn || typeof sn !== "string") return "unknown";
                                          if (sn.startsWith("A")) { batType = "AB1000"; } 
                                          else if (sn.startsWith("B")) { batType = "AB1000S"; } 
                                          else if (sn.startsWith("C")) {
                                              const sub = sn.length > 3 ? sn[3] : "";
                                              if (sub === "F") batType = "AB2000S";
                                              else if (sub === "E") batType = "AB2000X";
                                              else if (sub === "A") batType = "AB2000L";
                                              else batType = "AB2000";
                                          } 
                                          else if (sn.startsWith("F")) { batType = "AB3000X"; }
                                          else if (sn.startsWith("J")) {
                                              const sub = sn.length > 3 ? sn[3] : "";
                                              if (sub === "A") batType = "Internal 2,4kWh";
                                              else batType = "unknown internal";
                                          }
                                          else if (sn.startsWith("G")) {
                                              const sub = sn.length > 3 ? sn[3] : "";
                                              if (sub === "A") batType = "AB3000L";
                                              else batType = "unknown AB3000?";
                                          }
                                          if (model && typeof model === "string" && model.trim()) { batType = model.trim(); }
                                          return batType || "unknown";
                                      }
                                       
                                      function writeMainKeys(json) {
                                          const keys = ["timestamp", "messageId", "sn", "version", "product"];
                                          keys.forEach(key => {
                                              if (json[key] === undefined) return;
                                      		if (key === "sn" && SN === '') { SN = json[key]; }
                                              const dp = `${folderZendureApi}.${key}`;
                                      		const dpName = (key === "version") ? "zenSDK API Version" : key;
                                              if (existsState(dp)) {
                                                  if (getState(dp).val !== json[key]) { setState(dp, json[key], true); }
                                              } else {
                                                  createState(dp, json[key], { name: dpName, type: typeof json[key], role: "info", read: true, write: false });
                                              }
                                          });
                                      }
                                       
                                      function writeProperties(obj) {
                                          if (!obj) return;
                                          const base = folderZendureApi + ".properties";
                                          Object.keys(obj).forEach(key => {
                                              const dp = `${base}.${key}`;
                                              const val = obj[key];
                                              const meta = schema.properties?.[key] || {};
                                              if (existsState(dp)) {
                                                  if (getState(dp).val !== val) { setState(dp, val, true); }
                                              } else {
                                                  createState(dp, val, { name: meta.name || key, type: meta.type || typeof val, role: meta.role || "value", unit: meta.unit, read: true, write: false });
                                              }
                                          });
                                      }
                                       
                                      function writePackData(arr) {
                                          if (!Array.isArray(arr)) return;
                                          const base = folderZendureApi + ".packData";
                                          arr.forEach(pack => {
                                              if (!pack.sn) return;
                                              const folder = `${base}.${pack.sn}`;
                                              if (!existsObject(folder)) { setObject(folder, { type: "folder", common: { name: pack.sn }, native: {} }); }
                                              Object.keys(pack).forEach(key => {
                                                  const dp = `${folder}.${key}`;
                                                  const val = pack[key];
                                      			const meta = schema.packData?.[key] || {};
                                                  if (existsState(dp)) {
                                                      if (getState(dp).val !== val) { setState(dp, val, true); }
                                                  } else {
                                                      createState(dp, val, { name: meta.name || key, type: meta.type || typeof val, role: meta.role || "value", unit: meta.unit, read: true, write: false });
                                                  }
                                              });
                                          });
                                      }
                                       
                                      function buildDiff(oldJson, newJson) {
                                          if (!oldJson || !newJson) return null;
                                          const diff = {};
                                          const mainKeys = ["timestamp", "messageId", "sn", "version", "product"];
                                      	mainKeys.forEach(key => {
                                              if (newJson[key] !== undefined && newJson[key] !== oldJson[key]) { diff[key] = newJson[key]; }
                                          });
                                          if (newJson.properties) {
                                              const propDiff = {};
                                              Object.keys(newJson.properties).forEach(key => {
                                                  const newVal = newJson.properties[key];
                                                  const oldVal = oldJson.properties ? oldJson.properties[key] : undefined;
                                                  if (newVal !== oldVal) { propDiff[key] = newVal; }
                                              });
                                              if (Object.keys(propDiff).length > 0) { diff.properties = propDiff; }
                                          }
                                          if (Array.isArray(newJson.packData)) {
                                              const packDiff = [];
                                              const oldMap = {};
                                              if (Array.isArray(oldJson.packData)) {
                                                  oldJson.packData.forEach(p => { if (p.sn) oldMap[p.sn] = p; }); }
                                              newJson.packData.forEach(newPack => {
                                                  if (!newPack.sn) return;
                                                  const oldPack = oldMap[newPack.sn];
                                                  const singleDiff = { sn: newPack.sn };
                                                  let changed = false;
                                                  Object.keys(newPack).forEach(key => {
                                                      if (key === "sn") return;
                                                      const newVal = newPack[key];
                                                      const oldVal = oldPack ? oldPack[key] : undefined;
                                                      if (newVal !== oldVal) { singleDiff[key] = newVal; changed = true; }
                                                  });
                                                  if (changed) { packDiff.push(singleDiff); }
                                              });
                                              if (packDiff.length > 0) { diff.packData = packDiff; }
                                          }
                                          return Object.keys(diff).length === 0 ? null : diff;
                                      }
                                       
                                      function handleRxNewUpdate(val) {
                                          if (!val) return;
                                          let newJson;
                                          try { newJson = JSON.parse(val); } catch (e) { log("rxNew parse error: " + e, "warn"); return; }
                                          const smartModeWatcher = getState(dpSmartWatcher)?.val;
                                          if (smartModeWatcher === true && newJson.properties && newJson.properties.smartMode === 0) {
                                              //log("SmartModeWatcher: smartMode ist 0! Erzwinge RAM-Modus (1)...", "info");
                                              queuePost({ smartMode: 1 });
                                          }
                                          const oldStr = getRxOld(); 
                                          if (!oldStr || oldStr === "null" || oldStr === "") {
                                      		let full = transformJson(JSON.parse(JSON.stringify(newJson)));
                                      		writeMainKeys(full);
                                      		if (full.properties) {
                                      			writeProperties(full.properties);
                                      			const base = folderZendureApi + ".control";
                                                  Object.keys(schemaControl).forEach(ctrlKey => {
                                                      const def = schemaControl[ctrlKey];
                                                      const apiKey = def.apiKey || ctrlKey;
                                                      if (full.properties[apiKey] !== undefined) { setState(`${base}.${ctrlKey}`, def.transformRead ? def.transformRead(full.properties[apiKey]) : full.properties[apiKey], true); }
                                                  });
                                                  if (full.properties.inputLimit !== undefined) { setState(`${base}.auto_inputLimitMode`, full.properties.inputLimit, true); }
                                                  if (full.properties.outputLimit !== undefined) { setState(`${base}.auto_outputLimitMode`, full.properties.outputLimit, true); }
                                      			syncAutoInOutLimit(full.properties);
                                      		} 
                                      		if (full.packData) { writePackData(full.packData); }
                                      		setRxOld(val); setRxDiff(""); return;
                                      	}
                                          let oldJson;
                                          try { oldJson = JSON.parse(oldStr); } catch (e) { setRxOld(val); return; }
                                      	const isNewer = (newJson.timestamp && oldJson.timestamp && newJson.timestamp > oldJson.timestamp) || (!oldJson.timestamp && newJson.timestamp);
                                          if (!isNewer) return;
                                          const diff = buildDiff(oldJson, newJson);
                                          if (diff && Object.keys(diff).length > 0) {
                                              const diffStr = JSON.stringify(diff);
                                              setRxDiff(diffStr); handleRxDiffUpdate(diffStr); 
                                          } else { setRxDiff(""); }
                                          setRxOld(val);
                                      }
                                       
                                      function handleRxDiffUpdate(val) {
                                          if (!val) return;
                                          let diff;
                                          try { diff = JSON.parse(val); } catch (e) { log("rxDiff parse error: " + e, "warn"); return; }
                                          diff = transformJson(diff);
                                      	if (diff.properties) { syncControlFromDiff(diff); }							   
                                          writeMainKeys(diff);
                                          if (diff.properties) { writeProperties(diff.properties); }
                                          if (diff.packData) { writePackData(diff.packData); }
                                      }
                                       
                                      function syncControlFromDiff(diff) {
                                      	if (!diff || !diff.properties) return;
                                          const base = folderZendureApi + ".control";
                                          if (diff.properties.acMode !== undefined || diff.properties.inputLimit !== undefined || diff.properties.outputLimit !== undefined) { syncAutoInOutLimit(diff.properties); }
                                      	if (diff.properties.inputLimit !== undefined) { setState(`${base}.auto_inputLimitMode`, diff.properties.inputLimit, true); }
                                      	if (diff.properties.outputLimit !== undefined) { setState(`${base}.auto_outputLimitMode`, diff.properties.outputLimit, true); }
                                      	Object.keys(schemaControl).forEach(ctrlKey => {
                                      		const def = schemaControl[ctrlKey];
                                      		const apiKey = def.apiKey || ctrlKey;
                                      		if (diff.properties[apiKey] !== undefined) { setState(`${base}.${ctrlKey}`, def.transformRead ? def.transformRead(diff.properties[apiKey]) : diff.properties[apiKey], true); }
                                      	});
                                      }
                                       
                                      function createOrSet(id, val, common) {
                                          if (existsState(id)) {
                                              if (getState(id).val !== val) { setState(id, val, true); }
                                          } else { createState(id, val, { read: true, write: false, ...common }); }
                                      }
                                       
                                      function transformJson(obj) {
                                          if (!obj) return obj;
                                          if (obj.timestamp !== undefined && obj.timestamp !== null) {
                                              const formatted = formatTime(obj.timestamp);
                                              createOrSet(`${folderZendureApi}.timeUpdateTimestamp`, formatted, { type: "string", role: "text", name: "TimeUpdate (timestamp)" });
                                          }
                                          if (obj.properties) {
                                              Object.keys(obj.properties).forEach(key => {
                                                  let val = obj.properties[key];
                                                  switch (key) {
                                      				case "BatVolt": val = val / 100; break;
                                      				case "batCalTime":
                                      					createOrSet(`${folderZendureApi}.properties.batCalTime_h`, minutesToHhmm(val), { type: "string", role: "text", name: "batCalTime_h - Battery Calibration Time (HH:MM)", unit: "h" });
                                      					break;
                                      				case "acCouplingState": {
                                      					let states = [];
                                      					if (val & (1 << 0)) states.push("AC-coupled input present");
                                      					if (val & (1 << 1)) states.push("AC input present flag");
                                      					if (val & (1 << 2)) states.push("AC-coupled overload");
                                      					if (val & (1 << 3)) states.push("Excess AC input power");
                                      					const statusText = states.length > 0 ? states.join(", ") : "Normal / No Flags";
                                      					createOrSet(`${folderZendureApi}.properties.acCouplingState_String`, statusText, { type: "string", role: "text", name: "AC Coupling State (Text)" });
                                      				} break;
                                      				case "hyperTmp": val = (val - 2731) / 10.0; break;
                                                      case "minSoc":
                                      				case "socSet": val = val / 10; break;
                                      				case "socLimit": {
                                      					if (val >= 16) {
                                      					val = val & 0x03;
                                      					}
                                      				} break;
                                                      case "ts":
                                      					if (val !== undefined && val !== null) {
                                      						const formatted = formatTime(val);
                                      						createOrSet(`${folderZendureApi}.properties.timeUpdateTs`, formatted, { type: "string", role: "text", name: "TimeUpdate (ts)" });
                                      					} break;
                                                  }
                                                  obj.properties[key] = val;
                                              });
                                          }
                                          if (Array.isArray(obj.packData)) {
                                      		obj.packData.forEach(pack => {
                                      			if (pack.sn) {
                                      				const batType = getBatteryType(pack.sn, pack.model);
                                      				createOrSet(`${folderZendureApi}.packData.${pack.sn}.model`, batType, { type: "string", role: "text", name: "Battery Model" });
                                      			}
                                      		}); 	
                                              obj.packData.forEach(pack => {
                                                  Object.keys(pack).forEach(key => {
                                                      let val = pack[key];
                                                      switch (key) {
                                      					case "batcur":
                                      						if (val > 32767) { val = val - 65536; }
                                      						val = val / 10; break;
                                                          case "maxTemp": val = (val - 2731) / 10; break;
                                      					case "maxVol":
                                      					case "minVol":
                                      					case "totalVol": val = val / 100; break;
                                      					case "softVersion":
                                                              const major = Math.floor(val / 4096);
                                                              const minor = Math.floor((val % 4096) / 256);
                                                              const patch = val % 256;
                                                              val = `V${major}.${minor}.${patch}`; break;  
                                                      }
                                                      pack[key] = val;
                                                  });
                                              });
                                          }
                                          return obj;
                                      }
                                       
                                      startGetLoop();
                                      
                                      
                                      P
                                      P
                                      petman
                                      schrieb am zuletzt editiert von
                                      #599

                                      @maxclaudi sagte:

                                      // GET intervall
                                      // Empfehlung: >= 5000 ms.
                                      // Kleinere Werte möglich, aber m. M. nach sinnfrei und nicht empfohlen (Last / Stabilität).
                                      const getIntervalMs = 4000; // Intervall für GET 5000ms. Bei Problemen erhöhen. NICHT < 5000!

                                      Hallo maxclaudi,
                                      in deinem überarbeiteten Skript steht bei const getIntervalMs = 4000 und im Komentar dahinter, dass man nichrt unter 5000 gehen soll.

                                      Ich bewundere deinen Enthusiasmus bei der Analyse der Firmware.

                                      peter

                                      NUC mit Proxmox,VM's IOB, InfluxDB, Grafana, PiHole;
                                      Hitchi Lesekopf, jomjol AI-Device für Gas und Wasser;
                                      BKW, 4x485Wp, SF800Pro2, AB2000, AB2000L
                                      Insel: 2x430Wp, SF1200, HM800T

                                      maxclaudiM 1 Antwort Letzte Antwort
                                      1
                                      • maxclaudiM maxclaudi

                                        @T-147
                                        genau für solche Rückmeldungen ist die Vorlage da!
                                        Danke für das genaue Log.
                                        Du hast hier einen extremen Ausnahmefall aufgedeckt, den das Skript in dieser Basis-Version so noch nicht abfangen konnte.

                                        Was ist passiert?
                                        Deine Batterie ist in den hardwareseitigen Notlademodus gegangen und hat sich mit 1000W aus dem Netz gerettet.
                                        Das Skript hat diesen plötzlichen Netzbezug über das Gridmeter registriert und dachte wegen des criticalMode, im Haus sei ein riesiger Verbraucher angesprungen.
                                        Daraufhin hat es die Entladung wieder freigegeben und gegen die eigene Notladung des Hubs angekämpft.
                                        Ein klassischer Logik-Teufelskreis.

                                        Der criticalMode und der normale Regelkreis müssen sofort blockiert werden, wenn der Batterieschutz aktiv ist.
                                        Ich schaue mir das noch genauer an, damit der batteryLock immer die oberste Priorität hat und nicht mehr vom eigenen Laden ausgetrickst werden kann.

                                        Möchtest Du bitte dieses Blockly testen:

                                        updateBlockly-Test.xml

                                        T
                                        T
                                        T-147
                                        schrieb am zuletzt editiert von
                                        #600

                                        @maxclaudi sagte:
                                        Möchtest Du bitte dieses Blockly testen:

                                        updateBlockly-Test.xml

                                        Wird gemacht, kein Problem.

                                        Ich hatte zusätzlich noch die Vermutung, dass der Batterieschutz zwar aktiviert wird, aber die Regelung dann auf dem aktuellen Wert stehen bleibt (in meinem aktuellen Fall 240W).
                                        Ich habe deswegen dem Blockly unter Entladeschutz noch zwei kleine Teile hinzugefügt, die einmal den minSoc als zusätzlichen Parameter für den Entladeschutz hinzufügen und einmal nach Aktivieren des Ladeschutzes das In/Out-Limit auf 0 setzt, damit das Einspeisen beendet wird:
                                        grafik.jpeg
                                        Maybe ist das mit dem minSoc auch doppelt gemoppelt, aber er sollte den Entladeschutz aktivieren, je nachdem was eher eintritt.

                                        Ist noch nicht getestet, morgen kann ich da vielleicht schon mehr zu sagen.

                                        1 Antwort Letzte Antwort
                                        1
                                        • P petman

                                          @maxclaudi sagte:

                                          // GET intervall
                                          // Empfehlung: >= 5000 ms.
                                          // Kleinere Werte möglich, aber m. M. nach sinnfrei und nicht empfohlen (Last / Stabilität).
                                          const getIntervalMs = 4000; // Intervall für GET 5000ms. Bei Problemen erhöhen. NICHT < 5000!

                                          Hallo maxclaudi,
                                          in deinem überarbeiteten Skript steht bei const getIntervalMs = 4000 und im Komentar dahinter, dass man nichrt unter 5000 gehen soll.

                                          Ich bewundere deinen Enthusiasmus bei der Analyse der Firmware.

                                          peter

                                          maxclaudiM
                                          maxclaudiM
                                          maxclaudi
                                          schrieb am zuletzt editiert von maxclaudi
                                          #601

                                          @petman sagte:
                                          Hallo maxclaudi,

                                          Hallo Peter, bald kommt schon wieder das große Fest :-)

                                          in deinem überarbeiteten Skript steht bei const getIntervalMs = 4000 und im Komentar dahinter, dass man nichrt unter 5000 gehen soll.

                                          Du kennst das doch: Sicher ist sicher ;-)

                                          Bei mir läuft es mit 4000 (und selbst mit 3500) absolut stabil.
                                          Aber nicht jeder hat ein optimales WLAN oder einen eigenen Access Point direkt neben den Geräten. Zudem funken meine in einem komplett eigenen, isolierten Netz.
                                          Das muss man im Zweifel einfach selbst ausprobieren.
                                          Bei mir funktionieren die 3500 ms einwandfrei – wahrscheinlich auch, weil in dem isolierten Netz absolut kein anderer HTTP(S)-Traffic dazwischenfunken kann.

                                          Ich bewundere deinen Enthusiasmus bei der Analyse der Firmware.

                                          Ich sage dazu nur: Kühlschrank und Festplatte ;-)

                                          Ich liebe es einfach, wenn Kollegen einen Enthusiasmus und eine gesunde Portion Pragmatismus teilen und verstehen! :-)

                                          Viele Grüße


                                          edit /PS 09.10.2026 12:20h

                                          HINWEIS:
                                          Der originale Firmware-Code verifiziert, dass (z. B. bei MQTT im Standard) alle 5000 ms automatisch ein Report published wird.

                                          Wenn mittels GET via zenSDK noch schneller abgefragt wird, kann es hardwarebedingt vorkommen, dass man alte bzw. identische Werte empfängt. Es ist m. M. n. daher sinnfrei, schneller als alle 4000–5000 ms abzufragen.

                                          Zendure zenSDK Lokal API, SmartMode, SolarFlow AC 800 Pro 2

                                          neue Zendure Geräte Cloudless: zURLtool V2.0

                                          maxclaudiM 1 Antwort Letzte Antwort
                                          1

                                          Hey! Du scheinst an dieser Unterhaltung interessiert zu sein, hast aber noch kein Konto.

                                          Hast du es satt, bei jedem Besuch durch die gleichen Beiträge zu scrollen? Wenn du dich für ein Konto anmeldest, kommst du immer genau dorthin zurück, wo du zuvor warst, und kannst dich über neue Antworten benachrichtigen lassen (entweder per E-Mail oder Push-Benachrichtigung). Du kannst auch Lesezeichen speichern und Beiträge positiv bewerten, um anderen Community-Mitgliedern deine Wertschätzung zu zeigen.

                                          Mit deinem Input könnte dieser Beitrag noch besser werden 💗

                                          Registrieren Anmelden
                                          Antworten
                                          • In einem neuen Thema antworten
                                          Anmelden zum Antworten
                                          • Älteste zuerst
                                          • Neuste zuerst
                                          • Meiste Stimmen


                                          Support us

                                          ioBroker
                                          Community Adapters
                                          Donate

                                          454

                                          Online

                                          33.1k

                                          Benutzer

                                          84.2k

                                          Themen

                                          1.4m

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

                                          • Du hast noch kein Konto? Registrieren

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