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

  • Neuer Installer und neuer Fixer: übersichtliche Oberfläche und `iob docker`
    BluefoxB
    Bluefox
    7
    1
    652

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

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

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

Geplant Angeheftet Gesperrt Verschoben JavaScript
688 Beiträge 24 Kommentatoren 117.9k Aufrufe 22 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.
  • M MP_Trixi

    @maxclaudi
    Ok. Ich habe einige aliase gesetzt. Sind die danach wieder da oder reicht es wie in deinen Post oben geschrieben nur control.socCompSwitch zu löschen?

    maxclaudiM
    maxclaudiM
    maxclaudi
    schrieb am zuletzt editiert von
    #639

    @MP_Trixi
    Es reicht auch nur control.socCompSwitch zu löschen.

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

    neue Zendure Geräte Cloudless: zURLtool V2.0

    M 1 Antwort Letzte Antwort
    0
    • maxclaudiM maxclaudi

      @MP_Trixi
      Es reicht auch nur control.socCompSwitch zu löschen.

      M
      M
      MP_Trixi
      schrieb am zuletzt editiert von MP_Trixi
      #640

      @maxclaudi
      danke für deinen Input - habe ich gemacht (altes Script gestoppt, control.socComSwitch gelöscht, neues Script gestartet). Ich werde testen

      maxclaudiM 1 Antwort Letzte Antwort
      0
      • M MP_Trixi

        @maxclaudi
        danke für deinen Input - habe ich gemacht (altes Script gestoppt, control.socComSwitch gelöscht, neues Script gestartet). Ich werde testen

        maxclaudiM
        maxclaudiM
        maxclaudi
        schrieb am zuletzt editiert von maxclaudi
        #641

        @MP_Trixi
        Kann es sein, dass im Moment keine oder zu wenig Leistung kommt?
        Ist bei mir so.
        Es gibt eigenlich unter properties nur:

        properties.outputPackPower
        properties.packInputPower
        

        Zur Kontrolle kannst Du das Script zur Nulleinspeisung stoppen.
        Setze dann mal im dp:

        control.auto_outputLimitMode
        

        100 oder -100

        gib dem Befehl 10 sek Zeit.
        Danach ändert sich auch der Wert in
        properties.packInputPower
        oder
        properties.outputPackPower

        Wenn das funktioniert ist zu wenig PV-Leistung vorhanden. Müsstest Du auch in properties.solarInputPower feststellen.

        Für die PV-Leistung oder Sonne kann ich nichts ;-)

        PS: Durch das Edit von Dir sieht mein Post nun super aus :-)

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

        neue Zendure Geräte Cloudless: zURLtool V2.0

        1 Antwort Letzte Antwort
        0
        • maxclaudiM maxclaudi

          @T-147 sagte:
          Ich hab hier den SF800 Pro2, der fängt morgens schon ab 10W Gesamtleistung (über alle 4 Panels/MPPT) an zu laden. In meinem Fall und Setuo scheint sich die deutlich niedrigere Start-Spannung von Zendure im Vergleich zu meinem HMS-1000 auszuzahlen.

          Meiner Meinung nach sitzt du da leider einem kleinen Mess- bzw. Anzeigefehler auf, was die realen physikalischen Abläufe angeht.

          Die SolarFlow 800er-Pro-Serie hat die Steuerelektronik, die 4 MPPT-Regler und den Wechselrichter komplett in einem Gehäuse integriert.
          Dieses Gesamtsystem (inklusive ESP32-Chip, WLAN, Bluetooth und Batterie-Management-System) hat einen permanenten, spürbaren Eigenverbrauch für den reinen Betrieb.

          Wenn dir die App am Morgen anzeigt, dass mit "10 W" geladen wird, bedeutet das rein physikalisch folgendes:
          Deine PV-Module müssen zu diesem Zeitpunkt bereits den kompletten internen Grundverbrauch des Geräts (für CPU, WLAN, BMS-Aktivierung etc.) gedeckt haben. Erst die Leistung, die darüber hinausgeht, bleibt netto übrig und wandert als die von dir gesehenen 10 W in die Batterie.

          Man darf hier die reine Startspannung der MPPT-Tracker (die beim Pro 2 erfreulicherweise schon ab 14 V liegt) nicht mit der notwendigen Mindestleistung für den Systembetrieb verwechseln.
          Ohne dass die PV-Leistung die interne Grundlast des Speichers vorab komplett kompensiert, kann das Gerät weder einspeisen noch laden.
          Die Solarmodule müssen real also schon deutlich mehr als 10 W liefern, damit das System überhaupt aus dem Standby erwacht.

          maxclaudiM
          maxclaudiM
          maxclaudi
          schrieb am zuletzt editiert von
          #642

          maxclaudi sagte:

          Die SolarFlow 800er-Pro-Serie hat die Steuerelektronik, die 4 MPPT-Regler und den Wechselrichter komplett in einem Gehäuse integriert.
          Dieses Gesamtsystem (inklusive ESP32-Chip, WLAN, Bluetooth und Batterie-Management-System) hat einen permanenten, spürbaren Eigenverbrauch für den reinen Betrieb.

          Wenn dir die App am Morgen anzeigt, dass mit "10 W" geladen wird, bedeutet das rein physikalisch folgendes:
          Deine PV-Module müssen zu diesem Zeitpunkt bereits den kompletten internen Grundverbrauch des Geräts (für CPU, WLAN, BMS-Aktivierung etc.) gedeckt haben. Erst die Leistung, die darüber hinausgeht, bleibt netto übrig und wandert als die von dir gesehenen 10 W in die Batterie.

          Man darf hier die reine Startspannung der MPPT-Tracker (die beim Pro 2 erfreulicherweise schon ab 14 V liegt) nicht mit der notwendigen Mindestleistung für den Systembetrieb verwechseln.
          Ohne dass die PV-Leistung die interne Grundlast des Speichers vorab komplett kompensiert, kann das Gerät weder einspeisen noch laden.
          Die Solarmodule müssen real also schon deutlich mehr als 10 W liefern, damit das System überhaupt aus dem Standby erwacht.

          Heute ist bei mir so ein Tag - Regen - trüb.
          2400ACpro werden PV-Leistung: 0 angezeigt obwohl 3 Module (420W, 200W, 200W) per DC MC4 angeschlossen sind.
          Im Gegensatz Hoymiles:
          Jedes einzelne Modul an einem Hoymiles speist mit 9 bis 23W ein.

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

          neue Zendure Geräte Cloudless: zURLtool V2.0

          1 Antwort Letzte Antwort
          0
          • maxclaudiM maxclaudi

            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.

            Alternativ gesamtes Verzeichnis mit allen Datenpunkten (inkl. dp properties etc.) löschen, für zusätzliche Beschreibungen.

            Zum aktuellen Skript: HIER KLICKEN

            maxclaudiM
            maxclaudiM
            maxclaudi
            schrieb am zuletzt editiert von
            #643

            Update: Das zenSDK-Skript ist aktuell.
            Wichtigste Änderung bleibt das auto_in_out_Limit (Danke @danitz!).
            Da anscheinend noch ein paar ältere Versionen genutzt werden, obwohl es im letzten Skript schon drin war: Bitte updaten!

            maxclaudi sagte:

            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.

            Alternativ gesamtes Verzeichnis mit allen Datenpunkten (inkl. dp properties etc.) löschen, für zusätzliche Beschreibungen.

            Zum aktuellen Skript: HIER KLICKEN

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

            neue Zendure Geräte Cloudless: zURLtool V2.0

            1 Antwort Letzte Antwort
            1
            • maxclaudiM maxclaudi

              aktuell: zenSDK-Steuerungs-Script update 16.10.26 21:00h


              In memory of Daisy 02.05.24 – miss you.


              Das Projekt wurde komplett neu überarbeitet und auf maximale Effizienz optimiert.

              Zendure-Geräte Webserver: Lokale Steuerung über das zenSDK (HTTP)
              (In memory of Daisy 02.05.24 – miss you.)

              Für alle Zendure Geräte mit Webserver (2025/2026):

              • SolarFlow 800
              • SolarFlow 800 Plus
              • SolarFlow 800 Pro
              • SolarFlow 800 Pro 2
              • SolarFlow 1600 AC+
              • SolarFlow 2400 AC
              • Solarflow 2400 AC+
              • SolarFlow 2400 Pro
              • SolarFlow-Mix-Serie

              • Kein Hack: Nutzung der offiziellen zenSDK-Schnittstelle

              • Hardware-Schutz: Fokus auf RAM-Steuerung zur Schonung des Flash-Speichers (Flash-Schutz ist mir wichtig.)

              • Plattformunabhängig: Läuft unter Linux und Windows.

              • Funktion: Das Skript dient als Adapter-Ersatz für jeweils ein Zendure-Gerät.
                Es funktioniert sofort und wahlweise komplett lokal oder parallel zu Cloud und App (bei deaktiviertem HEMS).
                Außer der IP-Adresse des Zendure-Geräts sind keine weiteren Keys oder Authentifizierungen notwendig.

              Ein Wort in eigener Sache - Urheberrecht:


              Ich habe im Header des Skripts Hinweise zu meiner Motivation und zur Urheberschaft hinterlassen. Da in dieses Projekt extrem viel Eigenforschung (auch Thema Flash-Schutz) geflossen ist, bitte ich darum, die Urheberschaft zu respektieren. Wer diese Logik in öffentliche Projekte oder Adapter übernimmt, sollte so fair sein, die Quelle namentlich zu nennen. Dies ist der „Lohn“ für meine Arbeit.


              12.06.2026 WICHTIGES UPDATE IN EIGENER SACHE Urheberrecht

              Ich teile mein Wissen, meine (API-) Analysen und mathematischen Berechnungen gerne, um der Community zu helfen. Open Source basiert jedoch auf Fairness, Respekt und Transparenz.

              Leider muss ich immer wieder feststellen, dass aus meinen Skripten und Logiken eins zu eins kopiert wird. Wenn diese Arbeit dann andernorts ohne ein Wort des Dankes als „Eigenentwicklung“ oder gar als „Erkenntnis aus einer KI ohne Quelle“ verkauft wird, ist für mich eine rote Linie überschritten.

              Deshalb hier ein letztes Mal mein klares Statement für die Zukunft:

              Ich lade jeden herzlich ein, meine Logiken in öffentliche Projekte oder als hilfreiche Antwort in anderen Threads zu integrieren – aber nur mit einer sauberen Quellenangabe/Verlinkung auf meine Posts, Threads, das jeweilige Skript oder unter Nennung meines Namens „maxclaudi“.

              Sollte ich noch einmal feststellen, dass meine Quelltexte oder mathematischen Formeln anonym abgestaubt und die Spuren meiner Arbeit verwischt werden, ziehe ich die Konsequenzen:

              Ich werde sämtliche Veröffentlichungen, Updates und den Support für meine Skripte in diesem Forum augenblicklich und dauerhaft einstellen.

              Wer die Vorteile von Open Source nutzen möchte, sollte auch die Fairness besitzen, den Urheber zu nennen.


              Zum aktuellen zenSDK-Steuerungs-Skript: HIER KLICKEN

              • Wer meine Arbeit nützlich findet und sich bedanken möchte:
                Ein Klick auf den Pfeil nach oben (unten am Post, neben Zitieren) reicht völlig.


              update 26.10.2026; 18:19h

              zURLtool_V20 (c) maxclaudi 2026 - Version 2.0 cdh

              Neu in zURLtool V 2.0 dch
              • Sprachauswahl: Deutsch und Englisch sind auswählbar und während der Laufzeit umschaltbar.

              • Reset-Funktion: Geräte können zurückgesetzt werden (Hinweis: Zum vollständigen Abmelden - z.B. bei Verkauf - Gerät auch in der App entfernen!).

              • WLAN-Handling: Das manuelle Setzen des WiFi-Mode entfällt. Ein automatischer Scan zeigt alle vom Zendure-Gerät erreichbaren Netze an. Bei zur config.ini abweichendem, manuell ausgewählten WLAN muss auch das Passwort in der GUI angepasst werden.

              • Offline-Modus: Die Option OFFLINE = 1 in der config.ini überspringt den WLAN-Scan, ideal für Vorbereitungen oder Konfigurationen ohne aktiven Router.

              • Token & User-ID: Gerätespezifischer Token wird in mqtt_creds.txt gesichert. Für die korrekte userId (bzw. consumerId) empfiehlt sich der Einsatz des Zusatztools zAppEmulationDeviceInfo

              • Hinweis zu TLS / Port 8883: Das Tool ist ein reiner Konfigurator per Bluetooth und generiert keine TLS-Zertifikate oder Keys.

              00.png

              01-de.png

              zu zURLtool_V20 (c) maxclaudi 2026 - Version 2.0 cdh: HIER KLICKEN

              Beschreibung im Spoiler.


              zURLtool V 2.0 dch – Lokaler MQTT-Broker für Zendure 2026

              Hallo zusammen, das Update zURLtool V 2.0 dch de/gb ist nun fertig!


              Das Programm ist in seiner Art einzigartig, neu und durch monatelanges Reverse-Engineering, Analysen und intensives Testen entstanden.
              Es handelt sich um ein reines Windows-GUI-Programm, das ganz einfach per Doppelklick gestartet wird.

              Es verwendet keinen Code von anderen bekannten Projekten, die teils fehlerhaften und unvollständigen Code verwenden, mit dem Konfigurationen neuer Geräte nicht möglich sind.

              Technische Voraussetzungen & Basis:

              Vielen Dank an Henrik Blidh für die hervorragende BT-Bibliothek.

              Das Programm nutzt zur Kommunikation ausschließlich die offiziellen Windows-BT-Treiber ab Windows 10/11 und die integrierte Bluetooth-Bibliothek bleak:

              ==================================================

              LICENSE NOTICE FOR THE UTILIZED BLUETOOTH LIBRARY
              This program uses "bleak" for Bluetooth functionality.
              https://github.com/hbldh/bleak
              Copyright (c) 2020, Henrik Blidh
              Permission is hereby granted, free of charge, to any person obtaining a copy
              of this software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND.

              ==================================================

              Die Bibliothek wurde fest in das Programm eingebunden.

              Weitere Bibliotheken oder Frameworks müssen auf eurem PC nicht installiert werden.

              • Betriebssystem: Windows (10) 11 (64-Bit)
              • Hardware: Interner Bluetooth-Chip oder ein standardmäßiger USB-Bluetooth-Stick am Rechner.
              • Kompatibilität: Getestet mit Zendure-Geräten bis zur 2400er-Serie (sowohl ältere als auch neueste Generationen / Firmware-Stände).

              Inhalt des Programmpakets (zURLtoolV20.zip):
              Das ZIP-Archiv enthält genau drei Dateien, die zwingend im selben Ordner liegen bleiben müssen:
              • zURLtool_V20.exe (Größe: ca. 15.5 MB / 15555 KB)
              • config.ini (Konfigurationsdatei für eure Zugangsdaten)
              • ReadMe_zURLtoolV20.txt (Ausführliche Anleitung in Deutsch & Englisch)

              Was genau macht das zURLtool?
              Das Tool schreibt im Zendure-Gerät die Konfiguration der Broker-URL um.

              • Es wird NICHT in die Firmware eingegriffen und es wird nichts modifiziert. Die originale Firmware von Zendure bleibt zu 100 % unangetastet.

              • Es werden lediglich die standardmäßigen Bluetooth-Befehle verwendet, die die Firmware von Haus aus für die App bereitstellt.

              • Das Skript verhält sich für das Gerät exakt wie die originale App, greift auf die Konfigurationsvariablen im Speicher zu und überschreibt dort die Wi-Fi-Daten und den Eintrag für die Cloud-URL mit der IP eures eigenen lokalen Brokers.

              Es funktioniert quasi wie eine zusätzliche Länderauswahl, bei der man sich einfach mit einem anderen Server verbindet.
              Das bietet die Firmware von Haus aus an, da die Geräte sonst permanent fest an ganz bestimmte AWS-Server gebunden wären.
              Zurück zur Cloud ist auch möglich. Beispiele sind in der config.ini enthalten.

              Die Geräte verbinden sich und können beschrieben werden ohne hartes Reset und ohne hartes Trennen von allen Stromquellen.

              Freigabe vereinfacht.
              Wer schon eine Seriennummer und Freischalt-Code für zURLtool V1.0 dch hat, kann diese verwenden.
              Seriennummer und Freischalt-Code bleiben gültig, eine erneute Anfrage für zURLtool V2.0 ist nicht erforderlich.

              Der Freischalt-Ablauf:
              Damit das Programm funktioniert und um anonyme Massen-Weiterreichungen zu unterbinden, benötigt das Programm eine Seriennummer und einen Freigabe-Code.

              1. Sende mir deinen (Foren-) Namen per privater Nachricht (PN) im Forum oder per E-Mail an: ztool.maxclaudi AT gmail . com
              2. Du erhältst von mir eine persönliche "Seriennummer" und den dazu passenden "Freigabe-Code"
              3. Sichere dir die Seriennummer und den Freigabe-Code unbedingt in einer Textdatei als Backup! Falls du die Datei mal verlierst, hast du deine Seriennummer und deinen Freigabe-Code als Backup griffbereit.
              4. Starte die 'zURLtool_GUI.exe'.

              Trage beide Werte exakt so, wie du sie erhalten hast, in die dafür vorgesehenen Felder im Startfenster ein und klicke auf "Akzeptieren & Starten".
              Nach einmaliger Eingabe startet zURLtool zukünftig vollautomatisch in die Hauptoberfläche.

              Als Notfall hast du zusätzlich Seriennummer und Freigabecode als Backup.
              Vor dem ersten Start muss bzw. kann die config.ini in einem einfachen Text-Editor mit euren echten Daten befüllt werden.
              Auswählbare Beispiele und Erklärungen zu den WLAN-Modi sind direkt darin enthalten.

              Wenn keine config.ini vorhanden ist oder nicht benutzt wird, müssen die Daten in die GUI eingegeben werden.

              Warum ist das Programm Closed Source?
              Zu oft wurden meine Skripte, Codes oder Teile daraus in der Vergangenheit verwendet und/oder modifiziert – ohne Nennung meines Namens, ohne Danksagung / Anerkennung und unter Entfernung meiner originalen Hinweise.
              Entweder wurde (leicht abgeändert) einfach "übernommen" oder u. a. als eigener Erfolg ausgegeben oder kommentiert mit "ich habe einen Weg gefunden".
              Das ist ungerecht.

              Monatelange harte Arbeit sollte zumindest anerkannt werden.

              Sollte das Programm oder der Code in irgendeiner Form gehackt zur Verfügung gestellt oder verwendet werden, dann war dies mein letzter offizieller Beitrag in Form von Code für die Community.
              Das gilt auch wenn das Programm inkl. Seriennummer und Freigabe-Code mehrfach geteilt wird.

              Viel Spaß mit dem zURLtool V2.0 dch!


              01-eng.png

              02-eng.png

              04-eng.png

              05-eng.png


              Bitte ReadMe_zURLtoolV20.txt beachten, enthält die deutsche und englische Beschreibung.

              HINWEIS FÜR BESTEHENDE NUTZER:
              Wer bereits für die Version 1.0 eine Seriennummer und einen Freischalt-Code erhalten hat, kann diese Daten direkt weiterverwenden!
              Sie sind auch für die Version 2.0 gültig.
              Bitte in diesem Fall keine erneute Anfrage.

              RECHTLICHER HINWEIS ZUR FREIGABE:
              Eine Weitergabe von Seriennummer und Freigabe-Code
              sowie deren Mehrfachnutzung durch verschiedene Personen ist ausdrücklich UNZULÄSSIG.


              Please note ReadMe_zURLtoolV20.txt: contains the German and English description.

              NOTE FOR EXISTING USERS:
              If you have already received a serial number and activation code for version 1.0, you can reuse them directly!
              They are valid for version 2.0 as well. Please do not submit a new request in this case.

              LEGAL NOTICE ON ACTIVATION:
              Sharing the serial number and activation code, as well as their multiple use by different individuals, is strictly PROHIBITED.



              historisch: zURLtool V 1.0 dch

              16.08.2026, NEU
              zusätzliches Windows-Programm:

              zURLtool V 1.0 dch – Lokaler MQTT-Broker für Zendure 2026

              Es handelt sich um ein reines Windows-GUI-Programm, das ganz einfach per Doppelklick gestartet wird.

              Was genau macht das zURLtool?
              Das Tool konfiguriert im Zendure-Gerät die DNS-Adresse (URL) des Ziel-Brokers mit einer selbst gewählten DNS oder IP – beispielsweise für einen eigenen, lokalen MQTT-Broker anstelle der Cloud. 

              Es funktioniert einwandfrei mit allen Zendure-Geräten der neueren Generation (2025/2026) bis zum 2400 AC pro. 
              Die SolarFlow-Mix-Serie wurde mangels Testgeräten nicht getestet.

              • Es wird NICHT in die Firmware eingegriffen und es wird nichts modifiziert. Die originale Firmware von Zendure bleibt zu 100 % unangetastet.

              • Es werden lediglich die standardmäßigen Bluetooth-Befehle verwendet, die die Firmware von Haus aus für die App bereitstellt.

              • Das Skript verhält sich für das Gerät exakt wie die originale App, greift auf die Konfigurations-Variablen im Speicher zu und überschreibt dort die Wi-Fi-Daten und den Eintrag für die Cloud-URL mit der IP eures eigenen lokalen Brokers.

              Es funktioniert quasi wie eine zusätzliche Länderauswahl, bei der man sich einfach mit einem anderen Server verbindet.
              Das bietet die Firmware von Haus aus an, da die Geräte sonst permanent fest an ganz bestimmte AWS-Server gebunden wären.

              Zurück zur Cloud ist auch möglich. Beispiele sind in der config.ini enthalten.

              Die Geräte verbinden sich und können beschrieben werden ohne hartes Reset und ohne hartes Trennen von allen Stromquellen.

              • Betriebssystem: Windows 11 (64-Bit)
              • Hardware: Interner Bluetooth-Chip oder ein standardmäßiger USB-Bluetooth-Stick am Rechner.
              • Kompatibilität: Getestet mit Zendure-Geräten bis zur 2400er-Serie wie z. B. 2400 AC Pro (sowohl ältere als auch neueste Generationen / Firmware-Stände).

              Das Programm ist in seiner Art einzigartig, neu und durch monatelanges Reverse-Engineering, Analysen und intensives Testen entstanden.
              Es handelt sich um ein reines Windows-GUI-Programm, das ganz einfach per Doppelklick gestartet wird.
              Keine (zusätzlich(en)) Installation(en) über Setup etc. erfoderlich.
              Es verwendet keinen Code von anderen bekannten Projekten (wie dem Solarflow Bluetooth Manager oder dem zendure-cloud-disconnector).

              Zur Beschreibung und Download: zURLtool


              Null-Einspeisung JS-Script Basis-Vorlage vom 14. Okt. 2026; 10:04h
              einfach gehalten und für Anfänger leicht nachvollziehbar.
              Funktioniert mit Zendure-Geräte die 1 bis 6 Batterien verwenden (konfigurierbar) POST #632




              HISTORISCH:

              Immer wieder taucht die Frage auf (z. B. Michi 0 ), wie man den SmartMode:1 setzen kann.
              Bisher wurde das noch nirgends wirklich anwenderfreundlich beantwortet.

              Hier nun die Lösung:

              Mit diesem Script können nicht nur smartMode:1 und die MQTT-Verbindung (de-)aktiviert werden, sondern auch einiges mehr.


              Update 2026.01.13_16.05h
              ioBroker JavaScript: Zendure SolarFlow 800 Pro (SF2400AC) via zenSDK HTTP
              Kein Hack, dafür wurde die offizielle zenSDK verwendet.
              plattformunabhängig - Linux, Windows

              Jetzt können auch Commands gesetzt werden, wie in einem Adapter. 🙂

              Muss man nicht nutzen, kann man aber zusätzlich einsetzen – z. B. falls MQTT mal Probleme macht oder wenn weitere set-Befehle dazukommen sollen.

              Hinweis:
              Damit neue Datenpunkte automatisch aus dem JSON erstellt werden:
              → Instanzen → JavaScript-Adapter → Allgemeine Einstellungen → „Enable command setObject“ aktivieren!

              So viel wie möglich wurde ohne Gerät getestet und simuliert.
              Extra ohne Schleifen und strukturiert geschrieben, damit jeder das Script nachvollziehen kann.

              Intervalle

              • intervalGet sollte nicht < 30 s gesetzt werden – das bringt keinen Mehrwert.
              • intervalMqtt = 300 s ist völlig ausreichend, um zu prüfen, ob MQTT verbunden ist.

              Eigene Daten anpassen
              Zendure Gerät:

              const IP = "192.168.177.103";   // IP des Zendure Geräts
              const SN = "EXXXXXXXXXXXXX0";   // Seriennummer
              

              MQTT-Verbindungs-Status im Intervall abfragen? ja: true / nein: false

              // MQTT Status zyklisch abfragen? ja: true / nein: false
              const mqttStateAsking = true;   // true = zyklische MQTT-Abfrage aktiv, false = deaktiviert
              

              Intervalle für Abfragen (in Sekunden):

              const intervalGet = 60;   // SmartMode (default: 60 s, nicht < 30)
              const intervalMqtt = 300; // MQTT-Status (default: 300 s, > SmartMode)
              

              MQTT-Broker Konfiguration:

              const mqttBrokerIp = "192.168.177.200"; // IP MQTT Broker (auch in App eingestellt)
              const mqttPort = 1883;                  // Port MQTT Broker (auch in App eingestellt)           
              const mqttUsername = "Daisy";           // MQTT Username
              const mqttPassword = "coco";            // MQTT Passwort
              
              

              Maximale Input-/Output-Limits:

              // Maximum Input-Limit
              const maxInputLimit = 1000;   // SF800Pro
              //const maxInputLimit = 2400; // SF2400AC
              
              // Maximum Output-Limit
              const maxOutputLimit = 800;   // SF800Pro
              //const maxOutputLimit = 2400; // SF2400AC
              
              

              Hinweise

              • wichtig: wenn MQTT über dieses Script eingeschaltet wird, hat es vermutlich Vorrang!
                Es werden ggf. andere MQTT-Einstellungen in der App durch das Script überschrieben.

              • Wer meine Arbeit nützlich findet und sich bedanken möchte:
                Ein Klick auf den Pfeil nach oben (unten am Post, neben Zitieren) reicht völlig. Das ist für mich mehr als genug. 🙂

              • Dieser erste Post wird bei Änderungen editiert, damit das aktuelle Script immer direkt hier auffindbar ist.

              • Beschreibung / Anleitung
                Hinweise zur Benutzung, der aktuellen Version des Scripts und zusätzliche Tipps, siehe:
                Post im Thread

              Viel Spaß! ☀


              //
              // ioBroker JavaScript: Zendure SolarFlow 800 Pro (SF2400AC) via zenSDK HTTP
              // by maxclaudi 2026.01.13_16.05h for ioBroker Forum
              //------------------------------------------------------------------------------------
              // konfiguration
              //
              // Hinweis bei mehreren Zendure-Geräten:
              // - Für jedes Gerät ein eigenes Script mit individueller Konfiguration verwenden!
              //   -> IP-Adresse
              //   -> Seriennummer (SN)
              //   -> MQTT-Daten (Broker, Port, Benutzer, Passwort)
              //   -> maxInputLimit / maxOutputLimit (abhängig vom Gerätetyp)
              // - Intervalle (Standard): intervalGet = 60s, intervalMqtt = 300s – können gleich bleiben
              //
              // Empfehlung:
              //   • 3 Geräte: völlig unkritisch
              //   • 4 Geräte: problemlos möglich
              //   • mehr als 4: nicht empfohlen
              //
              //
              // Damit neue Datenpunkte automatisch aus dem JSON erstellt werden:
              // -> Instanzen -> javascript-Adapter -> Allgemeine Einstellungen -> Enable command"setObject“ aktivieren!
              //
              //------------------------------------------------------------------------------------
              // MQTT Status zyklisch abfragen? ja: true / nein: false
              const mqttStateAsking = true;   // true = zyklische MQTT-Abfrage aktiv, false = deaktiviert
              
              // Intervalle für Abfragen (Sekunden)
              const intervalGet = 60;                 // sek SmartMode   (default:  60 sek, not <30)
              const intervalMqtt = 300;               // sek MQTT-Status (default: 300 sek, > SmartMode)
               
              // IP und Seriennummer Zendure Gerät
              const IP = "192.168.40.111";            // IP des Zendure Geräts
              const SN = "EXXXXXXXXXXXXX0";           // Seriennummer
               
              // MQTT Broker Konfiguration
              const mqttBrokerIp = "192.168.40.200";  // IP MQTT Broker und in App eingestellt 
              const mqttPort = 1883;                  // PORT MQTT Broker und in App eingestellt           
              const mqttUsername = "Daisy";           // MQTT Username
              const mqttPassword = "coco";            // MQTT Passwort
               
              //maximum inputLimit SF800PRO: 800W / SF2400AC: 2400W
              const maxInputLimit = 1000; //SF800Pro
              //const maxInputLimit = 2400; //SF2400AC
               
              //maximum outputLimit SF800PRO: 800W / SF2400AC: 2400W
              const maxOutputLimit = 800; //SF800Pro
              //const maxOutputLimit = 2400; //SF2400AC
              //------------------------------------------------------------------------------------ 
              //------------------------------------------------------------------------------------
              // Datenpunkte
              const folderZendureApi = '0_userdata.0.zendure.' + SN;
              //------------------------------------------------------------------------------------
              const dpSmartModeInfo              = folderZendureApi + ".zendureSmartMode.smartModeInfo";
              const dpSetSmartMode               = folderZendureApi + ".zendureSmartMode.setSmartMode";
              const dpSetSmartModeResult         = folderZendureApi + ".zendureSmartMode.setResult";
              const dpTimestamp                  = folderZendureApi + ".zendureSmartMode.timestamp";
               
              const dpMqttConnectInfo            = folderZendureApi + ".zendureMqttState.mqttConnectInfo";
              const dpSetMqttConnect             = folderZendureApi + ".zendureMqttState.setMqttConnect";
              const dpSetMqttConnectResult       = folderZendureApi + ".zendureMqttState.setMqttConnectResult";
              const dpMqttTimestamp              = folderZendureApi + ".zendureMqttState.mqttTimestamp";
               
              //set dp
              const dpSetAcMode                  = folderZendureApi + ".control.acMode.setAcMode";
              const dpSetAcModeResult            = folderZendureApi + ".control.acMode.setAcModeResult";
              const dpSetAcModeResultTS          = folderZendureApi + ".control.acMode.setAcModeResultTs";
               
              const dpSetInputLimit              = folderZendureApi + ".control.inputLimit.setInputLimit";
              const dpSetInputLimitResult        = folderZendureApi + ".control.inputLimit.setInputLimitResult";
              const dpSetInputLimitResultTS      = folderZendureApi + ".control.inputLimit.setInputLimitResultTs";
               
              const dpSetOutputLimit             = folderZendureApi + ".control.outputLimit.setOutputLimit";
              const dpSetOutputLimitResult       = folderZendureApi + ".control.outputLimit.setOutputLimitResult";
              const dpSetOutputLimitResultTS     = folderZendureApi + ".control.outputLimit.setOutputLimitResultTs";
               
              const dpSetSocSet                  = folderZendureApi + ".control.socSet.setSocSet";
              const dpSetSocSetResult            = folderZendureApi + ".control.socSet.setSocSetResult";
              const dpSetSocSetResultTS          = folderZendureApi + ".control.socSet.setSocSetResultTs";
               
              const dpSetMinSoc                  = folderZendureApi + ".control.minSoc.setMinSoc";
              const dpSetMinSocResult            = folderZendureApi + ".control.minSoc.setMinSocResult";
              const dpSetMinSocResultTS          = folderZendureApi + ".control.minSoc.setMinSocResultTs";
               
              const dpSetGridReverse             = folderZendureApi + ".control.gridReverse.setGridReverse";
              const dpSetGridReverseResult       = folderZendureApi + ".control.gridReverse.setGridReverseResult";
              const dpSetGridReverseResultTS     = folderZendureApi + ".control.gridReverse.setGridReverseResultTs";
               
              const dpSetGridStandard            = folderZendureApi + ".control.gridStandard.setGridStandard";
              const dpSetGridStandardResult      = folderZendureApi + ".control.gridStandard.setGridStandardResult";
              const dpSetGridStandardResultTS    = folderZendureApi + ".control.gridStandard.setGridStandardResultTs";
               
              const dpSetInverseMaxPower         = folderZendureApi + ".control.inverseMaxPower.setInverseMaxPower";
              const dpSetInverseMaxPowerResult   = folderZendureApi + ".control.inverseMaxPower.setInverseMaxPowerResult";
              const dpSetInverseMaxPowerResultTS = folderZendureApi + ".control.inverseMaxPower.setInverseMaxPowerResultTs";
               
              const dpSetChargeMaxLimit         = folderZendureApi + ".control.chargeMaxLimit.setChargeMaxLimit";
              const dpSetChargeMaxLimitResult   = folderZendureApi + ".control.chargeMaxLimit.setChargeMaxLimitResult";
              const dpSetChargeMaxLimitTS       = folderZendureApi + ".control.chargeMaxLimit.setChargeMaxLimitResultTs"; 
              //------------------------------------------------------------------------------------
              const http = require("http");           // Node.js http Standardmodul
              //------------------------------------------------------------------------------------
               
              // Mapping: Payload key, optional transformation, result and timestamp dp
              const dpMap = {
                  [dpSetAcMode]:          { key: "acMode", result: dpSetAcModeResult, resultTS: dpSetAcModeResultTS, minNormal: 1, maxNormal: 2 },
                  [dpSetInputLimit]:      { key: "inputLimit", result: dpSetInputLimitResult, resultTS: dpSetInputLimitResultTS, minNormal: 0, maxNormal: maxInputLimit },
                  [dpSetOutputLimit]:     { key: "outputLimit", result: dpSetOutputLimitResult, resultTS: dpSetOutputLimitResultTS, minNormal: 0, maxNormal: maxOutputLimit },
                  [dpSetSocSet]:          { key: "socSet", transform: v => v*10, result: dpSetSocSetResult, resultTS: dpSetSocSetResultTS, minNormal: 70, maxNormal: 100 },
                  [dpSetMinSoc]:          { key: "minSoc", transform: v => v*10, result: dpSetMinSocResult, resultTS: dpSetMinSocResultTS, minNormal: 5, maxNormal: 50 },
                  [dpSetGridReverse]:     { key: "gridReverse", result: dpSetGridReverseResult, resultTS: dpSetGridReverseResultTS, minNormal: 0, maxNormal: 2 },
                  [dpSetGridStandard]:    { key: "gridStandard", result: dpSetGridStandardResult, resultTS: dpSetGridStandardResultTS, minNormal: 0, maxNormal: 2 },
                  [dpSetInverseMaxPower]: { key: "inverseMaxPower", result: dpSetInverseMaxPowerResult, resultTS: dpSetInverseMaxPowerResultTS, minNormal: 600, maxNormal: maxOutputLimit },
              	[dpSetChargeMaxLimit]:  { key: "chargeMaxLimit", result: dpSetChargeMaxLimitResult, resultTS: dpSetChargeMaxLimitTS, minNormal: 0, maxNormal: maxInputLimit }
              };
               
              // Queue HTTP
              const curlQueue = [];
              let curlRunning = false;
               
              function runQueue() {
                  if (curlRunning || curlQueue.length === 0) return;
                  const task = curlQueue.shift();
                  curlRunning = true;
                  task.fn(() => {
                      curlRunning = false;
                      runQueue();
                  });
              }
               
              //dp have to exist - get
              function delay(ms) {
                  return new Promise(resolve => setTimeout(resolve, ms));
              }
               
              // dp create
              createState(dpSmartModeInfo, 0, {
                      name: "SmartMode Info",
                      type: "number",
                      role: "state",
                      read: true,
                      write: false,
                      min: 0,
                      max: 1
                      }, async () => {
                         await delay(1);
                         getReport();
              });
               
              createState(dpSetSmartMode, -1, {
                     name: "SmartMode Set",
              	   type: "number",
              	   role: "state",
              	   read: true,
              	   write: true,
              	   min: -1,
              	   max: 1
              }, () => {});
               
              createState(dpSetSmartModeResult, "", {
                     name: "SmartMode Set Result",
              	   type: "string",
              	   role: "info",
              	   read: true,
              	   write: false
              }, () => {});
               
              createState(dpTimestamp, "", {
                     name: "Timestamp",
              	   type: "string",
              	   role: "info",
              	   read: true,
              	   write: false
                     }, async () => {
                         await delay(1);
              });
              
              createState(dpMqttConnectInfo, 0, {
                  name: "MQTT Connect Info",
                  type: "number", 
                  role: "state",
                  read: true,
                  write: false,
                  min: 0,
                  max: 1
              }, async () => {
                  await delay(1);
                  if (mqttStateAsking) {
                      getMqttStatus();
                  }
              });
               
              createState(dpSetMqttConnect, -1, {
                     name: "MQTT Connect Set",
              	   type: "number",
              	   role: "state",
              	   read: true,
              	   write: true,
              	   min: -1,
              	   max: 1
              }, () => {});
               
              createState(dpSetMqttConnectResult, "", {
                     name: "MQTT Connect Result",
              	   type: "string",
              	   role: "info",
              	   read: true,
              	   write: false
              }, () => {});
               
              createState(dpMqttTimestamp, "", {
                     name: "MQTT Timestamp",
              	   type: "string",
              	   role: "info",
              	   read: true,
              	   write: false
              }, async () => { await delay(1); });
               
              // set > result ts dp
              // acMode
              createState(dpSetAcMode, -1, {
                     name: "set acMode, 1: charging 2: discharging",
              	   desc: "1: Input, 2: Output",
              	   type: "number",
              	   role: "state",
              	   read: true,
              	   write: true,
              	   min: -1,
              	   max: 2
              }, () => {});
               
              createState(dpSetAcModeResult, "", {
                  name: "AcMode Set Result",
                  type: "string",
                  role: "info",
                  read: true,
                  write: false
              }, () => {});
               
              createState(dpSetAcModeResultTS, "", {
                  name: "AcMode Set Result Timestamp",
                  type: "string",
                  role: "info",
                  read: true,
                  write: false
              }, () => {});
               
              // inputLimit
              createState(dpSetInputLimit, -1, {
                     name: "set inputLimit",
              	   desc: "AC charging power limit",
              	   type: "number",
              	   role: "state",
              	   read: true,
              	   write: true,
              	   unit: 'W',
              	   min: -1,
              	   max: maxInputLimit //SF800Pro: 1000W, SF2400AC: 2400W
              }, () => {});
               
              createState(dpSetInputLimitResult, "", {
                  name: "InputLimit Set Result",
                  type: "string",
                  role: "info",
                  read: true,
                  write: false
              }, () => {});
               
              createState(dpSetInputLimitResultTS, "", {
                  name: "InputLimit Set Result Timestamp",
                  type: "string",
                  role: "info",
                  read: true,
                  write: false
              }, () => {});
               
              // outputLimit
              createState(dpSetOutputLimit, -1, {
                     name: "set outputLimit",
              	   desc: "Output power limit",
              	   type: "number",
              	   role: "state",
              	   read: true,
              	   write: true,
              	   unit: 'W',
              	   min: -1,
              	   max: maxOutputLimit //SF800Pro: 800W, SF2400AC: 2400W
              }, () => {});
               
              createState(dpSetOutputLimitResult, "", {
                  name: "OutputLimit Set Result",
                  type: "string",
                  role: "info",
                  read: true,
                  write: false
              }, () => {});
               
              createState(dpSetOutputLimitResultTS, "", {
                  name: "OutputLimit Set Result Timestamp",
                  type: "string",
                  role: "info",
                  read: true,
                  write: false
              }, () => {});
               
              // socSet
              createState(dpSetSocSet, -1, {
                     name: "set socSet 70-100%",
              	   desc: "Stop charging at battery percentage",
              	   type: "number",
              	   role: "state",
              	   read: true,
              	   write: true,
              	   unit: '%',
              	   min: -1,
              	   max: 100
              }, () => {});
               
              createState(dpSetSocSetResult, "", {
                  name: "SocSet Set Result",
                  type: "string",
                  role: "info",
                  read: true,
                  write: false
              }, () => {});
               
              createState(dpSetSocSetResultTS, "", {
                  name: "SocSet Set Result Timestamp",
                  type: "string",
                  role: "info",
                  read: true,
                  write: false
              }, () => {});
               
              // minSoc
              createState(dpSetMinSoc, -1, {
                     name: "set minSoc 0-50%",
              	   desc: "Stop discharging at battery percentage",
              	   type: "number",
              	   role: "state",
              	   read: true,
              	   write: true,
              	   unit: '%',
              	   min: -1,
              	   max: 50
              }, () => {});
               
              createState(dpSetMinSocResult, "", {
                  name: "MinSoc Set Result",
                  type: "string",
                  role: "info",
                  read: true,
                  write: false
              }, () => {});
               
              createState(dpSetMinSocResultTS, "", {
                  name: "MinSoc Set Result Timestamp",
                  type: "string",
                  role: "info",
                  read: true,
                  write: false
              }, () => {});
               
              // gridReverse
              createState(dpSetGridReverse, -1, {
                     name: "setGridReverse, 0: off, 1: rev. 2: no rev",
              	   desc: "0: Disabled, 1: Allowed reverse flow, 2: Forbidden reverse flow",
              	   type: "number",
              	   role: "state",
              	   read: true,
              	   write: true,
              	   min: -1,
              	   max: 2
              }, () => {});
               
              createState(dpSetGridReverseResult, "", {
                  name: "GridReverse Set Result",
                  type: "string",
                  role: "info",
                  read: true,
                  write: false
              }, () => {});
               
              createState(dpSetGridReverseResultTS, "", {
                  name: "GridReverse Set Result Timestamp",
                  type: "string",
                  role: "info",
                  read: true,
                  write: false
              }, () => {});
               
              // gridStandard
              createState(dpSetGridStandard, -1, {
                     name: "setGridStandard, 0: Germany / 2: France",
              	   desc: "Grid connection standard 0: Germany 1: France 2: Austria",
              	   type: "number",
              	   role: "state",
              	   read: true,
              	   write: true,
              	   min: -1,
              	   max: 2
              }, () => {});
               
              createState(dpSetGridStandardResult, "", {
                  name: "GridStandard Set Result",
                  type: "string",
                  role: "info",
                  read: true,
                  write: false
              }, () => {});
               
              createState(dpSetGridStandardResultTS, "", {
                  name: "GridStandard Set Result Timestamp",
                  type: "string",
                  role: "info",
                  read: true,
                  write: false
              }, () => {});
               
              // inverseMaxPower
              createState(dpSetInverseMaxPower, -1, {
                     name: "set inverseMaxPower",
              	   desc: "Maximum output power limit",
              	   type: "number",
              	   role: "state",
              	   read: true,
              	   write: true,
              	   unit: 'W',
              	   min: -1,
              	   max: maxOutputLimit
              }, () => {});
               
              createState(dpSetInverseMaxPowerResult, "", {
                  name: "inverseMaxPower Set Result",
                  type: "string",
                  role: "info",
                  read: true,
                  write: false
              }, () => {});
               
              createState(dpSetInverseMaxPowerResultTS, "", {
                  name: "inverseMaxPower Set Result Timestamp",
                  type: "string",
                  role: "info",
                  read: true,
                  write: false
              }, () => {});
               
              // chargeMaxLimit
              createState(dpSetChargeMaxLimit, -1, {
                     name: "set chargeMaxLimit",
              	   desc: "Maximum permissible charging power",
              	   type: "number",
              	   role: "state",
              	   read: true,
              	   write: true,
              	   unit: 'W',
              	   min: -1,
              	   max: maxInputLimit
              }, () => {});
               
              createState(dpSetChargeMaxLimitResult, "", {
                  name: "chargeMaxLimit Set Result",
                  type: "string",
                  role: "info",
                  read: true,
                  write: false
              }, () => {});
               
              createState(dpSetChargeMaxLimitTS, "", {
                  name: "chargeMaxLimit Set Result Timestamp",
                  type: "string",
                  role: "info",
                  read: true,
                  write: false
              }, () => {});
               
              //dpSet -1 
              const dpSetList = [
                  dpSetSmartMode,
                  dpSetMqttConnect,
                  dpSetAcMode,
                  dpSetInputLimit,
                  dpSetOutputLimit,
                  dpSetSocSet,
                  dpSetMinSoc,
                  dpSetGridReverse,
                  dpSetGridStandard,
                  dpSetInverseMaxPower,
              	dpSetChargeMaxLimit
              ];
               
              //reset -1
              function resetAllDpSet() {
                  dpSetList.forEach(dp => {
                      setState(dp, -1, true);
                  });
              }
               
              setTimeout(() => resetAllDpSet(), 10);
               
              // intervals start																											  
              setInterval(getReport, intervalGet*1000);
              if (mqttStateAsking) {
                  setInterval(getMqttStatus, intervalMqtt * 1000);
              }
               
              // trigger smartMode
              on({id: dpSetSmartMode, ack:false}, obj => {
                  const val = parseInt(obj.state.val,10);
                  if(val===0||val===1) setSmartMode(val);
              });
               
              // trigger MQTT connect
              on({id: dpSetMqttConnect, ack:false}, obj => {
                  const val = parseInt(obj.state.val,10);
                  if(val===0||val===1) setMqttConnect(val);
              });
               
              // trigger dp set
              Object.keys(dpMap).forEach(id => {
                  on({ id: id, ack: false }, obj => {
                      const val = obj.state.val;
                      const { minNormal, maxNormal } = dpMap[id];
               
                      // check values
                      if (val >= minNormal && val <= maxNormal) {
              																 
                          setControlDP(id, val);  // Transformation
                      } else {
                          log(`Value ${val} for ${id} is not allowed`, 'warn');
                      }
                  });
              });
               
              // time format
              function formatTime(ts) {
              	// ts unix sek							
                  const d = new Date(ts * 1000);
                  const pad = n => n.toString().padStart(2, "0");
                  return `${pad(d.getDate())}.${pad(d.getMonth()+1)}.${d.getFullYear().toString().slice(-2)} ${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`;
              }
               
              // helper HTTP-error
              function handleHttpError(context, err, options) {
                  const transient = [
                      "EHOSTUNREACH", "ECONNRESET", "ECONNREFUSED", "ETIMEDOUT",
                      "socket hang up", "ENETUNREACH", "EAI_AGAIN", "ENOTFOUND"
                  ];
               
                  const msg = err && err.message ? err.message : String(err);
                  const isTransient = transient.some(code => msg.includes(code));
               
                  //info_error
              	//log(`${context}: ${isTransient ? "war vorübergehend im Intervall nicht erfolgreich:" : "Warnung"} (${msg})`, isTransient ? "info" : "warn");
                  log(`${context}: ${isTransient ? "war vorübergehend im Intervall nicht erfolgreich:" : "Warnung"} (${msg})`, "info");
               
                  // set Timestamp
                  if (options && options.timestamp) {
                      setState(options.timestamp, formatTime(Math.floor(Date.now() / 1000)), true);
                  }
              }
               
              // smartMode GET report
              function getReport() {
              	resetAllDpSet();
                  curlQueue.push({
                      fn: async done => {      //async
                          const options = {
                              hostname: IP,
                              port: 80,
                              path: "/properties/report",
                              method: "GET",
                              timeout: 5000
                          };
                          const req = http.request(options, res => {
                              let data = "";
                              res.on("data", chunk => data += chunk);
                              res.on("end", async () => {     // async
                                  try {
                                      const json = JSON.parse(data);
                                      if (json.properties && typeof json.properties.smartMode !== "undefined") {
                                          setState(dpSmartModeInfo, json.properties.smartMode, true);
                                          setState(dpTimestamp, formatTime(Math.floor(Date.now() / 1000)), true);
                                      }
                                      //JSON
                                      await processJson(json);
                                  } catch (e) {
                                      log("GET JSON Parse Fehler: " + e, "info"); //no valid JSON
                                      setState(dpTimestamp, formatTime(Math.floor(Date.now() / 1000)), true);
                                  }
                                  done();
                              });
                          });
                          req.on("error", err => { 
                              handleHttpError("HTTP GET", err, { timestamp: dpTimestamp }); 
                              done(); 
                          });
                          req.end();
                      }
                  });
                  runQueue();
              }
               
              // smartMode POST
              function setSmartMode(val) {
                  curlQueue.push({
                      fn: done => {
                          const payload = JSON.stringify({ sn: SN, properties: { smartMode: val } });
                          const options = {
                              hostname: IP,
                              port: 80,
                              path: "/properties/write",
                              method: "POST",
                              headers: {
                                  "Content-Type": "application/json",
                                  "Content-Length": Buffer.byteLength(payload)
                              },
                              timeout: 5000
                          };
               
                          const req = http.request(options, res => {
                              let data = "";
                              res.on("data", chunk => data += chunk);
               
                              res.on("end", () => {
                                  let ok = false;
               
                                  if (data) {
                                      try {
                                          const json = JSON.parse(data);
                                          ok = json.success === true;
                                      } catch (e) {
                                          ok = false; // no valid JSON
                                      }
                                  }
              					setState(dpSetSmartModeResult, ok ? `ok set ${val}` : `error set ${val}`, true);
                                  setState(dpTimestamp, formatTime(Math.floor(Date.now() / 1000)), true);
              					setState(dpSetSmartMode, -1, true);
                                  done();
                              });
                          });
               
                          req.on("error", err => {
                              handleHttpError("HTTP POST", err, { timestamp: dpTimestamp });
                              setState(dpSetSmartModeResult, `error set ${val}`, true);
                              setState(dpTimestamp, formatTime(Math.floor(Date.now() / 1000)), true);
              				setState(dpSetSmartMode, -1, true);
                              done();
                          });
               
                          req.write(payload);
                          req.end();
                      }
                  });
               
                  runQueue();
              }
               
              // MQTT state GET
              function getMqttStatus() {
                  curlQueue.push({
                      fn: done => {
                          const options = {
                              hostname: IP,
                              port: 80,
                              path: "/rpc?method=HA.Mqtt.GetStatus",
                              method: "GET",
                              timeout: 5000
                          };
                          const req = http.request(options, res => {
                              let data = "";
                              res.on("data", chunk => data += chunk);
                              res.on("end", () => {
                                  try {
                                      const json = JSON.parse(data);
                                      const state = json.connected ? 1 : 0;
                                      setState(dpMqttConnectInfo, state, true);
                                      setState(dpMqttTimestamp, formatTime(Math.floor(Date.now() / 1000)), true);
                                  } catch (e) {
                                      log(`MQTT JSON Parse Fehler: ${e}`, "info"); //no valid JSON
                                      setState(dpMqttTimestamp, formatTime(Math.floor(Date.now() / 1000)), true);
                                  }
                                  done();
                              });
                          });
                          req.on("error", err => { 
                              handleHttpError("HTTP MQTT GET", err, { timestamp: dpMqttTimestamp }); 
                              done(); 
                          });
                          req.end();
                      }
                  });
                  runQueue();
              }
               
              // MQTT POST
              function setMqttConnect(val) {
                  curlQueue.push({
                      fn: done => {
                          const enable = !!val;
                          const payload = JSON.stringify({
                              sn: SN,
                              method: "HA.Mqtt.SetConfig",
                              params: {
                                  config: {
                                      enable: enable,
                                      server: mqttBrokerIp,
                                      port: mqttPort,
                                      protocol: "mqtt",
                                      username: mqttUsername,
                                      password: mqttPassword
                                  }
                              }
                          });
               
                          const options = {
                              hostname: IP,
                              port: 80,
                              path: "/rpc",
                              method: "POST",
                              headers: {
                                  "Content-Type": "application/json",
                                  "Content-Length": Buffer.byteLength(payload)
                              },
                              timeout: 5000
                          };
               
                          const req = http.request(options, res => {
                              let data = "";
                              res.on("data", chunk => data += chunk);
               
                              res.on("end", () => {
                                  let ok = false;
               
                                  // HTTP 200 =  ok, Request succes
                                  if (res.statusCode === 200) {
                                      if (data) {
                                          try {
                                              const json = JSON.parse(data);
                                              ok = json.success === true;
                                          } catch (e) {
                                              ok = false; // no valid JSON
                                          }
                                      }
                                  }
              					setState(dpSetMqttConnectResult, ok ? `ok set ${val}` : `error set ${val}`, true);
                                  setState(dpMqttTimestamp, formatTime(Math.floor(Date.now() / 1000)), true);
              					setState(dpSetMqttConnect, -1, true);
                                  done();
                              });
                          });
               
                          req.on("error", err => {
                              handleHttpError("HTTP MQTT POST", err, { timestamp: dpMqttTimestamp });
                              setState(dpSetMqttConnectResult, `error set ${val}`, true);
                              setState(dpMqttTimestamp, formatTime(Math.floor(Date.now() / 1000)), true);
              				setState(dpSetMqttConnect, -1, true);
                              done();
                          });
               
                          req.write(payload);
                          req.end();
                      }
                  });
               
                  runQueue();
              }
               
              // Generic POST function with result and timestamp handling
              function setControlDP(id, val) {
                  if (!dpMap[id]) return;
                  const { key, transform, result, resultTS } = dpMap[id];
                  const value = transform ? transform(val) : val;
               
                  curlQueue.push({
                      fn: done => {
                          const payload = JSON.stringify({ sn: SN, properties: { [key]: value } });
                          const options = {
                              hostname: IP,
                              port: 80,
                              path: "/properties/write",
                              method: "POST",
                              headers: {
                                  "Content-Type": "application/json",
                                  "Content-Length": Buffer.byteLength(payload)
                              },
                              timeout: 5000
                          };
               
                          const req = http.request(options, res => {
                              let data = "";
                              res.on("data", chunk => data += chunk);
               
                              res.on("end", () => {
                                  let ok = false;
               
                                  // HTTP 200 = Connection established
                                  if (res.statusCode === 200) {
                                      if (data) {
                                          try {
                                              const json = JSON.parse(data);
                                              ok = json.success === true;
                                          } catch (e) {
                                              ok = false; // no valid JSON
                                          }
                                      }
                                  }
               
                                  // Write success or failure to data points
                                  if (result) {
                                      const valueToSend = val;  // Wert, der gesendet wurde
              						setState(result, ok ? `ok set ${valueToSend}` : `error set ${valueToSend}`, true);
                                  }
                                  if (resultTS) setState(resultTS, formatTime(Math.floor(Date.now() / 1000)), true);
              					setState(id, -1, true);
                                  done();
                              });
                          });
                          
              			req.on("error", err => {
                              handleHttpError("HTTP POST " + key, err, { timestamp: resultTS });
                              if (result) {
                                  const valueToSend = val;  // Wert, der gesendet wurde
              					setState(result, `error set ${valueToSend}`, true);
                              }
                              if (resultTS) setState(resultTS, formatTime(Math.floor(Date.now() / 1000)), true);
              				setState(id, -1, true);
                              done();
                          });
                          req.write(payload);
                          req.end();
                      }
                  });
                  runQueue();
              }
               
              //------------------------------------------------------------------------------------
              // JSON
              //------------------------------------------------------------------------------------
              async function processJson(obj) {
                  try {
                      if (!obj || !obj.product) return;
               
                      const product = obj.product;
                      const basePath = `${folderZendureApi}.${product}`;
               
                      // create product folder if not exist
                      if (!existsObject(basePath)) {
                          setObject(basePath, {
                              type: 'folder',
                              common: { name: product },
                              native: {},
                          });
                      }
               
                      // create main keys
                      const mainKeys = ['timestamp', 'messageId', 'sn', 'version', 'product'];
                      for (const key of mainKeys) {
                          if (obj[key] !== undefined) {
                              const statePath = `${basePath}.${key}`;
                              if (existsState(statePath)) {
                                  setState(statePath, obj[key], true);
                              } else {
                                  createState(statePath, obj[key], {
                                      name: key,
                                      type: typeof obj[key],
                                      role: 'info',
                                      read: true,
                                      write: false,
                                  });
                              }
                          }
                      }
               
                      // properties
                      if (obj.properties) {
                          await iter(`${basePath}.properties`, obj.properties);
                      }
               
                      // PackData
                      if (obj.packData && Array.isArray(obj.packData)) {
                          await iter(`${basePath}`, { packData: obj.packData, timestamp: obj.timestamp });
                      }
                  } catch (e) {
                      log(`Error processing JSON: ${e}`, 'info');
                  }
              }
               
              //------------------------------------------------------------------------------------
              // Battery
              //------------------------------------------------------------------------------------
              function getBatteryType(sn, model) {
                let batType = '';
                if (sn?.startsWith('A')) batType = 'AB1000';
                else if (sn?.startsWith('B')) batType = 'AB1000S';
                else if (sn?.startsWith('C')) batType = sn[3] === 'F' ? 'AB2000S' : sn[3] === 'E' ? 'AB2000X' : 'AB2000';
                else if (sn?.startsWith('F')) batType = 'AB3000X';
                if (model?.trim()) batType = model.trim();
                return batType || 'unknown';
              } 
               
              //------------------------------------------------------------------------------------
              // helper Iteration
              //------------------------------------------------------------------------------------
              async function iter(id, obj) {
                  try {
              		if (!obj || typeof obj !== "object") {
                         //log("iter: Ungültiges oder leeres Objekt übersprungen", "info");
                         return;
                         }
              		for (let i in obj) {
                          if (i === 'packData' && Array.isArray(obj[i])) {
                              const ts = obj.timestamp ? obj.timestamp * 1000 : Date.now();
                              for (const pack of obj[i]) {
                                  if (!pack.sn) continue;
                                  const sn = pack.sn;
                                  const path = `${id}.packData.${sn}`;
               
                                  if (!existsObject(path)) {
                                      setObject(path, {
                                          type: 'folder',
                                          common: { name: sn },
                                          native: {},
                                      });
                                  }
               
                                  for (let [key, val] of Object.entries(pack)) {
                                      const statePath = `${path}.${key}`;
                                      switch (key) {
                                          case 'batcur':
                                              val = (val << 16 >> 16) / 10;
                                              if (existsState(statePath)) {
                                                  setState(statePath, val, true);;
                                              } else {
                                                  createState(statePath, val, {
                                                      name: 'Battery Current',
                                                      type: 'number',
                                                      desc: 'battery current',
                                                      role: 'value',
                                                      read: true,
                                                      write: false,
                                                      unit: 'A',
                                                  });
                                              }
                                              break;
              							case 'heatState':
                                              if (existsState(statePath)) {
                                                  setState(statePath, val, true);
                                              } else {
                                                  createState(statePath, val, {
                                                      name: 'Heat State, 0: Not heating, 1: heating',
                                                      type: 'number',
                                                      role: 'value',
                                                      read: true,
                                                      write: false,
                                                  });
                                              }
                                              break;		
              							case 'maxTemp':
                                              val = (val - 2731) / 10;
                                              if (existsState(statePath)) {
                                                  setState(statePath, val, true);
                                              } else {
                                                  createState(statePath, val, {
                                                      name: 'Highest Akku Temperature',
                                                      type: 'number',
                                                      desc: 'maximum temperature',
                                                      role: 'value',
                                                      read: true,
                                                      write: false,
                                                      unit: '°C',
                                                  });
                                              }
                                              break;
                                          case 'maxVol':
                                              val = val / 100;
                                              if (existsState(statePath)) {
                                                  setState(statePath, val, true);
                                              } else {
                                                  createState(statePath, val, {
                                                      name: 'Highest Cell Voltage',
                                                      type: 'number',
                                                      desc: 'highest cell voltage',
                                                      role: 'value',
                                                      read: true,
                                                      write: false,
                                                      unit: 'V',
                                                  });
                                              }
                                              break;
                                          case 'minVol':
                                              val = val / 100;
                                              if (existsState(statePath)) {
                                                  setState(statePath, val, true);
                                              } else {
                                                  createState(statePath, val, {
                                                      name: 'Lowest Cell Voltage',
                                                      type: 'number',
                                                      desc: 'lowest cell voltage',
                                                      role: 'value',
                                                      read: true,
                                                      write: false,
                                                      unit: 'V',
                                                  });
                                              }
                                              break;
              							case 'packType':
                                              if (existsState(statePath)) {
                                                  setState(statePath, val, true);
                                              } else {
                                                  createState(statePath, val, {
                                                      name: 'packType',
                                                      type: 'number',
                                                      role: 'value',
                                                      read: true,
                                                      write: false,
                                                  });
                                              }
                                              break;
              							case 'power':
                                              if (existsState(statePath)) {
                                                  setState(statePath, val, true);
                                              } else {
                                                  createState(statePath, val, {
                                                      name: 'Battery Power',
                                                      type: 'number',
                                                      role: 'value',
                                                      read: true,
                                                      write: false,
                                                      unit: 'W',
                                                  });
                                              }
              								break;
              							case 'sn':
                                          const batType = getBatteryType(val, pack.model);
                                          // Modelltyp als Datenpunkt setzen/erstellen
                                          const modelPath = `${path}.model`;
                                          if (existsState(modelPath)) {
              								setState(modelPath, batType, true);
                                              } else {
                                                  createState(modelPath, batType, {
                                                  name: 'Battery Model',
                                                  type: 'string',
                                                  role: 'text',
                                                  read: true,
                                                  write: false,
                                                  });
                                                 }
                                              break;
              							case 'socLevel':
                                              if (existsState(statePath)) {
                                                  setState(statePath, val, true);
                                              } else {
                                                  createState(statePath, val, {
                                                      name: 'Battery SoC Level',
                                                      type: 'number',
                                                      role: 'value',
                                                      read: true,
                                                      write: false,
                                                      unit: '%',
                                                  });
                                              }
              								break;
              							case 'softVersion':
                                              if (existsState(statePath)) {
                                                  setState(statePath, val, true);
                                              } else {
                                                  createState(statePath, val, {
                                                      name: 'Battery Software Version',
                                                      type: 'number',
                                                      role: 'value',
                                                      read: true,
                                                      write: false,
                                                  });
                                              }
              								break;
              							case 'state':
                                              if (existsState(statePath)) {
                                                  setState(statePath, val, true);
                                              } else {
                                                  createState(statePath, val, {
                                                      name: 'Battery, 0: Standby, 1: Charging, 2: Discharging',
                                                      type: 'number',
                                                      role: 'value',
                                                      read: true,
                                                      write: false,
                                                  });
                                              }
              								break;	
                                          case 'totalVol':
                                              val = val / 100;
                                              if (existsState(statePath)) {
                                                  setState(statePath, val, true);
                                              } else {
                                                  createState(statePath, val, {
                                                      name: 'Total Voltage',
                                                      type: 'number',
                                                      desc: 'total voltage',
                                                      role: 'value',
                                                      read: true,
                                                      write: false,
                                                      unit: 'V',
                                                  });
                                              }
                                              break;
                                          default:
                                              if (existsState(statePath)) {
                                                  setState(statePath, val, true);
                                              } else {
                                                  createState(statePath, val, {
                                                      name: key,
                                                      type: typeof val,
                                                      role: 'value',
                                                      read: true,
                                                      write: false,
                                                  });
                                              }
                                              break;
                                      }
                                  }
                              }
                          } else {
                              const ts = Date.now();
                              const statePath = `${id}.${i}`;
                              let val = obj[i];
               
                              switch (i) {
                                  case 'BatVolt':
                                      val = val / 100;
                                      if (existsState(statePath)) {
                                          setState(statePath, val, true);
                                      } else {
                                          createState(statePath, val, {
                                              name: 'Battery Voltage',
                                              type: 'number',
                                              desc: 'battery voltage',
                                              role: 'value',
                                              read: true,
                                              write: false,
                                              unit: 'V',
                                          });
                                      }
                                      break;
              					case 'chargeMaxLimit':
                                      if (existsState(statePath)) {
                                          setState(statePath, val, true);
                                      } else {
                                          createState(statePath, val, {
                                              name: 'maximum Charge Limit',
                                              type: 'number',
                                              desc: 'Maximum permissible charging power',
                                              role: 'value',
                                              read: true,
                                              write: false,
                                              unit: 'W',
                                          });
                                      }
                                      break;
                                  case 'acMode':
                                      if (existsState(statePath)) {
                                          setState(statePath, val, true);
                                      } else {
                                          createState(statePath, val, {
                                              name: 'acMode, 1: input mode / 2: output mode',
                                              type: 'number',
                                              desc: '1: charging / 2: discharging',
                                              role: 'value',
                                              read: true,
                                              write: false,
                                          });
                                      }
                                      break;
              					case 'dataReady':
                                      if (existsState(statePath)) {
                                          setState(statePath, val, true);
                                      } else {
                                          createState(statePath, val, {
                                              name: 'Data Ready, 0: Not ready, 1: Ready',
                                              type: 'number',
                                              role: 'value',
                                              read: true,
                                              write: false,
                                          });
                                      }
                                      break;	
              					case 'dcStatus':
                                      if (existsState(statePath)) {
                                          setState(statePath, val, true);
                                      } else {
                                          createState(statePath, val, {
                                              name: 'dcStatus, 0: Stopped, 1: Battery input, 2: Battery output',
                                              type: 'number',
                                              role: 'value',
                                              read: true,
                                              write: false,
                                          });
                                      }
                                      break;						
              					case 'electricLevel':
                                      if (existsState(statePath)) {
                                          setState(statePath, val, true);
                                      } else {
                                          createState(statePath, val, {
                                              name: 'Total Battery Charge Level',
                                              type: 'number',
                                              desc: 'SoC',
                                              role: 'value',
                                              read: true,
                                              write: false,
                                              unit: '%',
                                          });
                                      }
                                      break;
                                  case 'gridInputPower':
                                      if (existsState(statePath)) {
                                          setState(statePath, val, true);
                                      } else {
                                          createState(statePath, val, {
                                              name: 'Grid Input Power to Battery',
                                              type: 'number',
                                              role: 'value',
                                              read: true,
                                              write: false,
                                              unit: 'W',
                                          });
                                      }
                                      break;
                                  case 'gridReverse':
                                      if (existsState(statePath)) {
                                          setState(statePath, val, true);
                                      } else {
                                          createState(statePath, val, {
                                              name: '0: Disabled, 1: Allowed reverse flow, 2: Forbidden reverse flow',
                                              type: 'number',
                                              role: 'value',
                                              read: true,
                                              write: false,
                                          });
                                      }
                                      break;
                                  case 'gridStandard':
                                      if (existsState(statePath)) {
                                          setState(statePath, val, true);
                                      } else {
                                          createState(statePath, val, {
                                              name: 'Grid connection standard 0: Germany 1: France 2: Austria',
                                              type: 'number',
                                              role: 'value',
                                              read: true,
                                              write: false,
                                          });
                                      }
                                      break;
                                  case 'gridState':
                                      if (existsState(statePath)) {
                                          setState(statePath, val, true);
                                      } else {
                                          createState(statePath, val, {
                                              name: 'GridState, 0: Not connected, 1: Connected',
                                              type: 'number',
                                              role: 'value',
                                              read: true,
                                              write: false,
                                          });
                                      }
                                      break;									
                                  case 'heatState':
                                      if (existsState(statePath)) {
                                          setState(statePath, val, true);
                                      } else {
                                          createState(statePath, val, {
                                              name: 'Heat State, 0: Not heating, 1: heating',
                                              type: 'number',
                                              role: 'value',
                                              read: true,
                                              write: false,
                                          });
                                      }
                                      break;		
              					case 'hyperTmp':
                                      val = (val - 2731) / 10.0;
                                      if (existsState(statePath)) {
                                          setState(statePath, val, true);
                                              } else {
                                                  createState(statePath, val, {
                                                  name: 'Enclosure Temperature',
                                                  type: 'number',
                                                  role: 'value',
                                                  read: true,
                                                  write: false,
                                                  unit: '°C',
                                                  });
                                              }
                                      break;
              					case 'inputLimit':
                                      if (existsState(statePath)) {
                                          setState(statePath, val, true);
                                              } else {
                                                  createState(statePath, val, {
                                                  name: 'AC charging power limit to Battery',
                                                  type: 'number',
                                                  role: 'value',
                                                  read: true,
                                                  write: false,
                                                  unit: 'W',
                                                  });
                                              }
                                      break;
              					case 'inverseMaxPower': 
                                      if (existsState(statePath)) {
                                          setState(statePath, val, true);
                                              } else {
                                                  createState(statePath, val, {
                                                  name: 'Device Maximum Output Power Limit',
                                                  type: 'number',
                                                  role: 'value',
                                                  read: true,
                                                  write: false,
                                                  unit: 'W',
                                                  });
                                              }
                                      break;
              					case 'minSoc':
                                      val = val / 10;
                                      if (existsState(statePath)) {
                                          setState(statePath, val, true);
                                              } else {
                                                  createState(statePath, val, {
                                                  name: 'min SoC',
                                                  type: 'number',
                                                  desc: 'minimum Battery SoCset',
                                                  role: 'value',
                                                  read: true,
                                                  write: false,
                                                  unit: '%',
                                                  });
                                              }
                                      break;
              					case 'outputHomePower':
                                      if (existsState(statePath)) {
                                          setState(statePath, val, true);
                                              } else {
              									createState(statePath, val, {
                                                  name: 'outputHomePower',
                                                  type: 'number',
                                                  role: 'value',
                                                  read: true,
                                                  write: false,
              							        unit: 'W',
                                                  });
              								}
                                      break;
              					case 'outputLimit':
                                      if (existsState(statePath)) {
                                          setState(statePath, val, true);
                                              } else {
              									createState(statePath, val, {
                                                  name: 'Output power limit',
                                                  type: 'number',
                                                  role: 'value',
                                                  read: true,
                                                  write: false,
              							        unit: 'W',
                                                  });
              								}
                                      break;
              					case 'outputPackPower':
                                      if (existsState(statePath)) {
                                          setState(statePath, val, true);
                                              } else {
              									createState(statePath, val, {
                                                  name: 'Output power to battery pack (charging)',
                                                  type: 'number',
                                                  role: 'value',
                                                  read: true,
                                                  write: false,
              							        unit: 'W',
                                                  });
              								}
                                      break;
              					case 'packInputPower':
                                      if (existsState(statePath)) {
                                          setState(statePath, val, true);
                                              } else {
              									createState(statePath, val, {
                                                  name: 'Battery pack input power (discharging)',
                                                  type: 'number',
                                                  role: 'value',
                                                  read: true,
                                                  write: false,
              							        unit: 'W',
                                                  });
              								}
                                      break;
              					case 'packNum':
                                      if (existsState(statePath)) {
                                          setState(statePath, val, true);
                                              } else {
              									createState(statePath, val, {
                                                  name: 'Number of battery packs',
                                                  type: 'number',
                                                  role: 'value',
                                                  read: true,
                                                  write: false,
                                                  });
              								}
                                      break;
              					case 'packState':
                                      if (existsState(statePath)) {
                                          setState(statePath, val, true);
                                              } else {
              									createState(statePath, val, {
                                                  name: 'Battery State, 0: Standby, 1: Charging, 2: Discharging',
                                                  type: 'number',
                                                  role: 'value',
                                                  read: true,
                                                  write: false,
                                                  });
              								}
                                      break;
              					case 'pass':
                                      if (existsState(statePath)) {
                                          setState(statePath, val, true);
                                              } else {
              									createState(statePath, val, {
                                                  name: 'Bypass, 0: No, 1: Yes',
                                                  type: 'number',
                                                  role: 'value',
                                                  read: true,
                                                  write: false,
                                                  });
              								}
                                      break;
              					case 'pvStatus':
                                      if (existsState(statePath)) {
                                          setState(statePath, val, true);
                                              } else {
              									createState(statePath, val, {
                                                  name: 'PV State producing, 0: Stopped, 1: Running',
                                                  type: 'number',
                                                  role: 'value',
                                                  read: true,
                                                  write: false,
                                                  });
              								}
                                      break;	
              					case 'remainOutTime':
                                      if (existsState(statePath)) {
                                          setState(statePath, val, true);
                                              } else {
              									createState(statePath, val, {
                                                  name: 'Estimated discharge time in minutes, if not predictable: 59940',
                                                  type: 'number',
                                                  role: 'value',
                                                  read: true,
                                                  write: false,
              									unit: 'min',
                                                  });
              								}
                                      break;		
              					case 'reverseState':
                                      if (existsState(statePath)) {
                                          setState(statePath, val, true);
                                              } else {
              									createState(statePath, val, {
                                                  name: 'Reverse flow, 0: No, 1: Reverse flow',
                                                  type: 'number',
                                                  role: 'value',
                                                  read: true,
                                                  write: false,
                                                  });
              								}
                                      break;			
              					case 'rssi':
                                      if (existsState(statePath)) {
                                          setState(statePath, val, true);
                                              } else {
              									createState(statePath, val, {
                                                  name: 'Received Signal Strength Indicator',
                                                  type: 'number',
                                                  role: 'value',
                                                  read: true,
                                                  write: false,
              									unit: 'dBm',
                                                  });
              								}
                                      break;
              					case 'smartMode':
                                      if (existsState(statePath)) {
                                          setState(statePath, val, true);
                                              } else {
              									createState(statePath, val, {
                                                  name: '1: parameter written to RAM / 0: parameter is written to flash.',
                                                  type: 'number',
                                                  role: 'value',
                                                  read: true,
                                                  write: false,
                                                  });
              								}
                                      break;		
              					case 'socLimit':
                                      if (existsState(statePath)) {
                                          setState(statePath, val, true);
                                              } else {
                                                  createState(statePath, val, {
                                                  name: 'socLimit-Info 0: normal, 1: Charge limit reached, 2: Discharge limit reached',
                                                  type: 'number',
                                                  role: 'value',
                                                  read: true,
                                                  write: false,
                                                  });
                                              }
                                      break;
              					case 'socSet':
                                      val = val / 10;
                                      if (existsState(statePath)) {
                                          setState(statePath, val, true);
                                              } else {
                                                  createState(statePath, val, {
                                                  name: 'max SoC',
                                                  type: 'number',
                                                  desc: 'maximum Battery SoCset',
                                                  role: 'value',
                                                  read: true,
                                                  write: false,
                                                  unit: '%',
                                                  });
                                              }
                                      break;
              					case 'socStatus':
                                      if (existsState(statePath)) {
                                          setState(statePath, val, true);
                                              } else {
                                                  createState(statePath, val, {
                                                  name: 'Info of Calibrating, 0: No / 1: Calibrating',
                                                  type: 'number',
                                                  role: 'value',
                                                  read: true,
                                                  write: false,
                                                  });
                                              }
                                      break;
              					case 'solarInputPower':
                                      if (existsState(statePath)) {
                                          setState(statePath, val, true);
                                              } else {
                                                  createState(statePath, val, {
                                                  name: 'Total Solar Input Power',
                                                  type: 'number',
                                                  role: 'value',
                                                  read: true,
                                                  write: false,
              									unit: 'W',
                                                  });
                                              }
                                      break;
                                  case 'solarPower1':
                                      if (existsState(statePath)) {
                                          setState(statePath, val, true);
                                              } else {
                                                  createState(statePath, val, {
                                                  name: 'Solar line 1 input power',
                                                  type: 'number',
                                                  role: 'value',
                                                  read: true,
                                                  write: false,
              									unit: 'W',
                                                  });
                                              }
                                      break;
                                  case 'solarPower2':
                                      if (existsState(statePath)) {
                                          setState(statePath, val, true);
                                              } else {
                                                  createState(statePath, val, {
                                                  name: 'Solar line 2 input power',
                                                  type: 'number',
                                                  role: 'value',
                                                  read: true,
                                                  write: false,
              									unit: 'W',
                                                  });
                                              }
                                      break;
                                  case 'solarPower3':
                                      if (existsState(statePath)) {
                                          setState(statePath, val, true);
                                              } else {
                                                  createState(statePath, val, {
                                                  name: 'Solar line 3 input power',
                                                  type: 'number',
                                                  role: 'value',
                                                  read: true,
                                                  write: false,
              									unit: 'W',
                                                  });
                                              }
                                      break;
                                  case 'solarPower4':
                                      if (existsState(statePath)) {
                                          setState(statePath, val, true);
                                              } else {
                                                  createState(statePath, val, {
                                                  name: 'Solar line 4 input power',
                                                  type: 'number',
                                                  role: 'value',
                                                  read: true,
                                                  write: false,
              									unit: 'W',
                                                  });
                                              }
                                      break;
                                  case 'solarPower5':
                                      if (existsState(statePath)) {
                                          setState(statePath, val, true);
                                              } else {
                                                  createState(statePath, val, {
                                                  name: 'Solar line 5 input power',
                                                  type: 'number',
                                                  role: 'value',
                                                  read: true,
                                                  write: false,
              									unit: 'W',
                                                  });
                                              }
                                      break;
                                  case 'solarPower6':
                                      if (existsState(statePath)) {
                                          setState(statePath, val, true);
                                              } else {
                                                  createState(statePath, val, {
                                                  name: 'Solar line 6 input power',
                                                  type: 'number',
                                                  role: 'value',
                                                  read: true,
                                                  write: false,
              									unit: 'W',
                                                  });
                                              }
                                      break;
                                  case 'timestamp':
                                      const timestampVal = obj[i];
                                      const timestampFormatted = formatTime(timestampVal);
                                      if (existsState(statePath)) setState(statePath, timestampVal, true);
                                      else createState(statePath, timestampVal, {
                                          name: 'unix timestamp (timestamp)',
                                          type: 'number',
                                          desc: 'unix timestamp in Seconds since Jan 01 1970 (UTC)',
                                          role: 'value',
                                          read: true,
                                          write: false,
                                      });
                                      if (existsState(id + '.timeUpdateTimestamp')) setState(id + '.timeUpdateTimestamp', timestampFormatted, true);
                                      else createState(id + '.timeUpdateTimestamp', timestampFormatted, {
                                          name: 'TimeUpdate (timestamp)',
                                          type: 'string',
                                          desc: 'unix timestamp in readable Format (timestamp)',
                                          read: true,
                                          write: false,
                                      });
                                      break;
                                  case 'ts':
                                      const tsVal = obj[i];
                                      const tsFormatted = formatTime(tsVal);
                                      if (existsState(statePath)) setState(statePath, tsVal, true);
                                      else createState(statePath, tsVal, {
                                          name: 'unix timestamp (ts)',
                                          type: 'number',
                                          desc: 'unix timestamp in Seconds since Jan 01 1970 (UTC)',
                                          role: 'value',
                                          read: true,
                                          write: false,
                                      });
                                      if (existsState(id + '.timeUpdateTs')) setState(id + '.timeUpdateTs', tsFormatted, true);
                                      else createState(id + '.timeUpdateTs', tsFormatted, {
                                          name: 'TimeUpdate (ts)',
                                          type: 'string',
                                          desc: 'unix timestamp in readable Format (ts)',
                                          read: true,
                                          write: false,
                                      });
                                      break;			
                                  default:
                                      if (existsState(statePath)) {
                                          setState(statePath, val, true);
                                      } else {
                                          createState(statePath, val, {
                                              name: i,
                                              type: typeof val,
                                              role: 'value',
                                              read: true,
                                              write: false,
                                          });
                                      }
                                      break;
                              }
                          }
                      }
                  } catch (e) {
                      log(`Error in iter: ${e}`, 'info');
                  }
              }
               
              

              Historisch:
              Frühere Versionen der Scripte sind hier (im Spoiler) zu finden.
              Die kompletten Scripte sind zu groß für diesen Post, daher habe ich sie dort ausgelagert.

              maxclaudiM
              maxclaudiM
              maxclaudi
              schrieb am zuletzt editiert von maxclaudi
              #644

              20.10.2026; 15:18h Fortsetzung von:

              15.10.2026; 16:00 Uhr: Weiterer Bericht zum Betrieb über MQTT-Port 1883 und zenSDK POST #628

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

              Die Fortsetzung ist vermutlich gleichzeitig der Abschluss.

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

              Ich habe meine Analysen abgeschlossen und teile ein paar Ergebnisse mit euch.

              TLS Verbindung Port: 8883
              Das wichtigste Ergebnis vorab:
              Der herkömmliche, vorgesehene "Normal-" Betrieb über TLS ist absolut stabil möglich.

              Es gibt keine Verbindungsabbrüche und keine Reconnects mehr – und das gilt auch für den Mosquitto-Bridge-Betrieb, sofern die eigene lokale IP im Zendure-Gerät (mithilfe meines zURLtool) konfiguriert ist.
              Alles läuft extrem schnell und stabil.
              Für mich gibt es dadurch keinen Grund mehr, einen anderen Weg einzuschlagen - vor allem nicht mit zusätzlichen Optionen (mehr dazu folgt weiter unten).
              Durch die Stabilität und Schnelligkeit bei der jetzigen, regulären Betriebsart (TLS-Port 8883) kann ich keinen Mehrwert mehr über eine alternative Verbindung feststellen.
              Zuvor war der ESP anscheinend auch teils durch die ständige Verarbeitung der Disconnects und Reconnects überlastet.
              Jetzt ist alles super.

              Die ständigen Disconnects/Reconnects der 8883-TLS-Verbindungen - seltsamerweise vom Client zum mosquitto-Broker und vermehrt im Bridge-Betrieb - waren für mich ein Kernproblem.
              Laut Log passierte das ja quasi ununterbrochen im Minutentakt, teilweise sogar mehrfach pro Minute, obwohl man es subjektiv nicht richtig bemerkte.
              Dachte immer, dass der ESP32-C überlastet wäre - jedoch explizit durch was?
              Nur weil TLS verwendet wird und mit 1883 gab es keine Probleme.
              Konnte doch nicht sein...
              Nach tagelanger Analyse habe ich des Rätsels Lösung gefunden.
              Im Nachhinein betrachtet fast trivial – aber mein Reverse Engineering hat sich hier definitiv gelohnt.
              Die zuvor beobachteten ständigen Disconnects/Reconnects und OpenSSL-Fehler, die den ESP unbemerkt auslasteten, sind damit behoben.

              Bisher dachte ich, die kursierenden Informationen der Open-Source-Community zu Bluetooth, MQTT & Co. seien korrekt oder nur minimal fehlerbehaftet, weil sie sich an vielen Stellen fast eins zu eins gleichen.
              Dem ist aber nicht so.
              Scheinbar wurde hier oft nur voneinander abgeschrieben.


              Wenn man weiß, wie es geht, eröffnen sich ein paar sehr interessante Optionen:

              • HTTP-Server komplett abschalten (localAPIenable true/false):
                Damit lässt sich der HTTP-Port bzw. der interne Webserver gezielt ein- oder ausschalten.
                Wenn er deaktiviert ist, kann das zenSDK natürlich leider nicht genutzt werden.
                Den reglementierten, „offiziellen lokalen HA-MQTT“ möchte man aber sowieso eher nicht nutzen.
                Der Vorteil:
                Ist der Server aus, wird der ESP32-C zusätzlich entlastet.

              • Freie SoC-Grenzen:
                Es ist möglich, minSoc (5–98 %) und socSet (6–100 %) völlig frei einzustellen.
                Theoretisch beide von 0–100 %, jedoch muss socSet immer größer sein als minSoc!
                Damit lassen sich beispielsweise der Bypass (pass:0 / pass:2) und die Wirkung von socLimit indirekt steuern.
                Wenn der Webserver eingeschaltet bleibt, funktioniert das auch für zenSDK.
                Außer es wird in einer künftigen Firmware wieder geändert.
                Ich hoffe sehr, dass der Hersteller nichts ändert!
                Die Probleme mit den Verschlimmbesserungen beim SoC-Thema sollte endlich ein Ende finden.
                Hier gibt es bisher überhaupt kein Problem.
                Hoffentlich hört das Reglementieren auf..
                Aktuell getestet mit einigen Geräten <= V2.0.2 und V2.0.4 beim SF2400ACpro.


              Cloud- & Bridge-Modus:

              Bisher läuft alles tadellos über den Mosquitto-Broker im Bridgemodus zur Cloud.
              Das Ganze ist absolut flexibel:

              • Bei Bedarf muss man nur eine einzige Zeile auskommentieren und ist innerhalb von Sekunden entweder komplett lokal oder wieder mit der Cloud verbunden.

              • Das Gleiche gilt für den Bridge-Mode: Man kann sich mit der Cloud verbinden – wahlweise mit oder ohne Schreibrechte für die Cloud.

              • Bei deaktiviertem HEMS hat die Cloud von sich aus (bis jetzt) ausschließlich die Uhrzeit aktualisiert. Gut so.


              HEMS- & NVS-Verschleiß (Flash):

              Ich nutze HEMS selbst zwar nicht, habe es aber nun genauer auf dem Schirm.
              So wie es „für mich aktuell“ aussieht, werden bei der Nutzung von hemsEp sehr wohl manche Parameter direkt in den NVS geschrieben.
              Zwar nicht permanent kontinuierlich, aber bestimmte Keys landen dort.
              Das betrifft nicht nur den Hyper, sondern auch mindestens 1600ACplus und SF2400ACpro, vermutlich alle Geräte.
              Wer glaubt, dass alles ausschließlich im RAM landet, soll das gerne glauben.

              Wie geht es weiter?
              Für meinen Teil muss ich nun noch die Dokumentationen vervollständigen und ordnen.
              Werde noch einmal die Geräte – pro forma erneut, frisch – aufsetzen und im reinen, produktiven Testbetrieb beobachten und loggen.
              Evtl. widme ich mich nun noch etwas dem Thema hemsEp NVS/RAM – hat aber keine Priorität für mich, weil ich es nicht nutzen möchte.
              Weil wir gerade beim Thema NVS sind: smartMode: 1 lässt sich für mich schwerer nachverfolgen als hemsEp.
              Manches kann ich bis jetzt nicht eindeutig nachvollziehen, deshalb äußere ich mich auch nicht dazu.

              Das war es dann.

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

              Wie bei einem langweiligen Film: Endlich Ende! :-)

              edit 21.10.26; 10:35h
              Keine Tester mehr gesucht.
              Tester und Nuzer denen ich mit mosquitto broker half, bitte per PM oder email melden. Danke.

              Testers and users whom I have assisted while they were working with the Mosquitto broker: please get in touch with me via pm or email. Thanks.

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

              neue Zendure Geräte Cloudless: zURLtool V2.0

              D maxclaudiM 2 Antworten Letzte Antwort
              3
              • maxclaudiM maxclaudi

                20.10.2026; 15:18h Fortsetzung von:

                15.10.2026; 16:00 Uhr: Weiterer Bericht zum Betrieb über MQTT-Port 1883 und zenSDK POST #628

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

                Die Fortsetzung ist vermutlich gleichzeitig der Abschluss.

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

                Ich habe meine Analysen abgeschlossen und teile ein paar Ergebnisse mit euch.

                TLS Verbindung Port: 8883
                Das wichtigste Ergebnis vorab:
                Der herkömmliche, vorgesehene "Normal-" Betrieb über TLS ist absolut stabil möglich.

                Es gibt keine Verbindungsabbrüche und keine Reconnects mehr – und das gilt auch für den Mosquitto-Bridge-Betrieb, sofern die eigene lokale IP im Zendure-Gerät (mithilfe meines zURLtool) konfiguriert ist.
                Alles läuft extrem schnell und stabil.
                Für mich gibt es dadurch keinen Grund mehr, einen anderen Weg einzuschlagen - vor allem nicht mit zusätzlichen Optionen (mehr dazu folgt weiter unten).
                Durch die Stabilität und Schnelligkeit bei der jetzigen, regulären Betriebsart (TLS-Port 8883) kann ich keinen Mehrwert mehr über eine alternative Verbindung feststellen.
                Zuvor war der ESP anscheinend auch teils durch die ständige Verarbeitung der Disconnects und Reconnects überlastet.
                Jetzt ist alles super.

                Die ständigen Disconnects/Reconnects der 8883-TLS-Verbindungen - seltsamerweise vom Client zum mosquitto-Broker und vermehrt im Bridge-Betrieb - waren für mich ein Kernproblem.
                Laut Log passierte das ja quasi ununterbrochen im Minutentakt, teilweise sogar mehrfach pro Minute, obwohl man es subjektiv nicht richtig bemerkte.
                Dachte immer, dass der ESP32-C überlastet wäre - jedoch explizit durch was?
                Nur weil TLS verwendet wird und mit 1883 gab es keine Probleme.
                Konnte doch nicht sein...
                Nach tagelanger Analyse habe ich des Rätsels Lösung gefunden.
                Im Nachhinein betrachtet fast trivial – aber mein Reverse Engineering hat sich hier definitiv gelohnt.
                Die zuvor beobachteten ständigen Disconnects/Reconnects und OpenSSL-Fehler, die den ESP unbemerkt auslasteten, sind damit behoben.

                Bisher dachte ich, die kursierenden Informationen der Open-Source-Community zu Bluetooth, MQTT & Co. seien korrekt oder nur minimal fehlerbehaftet, weil sie sich an vielen Stellen fast eins zu eins gleichen.
                Dem ist aber nicht so.
                Scheinbar wurde hier oft nur voneinander abgeschrieben.


                Wenn man weiß, wie es geht, eröffnen sich ein paar sehr interessante Optionen:

                • HTTP-Server komplett abschalten (localAPIenable true/false):
                  Damit lässt sich der HTTP-Port bzw. der interne Webserver gezielt ein- oder ausschalten.
                  Wenn er deaktiviert ist, kann das zenSDK natürlich leider nicht genutzt werden.
                  Den reglementierten, „offiziellen lokalen HA-MQTT“ möchte man aber sowieso eher nicht nutzen.
                  Der Vorteil:
                  Ist der Server aus, wird der ESP32-C zusätzlich entlastet.

                • Freie SoC-Grenzen:
                  Es ist möglich, minSoc (5–98 %) und socSet (6–100 %) völlig frei einzustellen.
                  Theoretisch beide von 0–100 %, jedoch muss socSet immer größer sein als minSoc!
                  Damit lassen sich beispielsweise der Bypass (pass:0 / pass:2) und die Wirkung von socLimit indirekt steuern.
                  Wenn der Webserver eingeschaltet bleibt, funktioniert das auch für zenSDK.
                  Außer es wird in einer künftigen Firmware wieder geändert.
                  Ich hoffe sehr, dass der Hersteller nichts ändert!
                  Die Probleme mit den Verschlimmbesserungen beim SoC-Thema sollte endlich ein Ende finden.
                  Hier gibt es bisher überhaupt kein Problem.
                  Hoffentlich hört das Reglementieren auf..
                  Aktuell getestet mit einigen Geräten <= V2.0.2 und V2.0.4 beim SF2400ACpro.


                Cloud- & Bridge-Modus:

                Bisher läuft alles tadellos über den Mosquitto-Broker im Bridgemodus zur Cloud.
                Das Ganze ist absolut flexibel:

                • Bei Bedarf muss man nur eine einzige Zeile auskommentieren und ist innerhalb von Sekunden entweder komplett lokal oder wieder mit der Cloud verbunden.

                • Das Gleiche gilt für den Bridge-Mode: Man kann sich mit der Cloud verbinden – wahlweise mit oder ohne Schreibrechte für die Cloud.

                • Bei deaktiviertem HEMS hat die Cloud von sich aus (bis jetzt) ausschließlich die Uhrzeit aktualisiert. Gut so.


                HEMS- & NVS-Verschleiß (Flash):

                Ich nutze HEMS selbst zwar nicht, habe es aber nun genauer auf dem Schirm.
                So wie es „für mich aktuell“ aussieht, werden bei der Nutzung von hemsEp sehr wohl manche Parameter direkt in den NVS geschrieben.
                Zwar nicht permanent kontinuierlich, aber bestimmte Keys landen dort.
                Das betrifft nicht nur den Hyper, sondern auch mindestens 1600ACplus und SF2400ACpro, vermutlich alle Geräte.
                Wer glaubt, dass alles ausschließlich im RAM landet, soll das gerne glauben.

                Wie geht es weiter?
                Für meinen Teil muss ich nun noch die Dokumentationen vervollständigen und ordnen.
                Werde noch einmal die Geräte – pro forma erneut, frisch – aufsetzen und im reinen, produktiven Testbetrieb beobachten und loggen.
                Evtl. widme ich mich nun noch etwas dem Thema hemsEp NVS/RAM – hat aber keine Priorität für mich, weil ich es nicht nutzen möchte.
                Weil wir gerade beim Thema NVS sind: smartMode: 1 lässt sich für mich schwerer nachverfolgen als hemsEp.
                Manches kann ich bis jetzt nicht eindeutig nachvollziehen, deshalb äußere ich mich auch nicht dazu.

                Das war es dann.

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

                Wie bei einem langweiligen Film: Endlich Ende! :-)

                edit 21.10.26; 10:35h
                Keine Tester mehr gesucht.
                Tester und Nuzer denen ich mit mosquitto broker half, bitte per PM oder email melden. Danke.

                Testers and users whom I have assisted while they were working with the Mosquitto broker: please get in touch with me via pm or email. Thanks.

                D
                D
                Daniel 8
                schrieb am zuletzt editiert von
                #645

                @maxclaudi sagte:

                TLS Verbindung Port: 8883
                Das wichtigste Ergebnis vorab:
                Der herkömmliche, vorgesehene "Normal-" Betrieb über TLS ist absolut stabil möglich.

                Kann man den Port über die app Option einfach so einstellen?

                Das heißt mit dem läuft alles schneller und über mqtt. Dann bräuchte man Zensdk gar Nicht mehr.

                Irgendwie übersteigt das ganze hier jetzt mein können.

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

                maxclaudiM 1 Antwort Letzte Antwort
                1
                • D Daniel 8

                  @maxclaudi sagte:

                  TLS Verbindung Port: 8883
                  Das wichtigste Ergebnis vorab:
                  Der herkömmliche, vorgesehene "Normal-" Betrieb über TLS ist absolut stabil möglich.

                  Kann man den Port über die app Option einfach so einstellen?

                  Das heißt mit dem läuft alles schneller und über mqtt. Dann bräuchte man Zensdk gar Nicht mehr.

                  Irgendwie übersteigt das ganze hier jetzt mein können.

                  maxclaudiM
                  maxclaudiM
                  maxclaudi
                  schrieb am zuletzt editiert von
                  #646

                  @Daniel-8 sagte:

                  @maxclaudi sagte:

                  TLS Verbindung Port: 8883
                  Das wichtigste Ergebnis vorab:
                  Der herkömmliche, vorgesehene "Normal-" Betrieb über TLS ist absolut stabil möglich.

                  Kann man den Port über die app Option einfach so einstellen?

                  Um es verständlicher zu erklären: Es geht hier um zwei unterschiedliche MQTT-Verbindungen.

                  Port 1883 ohne TLS:
                  Man kann den Port 1883 ohne TLS am Gerät und in der App (gewusst wie) freischalten und aktivieren.
                  Ab dann ist das Gerät über Port 1883 mit einer DNS-Adresse verbunden.
                  Diese DNS-Adresse muss per AdGuard, Pi-hole oder über einen eigenen DNS-Server auf den lokalen Broker umgebogen werden, wenn man lokal über die Datenpunkte steuern möchte.
                  Aber bei dieser Verbindung sind zu viele - ich nenne es mal - Möglichkeiten vorhanden und sollte m. M. n. nur kurzzeitig verwendet werden, näher gehe ich darauf nicht ein.


                  Port 8883 mit TLS:
                  Das ist der Standard.
                  So verbindet sich jedes Gerät.
                  Das Gerät ist über Port 8883 mit einer DNS-Adresse verbunden.
                  Diese DNS-Adresse kann ebenfalls per AdGuard, Pi-hole oder DNS-Server auf die IP des lokalen Brokers umgebogen werden, wenn man lokal über die Datenpunkte steuern möchte.

                  Alternativ:
                  Man konfiguriert die Variable für DNS/IP auf dem Gerät mit dem zURLtool um auf den lokalen Broker.
                  Auf dem lokalen Broker (z.B. Mosquitto) kann zusätzlich eine Bridge eingerichtet werden, damit das Gerät mit der Cloud verbunden bleibt.
                  Es ist in wenigen Sekunden erledigt, diese Bridge wahlweise abzuschalten oder der Cloud nur Lesezugriffe oder vollen Zugriff zu erlauben.

                  Mit einem lokalen Broker über MQTT ist die Steuerung zuverlässig, stabil und schneller als über die Cloud oder die lokale API (also auch zenSDK).

                  zenSDK macht nach meinen Analysen und Tests nur einen kleinen Unterschied, solange kein erhöhter HTTP(S)-Traffic im jeweiligen IP-Bereich bzw. Netzwerk vorliegt.
                  Wenn diese Voraussetzung gegeben ist und ein Abfrageintervall (Get) von etwa 4000 ms stabil möglich ist, kann auch das zenSDK genutzt werden.
                  Es ist dann nur unwesentlich langsamer als MQTT.

                  Der Grund dafür ist hardwarebedingt.
                  Auch wenn MQTT durch den schnelleren, automatischen Bericht-Versand (Report Publish) vom Gerät und das schnellere Publizieren neuer Werte klar im Vorteil ist, benötigt das Gerät hardwarebedingt Zeit.
                  Eine Limit-Umschaltung von inputLimit oder outputLimit braucht beispielsweise Zeit, um den Zielwert vollständig umzusetzen.

                  Einfacher Vergleich:
                  Du kannst bei einem Auto mit Automatikgetriebe und Turbolader zwar das Gaspedal oder die Bremse voll durchtreten, aber es dauert dennoch eine gewisse Zeit, bis die Höchstgeschwindigkeit oder der Stillstand erreicht sind.

                  Alle Klarheiten beseitigt? ;-)


                  Das heißt mit dem läuft alles schneller und über mqtt. Dann bräuchte man Zensdk gar Nicht mehr.

                  Richtig.
                  zenSDK ist, wie erklärt, unter den passenden Voraussetzungen langsamer als der lokale Broker über MQTT.
                  Eigentlich ist MQTT vorzuziehen, da hier nicht auf neue Werte gepollt werden muss, sondern diese sofort automatisch publiziert werden.
                  Zudem kann die lokale API abgeschaltet werden (gewusst wie) und der ESP32-C wird dadurch weiter entlastet.

                  22.10.2026:
                  ESP32-C, das ist übrigens die Standard-CPU, die verwendet wird und noch nirgendwo offiziell weltweit dokumentiert wurde.

                  Irgendwie übersteigt das ganze hier jetzt mein können.

                  Jetzt nicht mehr ;-)

                  Hinweis:
                  Ich überlege gerade ein MQTT-Steurungs-Script anzupassen, damit es als Ersatz für das zenSDK-Steuerungs-Script verwendet werden kann.
                  Damit bleiben alle Datenpunkte identisch, wenn man es gleich konfiguriert wie das zenSDK-Steuerungs-Script.
                  Bei identischer Benennung und Konfiguration, muss man darauf achten dass nur 1 Steuerungs-Script aktiv ist.

                  Bei unterschiedlicher Benennung kann zenSDK und MQTT gleichzeitig, simultan genutzt werden oder auch nur eins von beiden als vorrangiges Steuerungsscripts.

                  MQTT liefer natürlich einiges mehr an Infos.

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

                  neue Zendure Geräte Cloudless: zURLtool V2.0

                  P 1 Antwort Letzte Antwort
                  2
                  • D
                    D
                    Daniel 8
                    schrieb am zuletzt editiert von
                    #647

                    Vielen Dank für die ausführlichen Antworten.

                    Dann belasse ich es mal bei der Steuerung über zenSDK.

                    Ich Kämpfe seit 3 Tagen mit einem Problem wo ich nicht weiß woher das kommt.
                    Immer um 6.57 Uhr bringt das Script die Meldung Das ich meine IP Adresse überprüfen soll.

                    Und heute hat irgendwie mein Steuerungsscript komplett verrückt gespielt und mal alles ins Netz eingespeist. Das lief bis jetzt ohne Probleme schon eine Weile. Wobei ich heute auch das Firmware Update auf 2
                    0.5 gemacht habe.

                    War leider arbeiten und konnte nicht schauen warum dies heute so passiert ist.

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

                    maxclaudiM 1 Antwort Letzte Antwort
                    0
                    • D Daniel 8

                      Vielen Dank für die ausführlichen Antworten.

                      Dann belasse ich es mal bei der Steuerung über zenSDK.

                      Ich Kämpfe seit 3 Tagen mit einem Problem wo ich nicht weiß woher das kommt.
                      Immer um 6.57 Uhr bringt das Script die Meldung Das ich meine IP Adresse überprüfen soll.

                      Und heute hat irgendwie mein Steuerungsscript komplett verrückt gespielt und mal alles ins Netz eingespeist. Das lief bis jetzt ohne Probleme schon eine Weile. Wobei ich heute auch das Firmware Update auf 2
                      0.5 gemacht habe.

                      War leider arbeiten und konnte nicht schauen warum dies heute so passiert ist.

                      maxclaudiM
                      maxclaudiM
                      maxclaudi
                      schrieb am zuletzt editiert von
                      #648

                      @Daniel-8 sagte:
                      Immer um 6.57 Uhr bringt das Script die Meldung Das ich meine IP Adresse überprüfen soll.

                      Wenn das täglich zur gleichen Uhrzeit vorkommt:
                      Prüfe mal deinen WLAN-Router oder WLAN-AP, ob dort ein täglicher Neustart konfiguriert ist.

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

                      neue Zendure Geräte Cloudless: zURLtool V2.0

                      D 1 Antwort Letzte Antwort
                      0
                      • maxclaudiM maxclaudi

                        @Daniel-8 sagte:
                        Immer um 6.57 Uhr bringt das Script die Meldung Das ich meine IP Adresse überprüfen soll.

                        Wenn das täglich zur gleichen Uhrzeit vorkommt:
                        Prüfe mal deinen WLAN-Router oder WLAN-AP, ob dort ein täglicher Neustart konfiguriert ist.

                        D
                        D
                        Daniel 8
                        schrieb am zuletzt editiert von Daniel 8
                        #649

                        @maxclaudi sagte:

                        @Daniel-8 sagte:
                        Immer um 6.57 Uhr bringt das Script die Meldung Das ich meine IP Adresse überprüfen soll.

                        Wenn das täglich zur gleichen Uhrzeit vorkommt:
                        Prüfe mal deinen WLAN-Router oder WLAN-AP, ob dort ein täglicher Neustart konfiguriert ist.

                        Ist nicht immer ganz genau zur gleichen Zeit. So ca. Plus minus 6 Minuten.

                        Nein habe nichts programmiert. Ich werde mal beobachten. Jetzt ist ja zumindest der Speicher mal neu gestartet mit der neuen Firmware.
                        Und so werde ich schritt für schritt testen. Habe ja auch nichts verändert. Deswegen verstehe ich es nicht warum es jetzt so ist.

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

                        maxclaudiM 1 Antwort Letzte Antwort
                        -1
                        • D Daniel 8

                          @maxclaudi sagte:

                          @Daniel-8 sagte:
                          Immer um 6.57 Uhr bringt das Script die Meldung Das ich meine IP Adresse überprüfen soll.

                          Wenn das täglich zur gleichen Uhrzeit vorkommt:
                          Prüfe mal deinen WLAN-Router oder WLAN-AP, ob dort ein täglicher Neustart konfiguriert ist.

                          Ist nicht immer ganz genau zur gleichen Zeit. So ca. Plus minus 6 Minuten.

                          Nein habe nichts programmiert. Ich werde mal beobachten. Jetzt ist ja zumindest der Speicher mal neu gestartet mit der neuen Firmware.
                          Und so werde ich schritt für schritt testen. Habe ja auch nichts verändert. Deswegen verstehe ich es nicht warum es jetzt so ist.

                          maxclaudiM
                          maxclaudiM
                          maxclaudi
                          schrieb am zuletzt editiert von
                          #650

                          @Daniel-8 sagte:
                          Ist nicht immer ganz genau zur gleichen Zeit. So ca. Plus minus 6 Minuten.

                          Nein habe nichts programmiert. Ich werde mal beobachten. Jetzt ist ja zumindest der Speicher mal neu gestartet mit der neuen Firmware.
                          Und so werde ich schritt für schritt testen. Habe ja auch nichts verändert. Deswegen verstehe ich es nicht warum es jetzt so ist.

                          Ich würde es dennoch nicht ausschließen und überprüfen.
                          Das gilt vor allem auch für sogenannte Repeater, falls einer im Einsatz ist.

                          Repeater sind für solche Verbindungen nicht optimal.
                          Besser ist es, einen Access Point (AP) zu verwenden oder den WLAN-Router so nah wie möglich an den Geräten zu platzieren.

                          Ich hatte mal bei einem Bekannten exakt den gleichen Fehler ausfindig gemacht:
                          Im AP war ab Werk bzw. als Standard ein täglicher Neustart hinterlegt, ohne dass er das selbst aktiviert hatte.
                          Der AP war nach einem Firmware-Update automatisch auf diesen täglichen Neustart konfiguriert worden.

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

                          neue Zendure Geräte Cloudless: zURLtool V2.0

                          1 Antwort Letzte Antwort
                          1
                          • D
                            D
                            Daniel 8
                            schrieb am zuletzt editiert von
                            #651

                            Ich habe alles geprüft kein Neustart. Bin noch auf der Suche. Informationen folgen wenn ich näheres weiß.
                            Der zendure meldet sich auf jedenfall im wlan ab und erst wieder an so sah es zumindest aus, wenn er durch die Sonnenstrahlen geweckt wird.
                            Als wie er nach einer bestimmten Zeit in einen Tiefschlaf gehen würde. Werde dies beobachten.

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

                            maxclaudiM 1 Antwort Letzte Antwort
                            0
                            • D Daniel 8

                              Ich habe alles geprüft kein Neustart. Bin noch auf der Suche. Informationen folgen wenn ich näheres weiß.
                              Der zendure meldet sich auf jedenfall im wlan ab und erst wieder an so sah es zumindest aus, wenn er durch die Sonnenstrahlen geweckt wird.
                              Als wie er nach einer bestimmten Zeit in einen Tiefschlaf gehen würde. Werde dies beobachten.

                              maxclaudiM
                              maxclaudiM
                              maxclaudi
                              schrieb am zuletzt editiert von
                              #652

                              @Daniel-8
                              Könnte Firmwarebedingt sein oder mit dem empfindlichen WLAN des esp zu tun haben.

                              Meine WLAN Verbindung ist eigentlich gut genug:

                              image.jpeg

                              Dennoch habe ich auch 1x am Tag ein reconnect vom Zendure-Gerät.

                              image.jpeg

                              LOG:
                              Der 2400ACpro hat vermutlich in dieser Sekunde kurzzeitig die Verbindung zum Netzwerk verloren.
                              Beim sofortigen Wiederverbindungsversuch lief die alte Sitzung im Mosquitto-Broker noch, weshalb der Broker die alte Verbindung gekappt hat, um die neue Anmeldung des Geräts zuzulassen.

                              Ich könnte nun auch versuchen herauszufinden, ob zu dieser Uhrzeit (01:35 Uhr) eventuell ein geplanter Neustart des Geräts stattgefunden hat.

                              Aber weil es so kurz ist und sonst alles tadellos funktioniert, werde ich erst mal nur das LOG im Auge behalten.

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

                              neue Zendure Geräte Cloudless: zURLtool V2.0

                              1 Antwort Letzte Antwort
                              0
                              • maxclaudiM maxclaudi

                                @Daniel-8 sagte:

                                @maxclaudi sagte:

                                TLS Verbindung Port: 8883
                                Das wichtigste Ergebnis vorab:
                                Der herkömmliche, vorgesehene "Normal-" Betrieb über TLS ist absolut stabil möglich.

                                Kann man den Port über die app Option einfach so einstellen?

                                Um es verständlicher zu erklären: Es geht hier um zwei unterschiedliche MQTT-Verbindungen.

                                Port 1883 ohne TLS:
                                Man kann den Port 1883 ohne TLS am Gerät und in der App (gewusst wie) freischalten und aktivieren.
                                Ab dann ist das Gerät über Port 1883 mit einer DNS-Adresse verbunden.
                                Diese DNS-Adresse muss per AdGuard, Pi-hole oder über einen eigenen DNS-Server auf den lokalen Broker umgebogen werden, wenn man lokal über die Datenpunkte steuern möchte.
                                Aber bei dieser Verbindung sind zu viele - ich nenne es mal - Möglichkeiten vorhanden und sollte m. M. n. nur kurzzeitig verwendet werden, näher gehe ich darauf nicht ein.


                                Port 8883 mit TLS:
                                Das ist der Standard.
                                So verbindet sich jedes Gerät.
                                Das Gerät ist über Port 8883 mit einer DNS-Adresse verbunden.
                                Diese DNS-Adresse kann ebenfalls per AdGuard, Pi-hole oder DNS-Server auf die IP des lokalen Brokers umgebogen werden, wenn man lokal über die Datenpunkte steuern möchte.

                                Alternativ:
                                Man konfiguriert die Variable für DNS/IP auf dem Gerät mit dem zURLtool um auf den lokalen Broker.
                                Auf dem lokalen Broker (z.B. Mosquitto) kann zusätzlich eine Bridge eingerichtet werden, damit das Gerät mit der Cloud verbunden bleibt.
                                Es ist in wenigen Sekunden erledigt, diese Bridge wahlweise abzuschalten oder der Cloud nur Lesezugriffe oder vollen Zugriff zu erlauben.

                                Mit einem lokalen Broker über MQTT ist die Steuerung zuverlässig, stabil und schneller als über die Cloud oder die lokale API (also auch zenSDK).

                                zenSDK macht nach meinen Analysen und Tests nur einen kleinen Unterschied, solange kein erhöhter HTTP(S)-Traffic im jeweiligen IP-Bereich bzw. Netzwerk vorliegt.
                                Wenn diese Voraussetzung gegeben ist und ein Abfrageintervall (Get) von etwa 4000 ms stabil möglich ist, kann auch das zenSDK genutzt werden.
                                Es ist dann nur unwesentlich langsamer als MQTT.

                                Der Grund dafür ist hardwarebedingt.
                                Auch wenn MQTT durch den schnelleren, automatischen Bericht-Versand (Report Publish) vom Gerät und das schnellere Publizieren neuer Werte klar im Vorteil ist, benötigt das Gerät hardwarebedingt Zeit.
                                Eine Limit-Umschaltung von inputLimit oder outputLimit braucht beispielsweise Zeit, um den Zielwert vollständig umzusetzen.

                                Einfacher Vergleich:
                                Du kannst bei einem Auto mit Automatikgetriebe und Turbolader zwar das Gaspedal oder die Bremse voll durchtreten, aber es dauert dennoch eine gewisse Zeit, bis die Höchstgeschwindigkeit oder der Stillstand erreicht sind.

                                Alle Klarheiten beseitigt? ;-)


                                Das heißt mit dem läuft alles schneller und über mqtt. Dann bräuchte man Zensdk gar Nicht mehr.

                                Richtig.
                                zenSDK ist, wie erklärt, unter den passenden Voraussetzungen langsamer als der lokale Broker über MQTT.
                                Eigentlich ist MQTT vorzuziehen, da hier nicht auf neue Werte gepollt werden muss, sondern diese sofort automatisch publiziert werden.
                                Zudem kann die lokale API abgeschaltet werden (gewusst wie) und der ESP32-C wird dadurch weiter entlastet.

                                22.10.2026:
                                ESP32-C, das ist übrigens die Standard-CPU, die verwendet wird und noch nirgendwo offiziell weltweit dokumentiert wurde.

                                Irgendwie übersteigt das ganze hier jetzt mein können.

                                Jetzt nicht mehr ;-)

                                Hinweis:
                                Ich überlege gerade ein MQTT-Steurungs-Script anzupassen, damit es als Ersatz für das zenSDK-Steuerungs-Script verwendet werden kann.
                                Damit bleiben alle Datenpunkte identisch, wenn man es gleich konfiguriert wie das zenSDK-Steuerungs-Script.
                                Bei identischer Benennung und Konfiguration, muss man darauf achten dass nur 1 Steuerungs-Script aktiv ist.

                                Bei unterschiedlicher Benennung kann zenSDK und MQTT gleichzeitig, simultan genutzt werden oder auch nur eins von beiden als vorrangiges Steuerungsscripts.

                                MQTT liefer natürlich einiges mehr an Infos.

                                P
                                P
                                petman
                                schrieb am zuletzt editiert von
                                #653

                                @maxclaudi sagte:

                                Ich überlege gerade ein MQTT-Steurungs-Script anzupassen, damit es als Ersatz für das zenSDK-Steuerungs-Script verwendet werden kann.

                                ja Bitte, und da gleich noch die Berechnung Energie- Tages-, Wochen-, Monats- Jahres-ertrag mit rein.

                                Ich weis, der Sourceanalytix kann das, ist aber immer noch BETA.

                                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
                                0
                                • P petman

                                  @maxclaudi sagte:

                                  Ich überlege gerade ein MQTT-Steurungs-Script anzupassen, damit es als Ersatz für das zenSDK-Steuerungs-Script verwendet werden kann.

                                  ja Bitte, und da gleich noch die Berechnung Energie- Tages-, Wochen-, Monats- Jahres-ertrag mit rein.

                                  Ich weis, der Sourceanalytix kann das, ist aber immer noch BETA.

                                  Peter

                                  maxclaudiM
                                  maxclaudiM
                                  maxclaudi
                                  schrieb am zuletzt editiert von
                                  #654

                                  @petman sagte:

                                  @maxclaudi sagte:

                                  Ich überlege gerade ein MQTT-Steurungs-Script anzupassen, damit es als Ersatz für das zenSDK-Steuerungs-Script verwendet werden kann.

                                  ja Bitte,

                                  Soweit fertig.
                                  Das MQTT-JS-Steuerungsskript (mqttSteuerung_zenSDK_Angleich.js) ist fertig und liefert einige Datenpunkte (Möglichkeiten) mehr. Struktur und Datenpunkte sind ansonsten exakt identisch mit dem zenSDK-JS-Steuerungsskript.

                                  und da gleich noch die Berechnung Energie- Tages-, Wochen-, Monats- Jahres-ertrag mit rein.

                                  Dazu habe ich ein separates JS-Skript (zendure_energy.js) geschrieben, damit es wahlweise genutzt werden kann. Die Datenpunkte liefern die Werte direkt in ioBroker. Zusätzlich kann man aktivieren, dass die Werte in CSV-Dateien geschrieben und gesichert werden – aufgeteilt nach Tag, Monat und Jahr.
                                  Das Skript funktioniert, sollte jedoch dauerhaft aktiv sein, da die Werte sonst unvollständig sind. Das CSV-Format bietet sich ideal für den Import in Excel, Grafana oder andere Systeme an.

                                  Bitte melde dich mal bei mir.

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

                                  neue Zendure Geräte Cloudless: zURLtool V2.0

                                  1 Antwort Letzte Antwort
                                  0
                                  • maxclaudiM maxclaudi

                                    zURLtool V 1.0 dch – Lokaler MQTT-Broker für Zendure 2026

                                    Hallo zusammen,das zURLtool V 1.0 dch ist nun fertig!
                                    Ich habe in den letzten Monaten eine eigene Bibliothek sowie eine komfortable grafische Oberfläche (GUI) für Windows entwickelt.

                                    Das Programm ist in seiner Art einzigartig, neu und durch monatelanges Reverse-Engineering, Analysen und intensives Testen entstanden.
                                    Es handelt sich um ein reines Windows-GUI-Programm, das ganz einfach per Doppelklick gestartet wird.
                                    Es verwendet keinen Code von anderen bekannten Projekten (wie dem Solarflow Bluetooth Manager oder dem zendure-cloud-disconnector).


                                    Technische Voraussetzungen & Basis:

                                    Das Programm nutzt zur Kommunikation ausschließlich die offiziellen Windows-BT-Treiber ab Windows 11 und die integrierte Bluetooth-Bibliothek bleak:


                                    LICENSE NOTICE FOR THE UTILIZED BLUETOOTH LIBRARY


                                    This program uses "bleak" for Bluetooth functionality.
                                    https://github.com
                                    Copyright (c) 2020, Henrik Blidh
                                    Permission is hereby granted, free of charge, to any person obtaining a copy
                                    of this software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND.


                                    Die Bibliothek wurde fest in das Programm eingebunden.
                                    Weitere Bibliotheken oder Frameworks müssen auf eurem PC nicht installiert werden.

                                    • Betriebssystem: Windows 11 (64-Bit)
                                    • Hardware: Interner Bluetooth-Chip oder ein standardmäßiger USB-Bluetooth-Stick am Rechner.
                                    • Kompatibilität: Getestet mit Zendure-Geräten bis zur 2400er-Serie (sowohl ältere als auch neueste Generationen / Firmware-Stände).

                                    Inhalt des Programmpakets (zURLtool.zip):
                                    Das ZIP-Archiv enthält genau drei Dateien, die zwingend im selben Ordner liegen bleiben müssen:

                                    • zURLtool_GUI.exe (Größe: ca. 11.6 MB / 11.921 KB)
                                    • config.ini (Konfigurationsdatei für eure Zugangsdaten)
                                    • ReadMe_zURLtool.txt (Ausführliche Anleitung in Deutsch & Englisch)

                                    Was genau macht das zURLtool?

                                    Das Tool schreibt im Zendure-Gerät die Konfiguration der Broker-URL um.

                                    • Es wird NICHT in die Firmware eingegriffen und es wird nichts modifiziert. Die originale Firmware von Zendure bleibt zu 100 % unangetastet.

                                    • Es werden lediglich die standardmäßigen Bluetooth-Befehle verwendet, die die Firmware von Haus aus für die App bereitstellt.

                                    • Das Skript verhält sich für das Gerät exakt wie die originale App, greift auf die Konfigurations-Variablen im Speicher zu und überschreibt dort die Wi-Fi-Daten und den Eintrag für die Cloud-URL mit der IP eures eigenen lokalen Brokers.

                                    Es funktioniert quasi wie eine zusätzliche Länderauswahl, bei der man sich einfach mit einem anderen Server verbindet.
                                    Das bietet die Firmware von Haus aus an, da die Geräte sonst permanent fest an ganz bestimmte AWS-Server gebunden wären.

                                    Zurück zur Cloud ist auch möglich. Beispiele sind in der config.ini enthalten.

                                    Die Geräte verbinden sich und können beschrieben werden ohne hartes Reset und ohne hartes Trennen von allen Stromquellen.


                                    Warum ist das Programm Closed Source?

                                    Zu oft wurden meine Skripte, Codes oder Teile daraus in der Vergangenheit verwendet und/oder modifiziert – ohne Nennung meines Namens, ohne Danksagung / Anerkennung und unter Entfernung meiner originalen Hinweise.

                                    Es wurde u. a. als eigener Erfolg ausgegeben oder kommentiert mit
                                    "ich habe einen Weg gefunden".
                                    Das ist ungerecht.
                                    Monatelange harte Arbeit sollte zumindest anerkannt werden.

                                    Sollte das Programm oder der Code in irgendeiner Form gehackt zur Verfügung gestellt oder verwendet werden, dann war dies mein letzter offizieller Beitrag in Form von Code für die Community.


                                    edit/update 16.08.26 Freigabe vereinfacht.
                                    Der Freischalt-Ablauf:

                                    Damit das Programm funktioniert und um anonyme Massen-Weiterreichungen zu unterbinden, benötigt das Programm eine Seriennummer und Freigabe-Code.

                                    1. Sende mir deinen (Foren-) Namen per privater Nachricht (PN) im Forum oder per E-Mail an: ztool.maxclaudi AT gmail . com

                                    2. Du erhältst von mir eine persönliche "Seriennummer" und den dazu passenden "Freigabe-Code"

                                    3. Sichere dir die Seriennummer und den Freigabe-Code unbedingt in einer Textdatei als Backup!
                                      Falls du die Datei mal verliert, hast du deine Seriennummer und Freigabe-Code als Backup griffbereit.

                                    4. Starte die 'zURLtool_GUI.exe'.
                                      Trage beide Werte exakt so, wie du sie erhalten hast, in die dafür vorgesehenen Felder im Startfenster ein und klicke auf "Akzeptieren & Starten".
                                      Nach einmaliger Eingabe startet zURLtool zukünftig vollautomatisch in die Haupt-Oberfläche.
                                      Als Notfall hast du zusätzlich Seriennummer und Freigabecode als Backup.


                                    Vor dem ersten Start muss die config.ini in einem einfachen Text-Editor mit euren echten Daten befüllt werden.

                                    Auswählbare Beispiele und Erklärungen zu den WLAN-Modi sind direkt darin enthalten.

                                    02.png

                                    Bilder im Spoiler sagen mehr als Worte, ich denke die Screenshots sind selbsterklärend:



                                    00.png

                                    _01.png

                                    02.png

                                    03.png

                                    04.png

                                    05.png


                                    WICHTIGER HINWEIS ZU DEN NEUEN GERÄTEN ODER NEUER FIRMWARE (TLS/Port 8883):

                                    Dieses Tool ist ein reines Konfigurations-Werkzeug und schreibt via Bluetooth ausschließlich die WLAN-Daten und die Broker-URL im Gerät um.
                                    Das Tool generiert KEINE TLS-Zertifikate oder Keys!

                                    Um die Bereitstellung der passenden Verschlüsselung auf eurem lokalen MQTT-Broker müsst ihr euch zwingend selbst kümmern.
                                    Wer hierzu Fragen hat: Das Internet und bekannte Foren helfen zu verschlüsselten Mosquitto-Brokern weiter.

                                    Von mir wird es dazu keinen Support und keine Zertifikats-Dateien geben.

                                    Viel Spaß mit dem zURLtool!


                                    DOWNLOAD & ENTPACK-ANLEITUNG


                                    Die fertige zURLtool_GUI.exe überschreitet das Foren-Limit von 5 MB.
                                    Deshalb musste ich das Archiv in 3 Teile aufteilen.
                                    Die 3 Teile findet ihr direkt in den nächsten 3 Beiträgen unter diesem Post.

                                    So entpackt ihr das Tool fehlerfrei:

                                    1. Ladet alle 3 Dateien (zURLtool.zip, zURLtool.z01 und zURLtool.z02) in den exakt selben Ordner auf eurem PC herunter.

                                    2. Macht zur eigenen Sicherheit vor dem Start gerne einen Virenscan.

                                    3. Entpackt ausschließlich die Hauptdatei zURLtool.zip

                                    4. Verwendet dazu bitte ein kostenloses Programm wie 7-Zip oder WinRAR
                                      (der Windows-eigene Entpacker scheitert leider an mehrteiligen ZIP-Archiven).
                                      Das Entpack-Programm zieht sich die Daten aus den Teilen .z01 und .z02 ganz automatisch dazu.

                                    maxclaudiM
                                    maxclaudiM
                                    maxclaudi
                                    schrieb am zuletzt editiert von maxclaudi
                                    #655

                                    zURLtool_V20 (c) maxclaudi 2026 - Version 2.0 cdh

                                    Neu in zURLtool V 2.0 dch
                                    • Sprachauswahl: Deutsch und Englisch sind auswählbar und während der Laufzeit umschaltbar.

                                    • Reset-Funktion: Geräte können zurückgesetzt werden (Hinweis: Zum vollständigen Abmelden - z.B. bei Verkauf - Gerät auch in der App entfernen!).

                                    • WLAN-Handling: Das manuelle Setzen des WiFi-Mode entfällt. Ein automatischer Scan zeigt alle vom Zendure-Gerät erreichbaren Netze an. Bei zur config.ini abweichendem, manuell ausgewählten WLAN muss auch das Passwort in der GUI angepasst werden.

                                    • Offline-Modus: Die Option OFFLINE = 1 in der config.ini überspringt den WLAN-Scan, ideal für Vorbereitungen oder Konfigurationen ohne aktiven Router.

                                    • Token & User-ID: Gerätespezifischer Token wird in mqtt_creds.txt gesichert. Für die korrekte userId (bzw. consumerId) empfiehlt sich der Einsatz des Zusatztools zAppEmulationDeviceInfo

                                    • Ein Backup mit den verwendeten Daten in der config.ini wird zusätzlich gespeichert. Beispiel: bak_config_26.10.26_14.51.35.ini

                                    • Hinweis zu TLS / Port 8883: Das Tool ist ein reiner Konfigurator per Bluetooth und generiert keine TLS-Zertifikate oder Keys.

                                    00.png

                                    01-de.png

                                    Beschreibung im Spoiler.


                                    zURLtool V 2.0 dch – Lokaler MQTT-Broker für Zendure 2026

                                    Hallo zusammen, das Update zURLtool V 2.0 dch de/gb ist nun fertig!


                                    Das Programm ist in seiner Art einzigartig, neu und durch monatelanges Reverse-Engineering, Analysen und intensives Testen entstanden.
                                    Es handelt sich um ein reines Windows-GUI-Programm, das ganz einfach per Doppelklick gestartet wird.

                                    Es verwendet keinen Code von anderen bekannten Projekten, die teils fehlerhaften und unvollständigen Code verwenden, mit dem Konfigurationen neuer Geräte nicht möglich sind.

                                    Technische Voraussetzungen & Basis:

                                    Vielen Dank an Henrik Blidh für die hervorragende BT-Bibliothek.

                                    Das Programm nutzt zur Kommunikation ausschließlich die offiziellen Windows-BT-Treiber ab Windows 10/11 und die integrierte Bluetooth-Bibliothek bleak:

                                    ==================================================

                                    LICENSE NOTICE FOR THE UTILIZED BLUETOOTH LIBRARY
                                    This program uses "bleak" for Bluetooth functionality.
                                    https://github.com/hbldh/bleak
                                    Copyright (c) 2020, Henrik Blidh
                                    Permission is hereby granted, free of charge, to any person obtaining a copy
                                    of this software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND.

                                    ==================================================

                                    Die Bibliothek wurde fest in das Programm eingebunden.

                                    Weitere Bibliotheken oder Frameworks müssen auf eurem PC nicht installiert werden.

                                    • Betriebssystem: Windows (10) 11 (64-Bit)
                                    • Hardware: Interner Bluetooth-Chip oder ein standardmäßiger USB-Bluetooth-Stick am Rechner.
                                    • Kompatibilität: Getestet mit Zendure-Geräten bis zur 2400er-Serie (sowohl ältere als auch neueste Generationen / Firmware-Stände).

                                    Inhalt des Programmpakets (zURLtoolV20.zip):
                                    Das ZIP-Archiv enthält genau drei Dateien, die zwingend im selben Ordner liegen bleiben müssen:
                                    • zURLtool_V20.exe (Größe: ca. 15.5 MB / 15555 KB)
                                    • config.ini (Konfigurationsdatei für eure Zugangsdaten)
                                    • ReadMe_zURLtoolV20.txt (Ausführliche Anleitung in Deutsch & Englisch)

                                    Was genau macht das zURLtool?
                                    Das Tool schreibt im Zendure-Gerät die Konfiguration der Broker-URL um.

                                    • Es wird NICHT in die Firmware eingegriffen und es wird nichts modifiziert. Die originale Firmware von Zendure bleibt zu 100 % unangetastet.

                                    • Es werden lediglich die standardmäßigen Bluetooth-Befehle verwendet, die die Firmware von Haus aus für die App bereitstellt.

                                    • Das Skript verhält sich für das Gerät exakt wie die originale App, greift auf die Konfigurationsvariablen im Speicher zu und überschreibt dort die Wi-Fi-Daten und den Eintrag für die Cloud-URL mit der IP eures eigenen lokalen Brokers.

                                    Es funktioniert quasi wie eine zusätzliche Länderauswahl, bei der man sich einfach mit einem anderen Server verbindet.
                                    Das bietet die Firmware von Haus aus an, da die Geräte sonst permanent fest an ganz bestimmte AWS-Server gebunden wären.
                                    Zurück zur Cloud ist auch möglich. Beispiele sind in der config.ini enthalten.

                                    Die Geräte verbinden sich und können beschrieben werden ohne hartes Reset und ohne hartes Trennen von allen Stromquellen.

                                    Freigabe vereinfacht.
                                    Wer schon eine Seriennummer und Freischalt-Code für zURLtool V1.0 dch hat, kann diese verwenden.
                                    Seriennummer und Freischalt-Code bleiben gültig, eine erneute Anfrage für zURLtool V2.0 ist nicht erforderlich.

                                    Der Freischalt-Ablauf:
                                    Damit das Programm funktioniert und um anonyme Massen-Weiterreichungen zu unterbinden, benötigt das Programm eine Seriennummer und einen Freigabe-Code.

                                    1. Sende mir deinen (Foren-) Namen per privater Nachricht (PN) im Forum oder per E-Mail an: ztool.maxclaudi AT gmail . com
                                    2. Du erhältst von mir eine persönliche "Seriennummer" und den dazu passenden "Freigabe-Code"
                                    3. Sichere dir die Seriennummer und den Freigabe-Code unbedingt in einer Textdatei als Backup! Falls du die Datei mal verlierst, hast du deine Seriennummer und deinen Freigabe-Code als Backup griffbereit.
                                    4. Starte die 'zURLtool_GUI.exe'.

                                    Trage beide Werte exakt so, wie du sie erhalten hast, in die dafür vorgesehenen Felder im Startfenster ein und klicke auf "Akzeptieren & Starten".
                                    Nach einmaliger Eingabe startet zURLtool zukünftig vollautomatisch in die Hauptoberfläche.

                                    Als Notfall hast du zusätzlich Seriennummer und Freigabecode als Backup.
                                    Vor dem ersten Start muss bzw. kann die config.ini in einem einfachen Text-Editor mit euren echten Daten befüllt werden.
                                    Auswählbare Beispiele und Erklärungen zu den WLAN-Modi sind direkt darin enthalten.

                                    Wenn keine config.ini vorhanden ist oder nicht benutzt wird, müssen die Daten in die GUI eingegeben werden.

                                    Warum ist das Programm Closed Source?
                                    Zu oft wurden meine Skripte, Codes oder Teile daraus in der Vergangenheit verwendet und/oder modifiziert – ohne Nennung meines Namens, ohne Danksagung / Anerkennung und unter Entfernung meiner originalen Hinweise.
                                    Entweder wurde (leicht abgeändert) einfach "übernommen" oder u. a. als eigener Erfolg ausgegeben oder kommentiert mit "ich habe einen Weg gefunden".
                                    Das ist ungerecht.

                                    Monatelange harte Arbeit sollte zumindest anerkannt werden.

                                    Sollte das Programm oder der Code in irgendeiner Form gehackt zur Verfügung gestellt oder verwendet werden, dann war dies mein letzter offizieller Beitrag in Form von Code für die Community.
                                    Das gilt auch wenn das Programm inkl. Seriennummer und Freigabe-Code mehrfach geteilt wird.

                                    Viel Spaß mit dem zURLtool V2.0 dch!


                                    01-eng.png

                                    02-eng.png

                                    04-eng.png

                                    05-eng.png


                                    Bitte ReadMe_zURLtoolV20.txt beachten, enthält die deutsche und englische Beschreibung.

                                    HINWEIS FÜR BESTEHENDE NUTZER:
                                    Wer bereits für die Version 1.0 eine Seriennummer und einen Freischalt-Code erhalten hat, kann diese Daten direkt weiterverwenden!
                                    Sie sind auch für die Version 2.0 gültig.
                                    Bitte in diesem Fall keine erneute Anfrage.

                                    RECHTLICHER HINWEIS ZUR FREIGABE:
                                    Eine Weitergabe von Seriennummer und Freigabe-Code
                                    sowie deren Mehrfachnutzung durch verschiedene Personen ist ausdrücklich UNZULÄSSIG.


                                    Please note ReadMe_zURLtoolV20.txt: contains the German and English description.

                                    NOTE FOR EXISTING USERS:
                                    If you have already received a serial number and activation code for version 1.0, you can reuse them directly!
                                    They are valid for version 2.0 as well. Please do not submit a new request in this case.

                                    LEGAL NOTICE ON ACTIVATION:
                                    Sharing the serial number and activation code, as well as their multiple use by different individuals, is strictly PROHIBITED.

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

                                    neue Zendure Geräte Cloudless: zURLtool V2.0

                                    maxclaudiM 5 Antworten Letzte Antwort
                                    0
                                    • maxclaudiM maxclaudi

                                      zURLtool_V20 (c) maxclaudi 2026 - Version 2.0 cdh

                                      Neu in zURLtool V 2.0 dch
                                      • Sprachauswahl: Deutsch und Englisch sind auswählbar und während der Laufzeit umschaltbar.

                                      • Reset-Funktion: Geräte können zurückgesetzt werden (Hinweis: Zum vollständigen Abmelden - z.B. bei Verkauf - Gerät auch in der App entfernen!).

                                      • WLAN-Handling: Das manuelle Setzen des WiFi-Mode entfällt. Ein automatischer Scan zeigt alle vom Zendure-Gerät erreichbaren Netze an. Bei zur config.ini abweichendem, manuell ausgewählten WLAN muss auch das Passwort in der GUI angepasst werden.

                                      • Offline-Modus: Die Option OFFLINE = 1 in der config.ini überspringt den WLAN-Scan, ideal für Vorbereitungen oder Konfigurationen ohne aktiven Router.

                                      • Token & User-ID: Gerätespezifischer Token wird in mqtt_creds.txt gesichert. Für die korrekte userId (bzw. consumerId) empfiehlt sich der Einsatz des Zusatztools zAppEmulationDeviceInfo

                                      • Ein Backup mit den verwendeten Daten in der config.ini wird zusätzlich gespeichert. Beispiel: bak_config_26.10.26_14.51.35.ini

                                      • Hinweis zu TLS / Port 8883: Das Tool ist ein reiner Konfigurator per Bluetooth und generiert keine TLS-Zertifikate oder Keys.

                                      00.png

                                      01-de.png

                                      Beschreibung im Spoiler.


                                      zURLtool V 2.0 dch – Lokaler MQTT-Broker für Zendure 2026

                                      Hallo zusammen, das Update zURLtool V 2.0 dch de/gb ist nun fertig!


                                      Das Programm ist in seiner Art einzigartig, neu und durch monatelanges Reverse-Engineering, Analysen und intensives Testen entstanden.
                                      Es handelt sich um ein reines Windows-GUI-Programm, das ganz einfach per Doppelklick gestartet wird.

                                      Es verwendet keinen Code von anderen bekannten Projekten, die teils fehlerhaften und unvollständigen Code verwenden, mit dem Konfigurationen neuer Geräte nicht möglich sind.

                                      Technische Voraussetzungen & Basis:

                                      Vielen Dank an Henrik Blidh für die hervorragende BT-Bibliothek.

                                      Das Programm nutzt zur Kommunikation ausschließlich die offiziellen Windows-BT-Treiber ab Windows 10/11 und die integrierte Bluetooth-Bibliothek bleak:

                                      ==================================================

                                      LICENSE NOTICE FOR THE UTILIZED BLUETOOTH LIBRARY
                                      This program uses "bleak" for Bluetooth functionality.
                                      https://github.com/hbldh/bleak
                                      Copyright (c) 2020, Henrik Blidh
                                      Permission is hereby granted, free of charge, to any person obtaining a copy
                                      of this software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND.

                                      ==================================================

                                      Die Bibliothek wurde fest in das Programm eingebunden.

                                      Weitere Bibliotheken oder Frameworks müssen auf eurem PC nicht installiert werden.

                                      • Betriebssystem: Windows (10) 11 (64-Bit)
                                      • Hardware: Interner Bluetooth-Chip oder ein standardmäßiger USB-Bluetooth-Stick am Rechner.
                                      • Kompatibilität: Getestet mit Zendure-Geräten bis zur 2400er-Serie (sowohl ältere als auch neueste Generationen / Firmware-Stände).

                                      Inhalt des Programmpakets (zURLtoolV20.zip):
                                      Das ZIP-Archiv enthält genau drei Dateien, die zwingend im selben Ordner liegen bleiben müssen:
                                      • zURLtool_V20.exe (Größe: ca. 15.5 MB / 15555 KB)
                                      • config.ini (Konfigurationsdatei für eure Zugangsdaten)
                                      • ReadMe_zURLtoolV20.txt (Ausführliche Anleitung in Deutsch & Englisch)

                                      Was genau macht das zURLtool?
                                      Das Tool schreibt im Zendure-Gerät die Konfiguration der Broker-URL um.

                                      • Es wird NICHT in die Firmware eingegriffen und es wird nichts modifiziert. Die originale Firmware von Zendure bleibt zu 100 % unangetastet.

                                      • Es werden lediglich die standardmäßigen Bluetooth-Befehle verwendet, die die Firmware von Haus aus für die App bereitstellt.

                                      • Das Skript verhält sich für das Gerät exakt wie die originale App, greift auf die Konfigurationsvariablen im Speicher zu und überschreibt dort die Wi-Fi-Daten und den Eintrag für die Cloud-URL mit der IP eures eigenen lokalen Brokers.

                                      Es funktioniert quasi wie eine zusätzliche Länderauswahl, bei der man sich einfach mit einem anderen Server verbindet.
                                      Das bietet die Firmware von Haus aus an, da die Geräte sonst permanent fest an ganz bestimmte AWS-Server gebunden wären.
                                      Zurück zur Cloud ist auch möglich. Beispiele sind in der config.ini enthalten.

                                      Die Geräte verbinden sich und können beschrieben werden ohne hartes Reset und ohne hartes Trennen von allen Stromquellen.

                                      Freigabe vereinfacht.
                                      Wer schon eine Seriennummer und Freischalt-Code für zURLtool V1.0 dch hat, kann diese verwenden.
                                      Seriennummer und Freischalt-Code bleiben gültig, eine erneute Anfrage für zURLtool V2.0 ist nicht erforderlich.

                                      Der Freischalt-Ablauf:
                                      Damit das Programm funktioniert und um anonyme Massen-Weiterreichungen zu unterbinden, benötigt das Programm eine Seriennummer und einen Freigabe-Code.

                                      1. Sende mir deinen (Foren-) Namen per privater Nachricht (PN) im Forum oder per E-Mail an: ztool.maxclaudi AT gmail . com
                                      2. Du erhältst von mir eine persönliche "Seriennummer" und den dazu passenden "Freigabe-Code"
                                      3. Sichere dir die Seriennummer und den Freigabe-Code unbedingt in einer Textdatei als Backup! Falls du die Datei mal verlierst, hast du deine Seriennummer und deinen Freigabe-Code als Backup griffbereit.
                                      4. Starte die 'zURLtool_GUI.exe'.

                                      Trage beide Werte exakt so, wie du sie erhalten hast, in die dafür vorgesehenen Felder im Startfenster ein und klicke auf "Akzeptieren & Starten".
                                      Nach einmaliger Eingabe startet zURLtool zukünftig vollautomatisch in die Hauptoberfläche.

                                      Als Notfall hast du zusätzlich Seriennummer und Freigabecode als Backup.
                                      Vor dem ersten Start muss bzw. kann die config.ini in einem einfachen Text-Editor mit euren echten Daten befüllt werden.
                                      Auswählbare Beispiele und Erklärungen zu den WLAN-Modi sind direkt darin enthalten.

                                      Wenn keine config.ini vorhanden ist oder nicht benutzt wird, müssen die Daten in die GUI eingegeben werden.

                                      Warum ist das Programm Closed Source?
                                      Zu oft wurden meine Skripte, Codes oder Teile daraus in der Vergangenheit verwendet und/oder modifiziert – ohne Nennung meines Namens, ohne Danksagung / Anerkennung und unter Entfernung meiner originalen Hinweise.
                                      Entweder wurde (leicht abgeändert) einfach "übernommen" oder u. a. als eigener Erfolg ausgegeben oder kommentiert mit "ich habe einen Weg gefunden".
                                      Das ist ungerecht.

                                      Monatelange harte Arbeit sollte zumindest anerkannt werden.

                                      Sollte das Programm oder der Code in irgendeiner Form gehackt zur Verfügung gestellt oder verwendet werden, dann war dies mein letzter offizieller Beitrag in Form von Code für die Community.
                                      Das gilt auch wenn das Programm inkl. Seriennummer und Freigabe-Code mehrfach geteilt wird.

                                      Viel Spaß mit dem zURLtool V2.0 dch!


                                      01-eng.png

                                      02-eng.png

                                      04-eng.png

                                      05-eng.png


                                      Bitte ReadMe_zURLtoolV20.txt beachten, enthält die deutsche und englische Beschreibung.

                                      HINWEIS FÜR BESTEHENDE NUTZER:
                                      Wer bereits für die Version 1.0 eine Seriennummer und einen Freischalt-Code erhalten hat, kann diese Daten direkt weiterverwenden!
                                      Sie sind auch für die Version 2.0 gültig.
                                      Bitte in diesem Fall keine erneute Anfrage.

                                      RECHTLICHER HINWEIS ZUR FREIGABE:
                                      Eine Weitergabe von Seriennummer und Freigabe-Code
                                      sowie deren Mehrfachnutzung durch verschiedene Personen ist ausdrücklich UNZULÄSSIG.


                                      Please note ReadMe_zURLtoolV20.txt: contains the German and English description.

                                      NOTE FOR EXISTING USERS:
                                      If you have already received a serial number and activation code for version 1.0, you can reuse them directly!
                                      They are valid for version 2.0 as well. Please do not submit a new request in this case.

                                      LEGAL NOTICE ON ACTIVATION:
                                      Sharing the serial number and activation code, as well as their multiple use by different individuals, is strictly PROHIBITED.

                                      maxclaudiM
                                      maxclaudiM
                                      maxclaudi
                                      schrieb am zuletzt editiert von maxclaudi
                                      #656

                                      Download zURLtoolV20.zip in 4 Teilen.
                                      Teil 1v4:

                                      zURLtoolV20.zip

                                      Achtung: Falls die Datei nach dem Download einen anderen Namen als zURLtoolV20.zip hat, benenne sie bitte entsprechend um.
                                      Es kann und soll gerne ein Virenscan erfolgen!

                                      HINWEIS ZUM ENTPACKEN (MEHRTEILIGES ARCHIV):
                                      Das Programmpaket ist auf 4 Teile aufgeteilt. Bitte verwende zum Entpacken
                                      ein vollwertiges Packprogramm wie 7-Zip, WinRAR oder Bandizip. Das integrierte Windows-eigene ZIP-Programm kann mehrteilige Archive NICHT korrekt zusammensetzen und führt zu Fehlfunktionen oder Fehlermeldungen beim Entpacken!

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

                                      neue Zendure Geräte Cloudless: zURLtool V2.0

                                      1 Antwort Letzte Antwort
                                      0
                                      • maxclaudiM maxclaudi

                                        zURLtool_V20 (c) maxclaudi 2026 - Version 2.0 cdh

                                        Neu in zURLtool V 2.0 dch
                                        • Sprachauswahl: Deutsch und Englisch sind auswählbar und während der Laufzeit umschaltbar.

                                        • Reset-Funktion: Geräte können zurückgesetzt werden (Hinweis: Zum vollständigen Abmelden - z.B. bei Verkauf - Gerät auch in der App entfernen!).

                                        • WLAN-Handling: Das manuelle Setzen des WiFi-Mode entfällt. Ein automatischer Scan zeigt alle vom Zendure-Gerät erreichbaren Netze an. Bei zur config.ini abweichendem, manuell ausgewählten WLAN muss auch das Passwort in der GUI angepasst werden.

                                        • Offline-Modus: Die Option OFFLINE = 1 in der config.ini überspringt den WLAN-Scan, ideal für Vorbereitungen oder Konfigurationen ohne aktiven Router.

                                        • Token & User-ID: Gerätespezifischer Token wird in mqtt_creds.txt gesichert. Für die korrekte userId (bzw. consumerId) empfiehlt sich der Einsatz des Zusatztools zAppEmulationDeviceInfo

                                        • Ein Backup mit den verwendeten Daten in der config.ini wird zusätzlich gespeichert. Beispiel: bak_config_26.10.26_14.51.35.ini

                                        • Hinweis zu TLS / Port 8883: Das Tool ist ein reiner Konfigurator per Bluetooth und generiert keine TLS-Zertifikate oder Keys.

                                        00.png

                                        01-de.png

                                        Beschreibung im Spoiler.


                                        zURLtool V 2.0 dch – Lokaler MQTT-Broker für Zendure 2026

                                        Hallo zusammen, das Update zURLtool V 2.0 dch de/gb ist nun fertig!


                                        Das Programm ist in seiner Art einzigartig, neu und durch monatelanges Reverse-Engineering, Analysen und intensives Testen entstanden.
                                        Es handelt sich um ein reines Windows-GUI-Programm, das ganz einfach per Doppelklick gestartet wird.

                                        Es verwendet keinen Code von anderen bekannten Projekten, die teils fehlerhaften und unvollständigen Code verwenden, mit dem Konfigurationen neuer Geräte nicht möglich sind.

                                        Technische Voraussetzungen & Basis:

                                        Vielen Dank an Henrik Blidh für die hervorragende BT-Bibliothek.

                                        Das Programm nutzt zur Kommunikation ausschließlich die offiziellen Windows-BT-Treiber ab Windows 10/11 und die integrierte Bluetooth-Bibliothek bleak:

                                        ==================================================

                                        LICENSE NOTICE FOR THE UTILIZED BLUETOOTH LIBRARY
                                        This program uses "bleak" for Bluetooth functionality.
                                        https://github.com/hbldh/bleak
                                        Copyright (c) 2020, Henrik Blidh
                                        Permission is hereby granted, free of charge, to any person obtaining a copy
                                        of this software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND.

                                        ==================================================

                                        Die Bibliothek wurde fest in das Programm eingebunden.

                                        Weitere Bibliotheken oder Frameworks müssen auf eurem PC nicht installiert werden.

                                        • Betriebssystem: Windows (10) 11 (64-Bit)
                                        • Hardware: Interner Bluetooth-Chip oder ein standardmäßiger USB-Bluetooth-Stick am Rechner.
                                        • Kompatibilität: Getestet mit Zendure-Geräten bis zur 2400er-Serie (sowohl ältere als auch neueste Generationen / Firmware-Stände).

                                        Inhalt des Programmpakets (zURLtoolV20.zip):
                                        Das ZIP-Archiv enthält genau drei Dateien, die zwingend im selben Ordner liegen bleiben müssen:
                                        • zURLtool_V20.exe (Größe: ca. 15.5 MB / 15555 KB)
                                        • config.ini (Konfigurationsdatei für eure Zugangsdaten)
                                        • ReadMe_zURLtoolV20.txt (Ausführliche Anleitung in Deutsch & Englisch)

                                        Was genau macht das zURLtool?
                                        Das Tool schreibt im Zendure-Gerät die Konfiguration der Broker-URL um.

                                        • Es wird NICHT in die Firmware eingegriffen und es wird nichts modifiziert. Die originale Firmware von Zendure bleibt zu 100 % unangetastet.

                                        • Es werden lediglich die standardmäßigen Bluetooth-Befehle verwendet, die die Firmware von Haus aus für die App bereitstellt.

                                        • Das Skript verhält sich für das Gerät exakt wie die originale App, greift auf die Konfigurationsvariablen im Speicher zu und überschreibt dort die Wi-Fi-Daten und den Eintrag für die Cloud-URL mit der IP eures eigenen lokalen Brokers.

                                        Es funktioniert quasi wie eine zusätzliche Länderauswahl, bei der man sich einfach mit einem anderen Server verbindet.
                                        Das bietet die Firmware von Haus aus an, da die Geräte sonst permanent fest an ganz bestimmte AWS-Server gebunden wären.
                                        Zurück zur Cloud ist auch möglich. Beispiele sind in der config.ini enthalten.

                                        Die Geräte verbinden sich und können beschrieben werden ohne hartes Reset und ohne hartes Trennen von allen Stromquellen.

                                        Freigabe vereinfacht.
                                        Wer schon eine Seriennummer und Freischalt-Code für zURLtool V1.0 dch hat, kann diese verwenden.
                                        Seriennummer und Freischalt-Code bleiben gültig, eine erneute Anfrage für zURLtool V2.0 ist nicht erforderlich.

                                        Der Freischalt-Ablauf:
                                        Damit das Programm funktioniert und um anonyme Massen-Weiterreichungen zu unterbinden, benötigt das Programm eine Seriennummer und einen Freigabe-Code.

                                        1. Sende mir deinen (Foren-) Namen per privater Nachricht (PN) im Forum oder per E-Mail an: ztool.maxclaudi AT gmail . com
                                        2. Du erhältst von mir eine persönliche "Seriennummer" und den dazu passenden "Freigabe-Code"
                                        3. Sichere dir die Seriennummer und den Freigabe-Code unbedingt in einer Textdatei als Backup! Falls du die Datei mal verlierst, hast du deine Seriennummer und deinen Freigabe-Code als Backup griffbereit.
                                        4. Starte die 'zURLtool_GUI.exe'.

                                        Trage beide Werte exakt so, wie du sie erhalten hast, in die dafür vorgesehenen Felder im Startfenster ein und klicke auf "Akzeptieren & Starten".
                                        Nach einmaliger Eingabe startet zURLtool zukünftig vollautomatisch in die Hauptoberfläche.

                                        Als Notfall hast du zusätzlich Seriennummer und Freigabecode als Backup.
                                        Vor dem ersten Start muss bzw. kann die config.ini in einem einfachen Text-Editor mit euren echten Daten befüllt werden.
                                        Auswählbare Beispiele und Erklärungen zu den WLAN-Modi sind direkt darin enthalten.

                                        Wenn keine config.ini vorhanden ist oder nicht benutzt wird, müssen die Daten in die GUI eingegeben werden.

                                        Warum ist das Programm Closed Source?
                                        Zu oft wurden meine Skripte, Codes oder Teile daraus in der Vergangenheit verwendet und/oder modifiziert – ohne Nennung meines Namens, ohne Danksagung / Anerkennung und unter Entfernung meiner originalen Hinweise.
                                        Entweder wurde (leicht abgeändert) einfach "übernommen" oder u. a. als eigener Erfolg ausgegeben oder kommentiert mit "ich habe einen Weg gefunden".
                                        Das ist ungerecht.

                                        Monatelange harte Arbeit sollte zumindest anerkannt werden.

                                        Sollte das Programm oder der Code in irgendeiner Form gehackt zur Verfügung gestellt oder verwendet werden, dann war dies mein letzter offizieller Beitrag in Form von Code für die Community.
                                        Das gilt auch wenn das Programm inkl. Seriennummer und Freigabe-Code mehrfach geteilt wird.

                                        Viel Spaß mit dem zURLtool V2.0 dch!


                                        01-eng.png

                                        02-eng.png

                                        04-eng.png

                                        05-eng.png


                                        Bitte ReadMe_zURLtoolV20.txt beachten, enthält die deutsche und englische Beschreibung.

                                        HINWEIS FÜR BESTEHENDE NUTZER:
                                        Wer bereits für die Version 1.0 eine Seriennummer und einen Freischalt-Code erhalten hat, kann diese Daten direkt weiterverwenden!
                                        Sie sind auch für die Version 2.0 gültig.
                                        Bitte in diesem Fall keine erneute Anfrage.

                                        RECHTLICHER HINWEIS ZUR FREIGABE:
                                        Eine Weitergabe von Seriennummer und Freigabe-Code
                                        sowie deren Mehrfachnutzung durch verschiedene Personen ist ausdrücklich UNZULÄSSIG.


                                        Please note ReadMe_zURLtoolV20.txt: contains the German and English description.

                                        NOTE FOR EXISTING USERS:
                                        If you have already received a serial number and activation code for version 1.0, you can reuse them directly!
                                        They are valid for version 2.0 as well. Please do not submit a new request in this case.

                                        LEGAL NOTICE ON ACTIVATION:
                                        Sharing the serial number and activation code, as well as their multiple use by different individuals, is strictly PROHIBITED.

                                        maxclaudiM
                                        maxclaudiM
                                        maxclaudi
                                        schrieb am zuletzt editiert von maxclaudi
                                        #657

                                        Download zURLtoolV20.zip in 4 Teilen.
                                        Teil 2v4:

                                        zURLtoolV20.z01

                                        Achtung: Falls die Datei nach dem Download einen anderen Namen als zURLtoolV20.z01 hat, benenne sie bitte entsprechend um.
                                        Es kann und soll gerne ein Virenscan erfolgen!

                                        HINWEIS ZUM ENTPACKEN (MEHRTEILIGES ARCHIV):
                                        Das Programmpaket ist auf 4 Teile aufgeteilt. Bitte verwende zum Entpacken
                                        ein vollwertiges Packprogramm wie 7-Zip, WinRAR oder Bandizip. Das integrierte Windows-eigene ZIP-Programm kann mehrteilige Archive NICHT korrekt zusammensetzen und führt zu Fehlfunktionen oder Fehlermeldungen beim Entpacken!

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

                                        neue Zendure Geräte Cloudless: zURLtool V2.0

                                        1 Antwort Letzte Antwort
                                        0
                                        • maxclaudiM maxclaudi

                                          zURLtool_V20 (c) maxclaudi 2026 - Version 2.0 cdh

                                          Neu in zURLtool V 2.0 dch
                                          • Sprachauswahl: Deutsch und Englisch sind auswählbar und während der Laufzeit umschaltbar.

                                          • Reset-Funktion: Geräte können zurückgesetzt werden (Hinweis: Zum vollständigen Abmelden - z.B. bei Verkauf - Gerät auch in der App entfernen!).

                                          • WLAN-Handling: Das manuelle Setzen des WiFi-Mode entfällt. Ein automatischer Scan zeigt alle vom Zendure-Gerät erreichbaren Netze an. Bei zur config.ini abweichendem, manuell ausgewählten WLAN muss auch das Passwort in der GUI angepasst werden.

                                          • Offline-Modus: Die Option OFFLINE = 1 in der config.ini überspringt den WLAN-Scan, ideal für Vorbereitungen oder Konfigurationen ohne aktiven Router.

                                          • Token & User-ID: Gerätespezifischer Token wird in mqtt_creds.txt gesichert. Für die korrekte userId (bzw. consumerId) empfiehlt sich der Einsatz des Zusatztools zAppEmulationDeviceInfo

                                          • Ein Backup mit den verwendeten Daten in der config.ini wird zusätzlich gespeichert. Beispiel: bak_config_26.10.26_14.51.35.ini

                                          • Hinweis zu TLS / Port 8883: Das Tool ist ein reiner Konfigurator per Bluetooth und generiert keine TLS-Zertifikate oder Keys.

                                          00.png

                                          01-de.png

                                          Beschreibung im Spoiler.


                                          zURLtool V 2.0 dch – Lokaler MQTT-Broker für Zendure 2026

                                          Hallo zusammen, das Update zURLtool V 2.0 dch de/gb ist nun fertig!


                                          Das Programm ist in seiner Art einzigartig, neu und durch monatelanges Reverse-Engineering, Analysen und intensives Testen entstanden.
                                          Es handelt sich um ein reines Windows-GUI-Programm, das ganz einfach per Doppelklick gestartet wird.

                                          Es verwendet keinen Code von anderen bekannten Projekten, die teils fehlerhaften und unvollständigen Code verwenden, mit dem Konfigurationen neuer Geräte nicht möglich sind.

                                          Technische Voraussetzungen & Basis:

                                          Vielen Dank an Henrik Blidh für die hervorragende BT-Bibliothek.

                                          Das Programm nutzt zur Kommunikation ausschließlich die offiziellen Windows-BT-Treiber ab Windows 10/11 und die integrierte Bluetooth-Bibliothek bleak:

                                          ==================================================

                                          LICENSE NOTICE FOR THE UTILIZED BLUETOOTH LIBRARY
                                          This program uses "bleak" for Bluetooth functionality.
                                          https://github.com/hbldh/bleak
                                          Copyright (c) 2020, Henrik Blidh
                                          Permission is hereby granted, free of charge, to any person obtaining a copy
                                          of this software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND.

                                          ==================================================

                                          Die Bibliothek wurde fest in das Programm eingebunden.

                                          Weitere Bibliotheken oder Frameworks müssen auf eurem PC nicht installiert werden.

                                          • Betriebssystem: Windows (10) 11 (64-Bit)
                                          • Hardware: Interner Bluetooth-Chip oder ein standardmäßiger USB-Bluetooth-Stick am Rechner.
                                          • Kompatibilität: Getestet mit Zendure-Geräten bis zur 2400er-Serie (sowohl ältere als auch neueste Generationen / Firmware-Stände).

                                          Inhalt des Programmpakets (zURLtoolV20.zip):
                                          Das ZIP-Archiv enthält genau drei Dateien, die zwingend im selben Ordner liegen bleiben müssen:
                                          • zURLtool_V20.exe (Größe: ca. 15.5 MB / 15555 KB)
                                          • config.ini (Konfigurationsdatei für eure Zugangsdaten)
                                          • ReadMe_zURLtoolV20.txt (Ausführliche Anleitung in Deutsch & Englisch)

                                          Was genau macht das zURLtool?
                                          Das Tool schreibt im Zendure-Gerät die Konfiguration der Broker-URL um.

                                          • Es wird NICHT in die Firmware eingegriffen und es wird nichts modifiziert. Die originale Firmware von Zendure bleibt zu 100 % unangetastet.

                                          • Es werden lediglich die standardmäßigen Bluetooth-Befehle verwendet, die die Firmware von Haus aus für die App bereitstellt.

                                          • Das Skript verhält sich für das Gerät exakt wie die originale App, greift auf die Konfigurationsvariablen im Speicher zu und überschreibt dort die Wi-Fi-Daten und den Eintrag für die Cloud-URL mit der IP eures eigenen lokalen Brokers.

                                          Es funktioniert quasi wie eine zusätzliche Länderauswahl, bei der man sich einfach mit einem anderen Server verbindet.
                                          Das bietet die Firmware von Haus aus an, da die Geräte sonst permanent fest an ganz bestimmte AWS-Server gebunden wären.
                                          Zurück zur Cloud ist auch möglich. Beispiele sind in der config.ini enthalten.

                                          Die Geräte verbinden sich und können beschrieben werden ohne hartes Reset und ohne hartes Trennen von allen Stromquellen.

                                          Freigabe vereinfacht.
                                          Wer schon eine Seriennummer und Freischalt-Code für zURLtool V1.0 dch hat, kann diese verwenden.
                                          Seriennummer und Freischalt-Code bleiben gültig, eine erneute Anfrage für zURLtool V2.0 ist nicht erforderlich.

                                          Der Freischalt-Ablauf:
                                          Damit das Programm funktioniert und um anonyme Massen-Weiterreichungen zu unterbinden, benötigt das Programm eine Seriennummer und einen Freigabe-Code.

                                          1. Sende mir deinen (Foren-) Namen per privater Nachricht (PN) im Forum oder per E-Mail an: ztool.maxclaudi AT gmail . com
                                          2. Du erhältst von mir eine persönliche "Seriennummer" und den dazu passenden "Freigabe-Code"
                                          3. Sichere dir die Seriennummer und den Freigabe-Code unbedingt in einer Textdatei als Backup! Falls du die Datei mal verlierst, hast du deine Seriennummer und deinen Freigabe-Code als Backup griffbereit.
                                          4. Starte die 'zURLtool_GUI.exe'.

                                          Trage beide Werte exakt so, wie du sie erhalten hast, in die dafür vorgesehenen Felder im Startfenster ein und klicke auf "Akzeptieren & Starten".
                                          Nach einmaliger Eingabe startet zURLtool zukünftig vollautomatisch in die Hauptoberfläche.

                                          Als Notfall hast du zusätzlich Seriennummer und Freigabecode als Backup.
                                          Vor dem ersten Start muss bzw. kann die config.ini in einem einfachen Text-Editor mit euren echten Daten befüllt werden.
                                          Auswählbare Beispiele und Erklärungen zu den WLAN-Modi sind direkt darin enthalten.

                                          Wenn keine config.ini vorhanden ist oder nicht benutzt wird, müssen die Daten in die GUI eingegeben werden.

                                          Warum ist das Programm Closed Source?
                                          Zu oft wurden meine Skripte, Codes oder Teile daraus in der Vergangenheit verwendet und/oder modifiziert – ohne Nennung meines Namens, ohne Danksagung / Anerkennung und unter Entfernung meiner originalen Hinweise.
                                          Entweder wurde (leicht abgeändert) einfach "übernommen" oder u. a. als eigener Erfolg ausgegeben oder kommentiert mit "ich habe einen Weg gefunden".
                                          Das ist ungerecht.

                                          Monatelange harte Arbeit sollte zumindest anerkannt werden.

                                          Sollte das Programm oder der Code in irgendeiner Form gehackt zur Verfügung gestellt oder verwendet werden, dann war dies mein letzter offizieller Beitrag in Form von Code für die Community.
                                          Das gilt auch wenn das Programm inkl. Seriennummer und Freigabe-Code mehrfach geteilt wird.

                                          Viel Spaß mit dem zURLtool V2.0 dch!


                                          01-eng.png

                                          02-eng.png

                                          04-eng.png

                                          05-eng.png


                                          Bitte ReadMe_zURLtoolV20.txt beachten, enthält die deutsche und englische Beschreibung.

                                          HINWEIS FÜR BESTEHENDE NUTZER:
                                          Wer bereits für die Version 1.0 eine Seriennummer und einen Freischalt-Code erhalten hat, kann diese Daten direkt weiterverwenden!
                                          Sie sind auch für die Version 2.0 gültig.
                                          Bitte in diesem Fall keine erneute Anfrage.

                                          RECHTLICHER HINWEIS ZUR FREIGABE:
                                          Eine Weitergabe von Seriennummer und Freigabe-Code
                                          sowie deren Mehrfachnutzung durch verschiedene Personen ist ausdrücklich UNZULÄSSIG.


                                          Please note ReadMe_zURLtoolV20.txt: contains the German and English description.

                                          NOTE FOR EXISTING USERS:
                                          If you have already received a serial number and activation code for version 1.0, you can reuse them directly!
                                          They are valid for version 2.0 as well. Please do not submit a new request in this case.

                                          LEGAL NOTICE ON ACTIVATION:
                                          Sharing the serial number and activation code, as well as their multiple use by different individuals, is strictly PROHIBITED.

                                          maxclaudiM
                                          maxclaudiM
                                          maxclaudi
                                          schrieb am zuletzt editiert von maxclaudi
                                          #658

                                          Download zURLtoolV20.zip in 4 Teilen.
                                          Teil 3v4:

                                          zURLtoolV20.z02

                                          Achtung: Falls die Datei nach dem Download einen anderen Namen als zURLtoolV20.z02 hat, benenne sie bitte entsprechend um.
                                          Es kann und soll gerne ein Virenscan erfolgen!

                                          HINWEIS ZUM ENTPACKEN (MEHRTEILIGES ARCHIV):
                                          Das Programmpaket ist auf 4 Teile aufgeteilt. Bitte verwende zum Entpacken
                                          ein vollwertiges Packprogramm wie 7-Zip, WinRAR oder Bandizip. Das integrierte Windows-eigene ZIP-Programm kann mehrteilige Archive NICHT korrekt zusammensetzen und führt zu Fehlfunktionen oder Fehlermeldungen beim Entpacken!

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

                                          neue Zendure Geräte Cloudless: zURLtool V2.0

                                          1 Antwort Letzte Antwort
                                          0

                                          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

                                          244

                                          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