Weiter zum Inhalt
  • Home
  • Aktuell
  • 0 Ungelesen 0
  • Kategorien
  • Unreplied
  • Beliebt
  • GitHub
  • Docu
  • Hilfe
Skins
  • Hell
  • Brite
  • Cerulean
  • Cosmo
  • Flatly
  • Journal
  • Litera
  • Lumen
  • Lux
  • Materia
  • Minty
  • Morph
  • Pulse
  • Sandstone
  • Simplex
  • Sketchy
  • Spacelab
  • United
  • Yeti
  • Zephyr
  • Dunkel
  • Cyborg
  • Darkly
  • Quartz
  • Slate
  • Solar
  • Superhero
  • Vapor

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

Community Forum

donate donate
  1. ioBroker Community Home
  2. Deutsch
  3. Skripten / Logik
  4. JavaScript
  5. Zendure zenSDK Lokal API, SmartMode, SolarFlow AC 800 Pro 2

NEWS

  • Monatsrückblick Oktober 2026 ist online!
    BluefoxB
    Bluefox
    9
    1
    213

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

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

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

Geplant Angeheftet Gesperrt Verschoben JavaScript
689 Beiträge 24 Kommentatoren 124.1k 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.
  • maxclaudiM maxclaudi

    maxclaudi sagte:

    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.

    Wie geht es weiter?
    ..... 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.

    Erste Analysen zu HEMS.
    Interessant ist z. B. Folgendes, was den acMode betrifft, wenn man selbst über ein Script steuert:

    Auszug HEMS - Analyse (vermutlich weltweit die erste Veröffentlichung dazu 27.10.2026; 12:44h):
    Firmware enthält eine explizite zeitliche Sperre bei hemsEP mit 6 Sekunden Mindestabstand zwischen zwei Mode-Wechseln – das ist eine bewusste Entscheidung der Firmware-Autoren für genau diese eine, sicherheitsrelevante Funktion (Lademodus-Wechsel).

    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.


    Edit 28.10.2026, 11:00 Uhr
    Neu: Soft-Reset – Analyse und Anwendung lokal im ioBroker mittels MQTT und zenSDK möglich – ohne dass in NVS (FLASH) geschrieben wird.

    detaillierter im Spoiler.


    Ich habe den Reset-Verarbeitungspfad in der Firmware ESP32_v8251 detailliert untersucht.
    Der untersuchte Befehl löst einen zeitverzögerten Software-Neustart des ESP32 aus und keinen Factory-Reset.
    Dabei habe ich nachvollzogen, wie der Handler den Verzögerungswert verarbeitet und wie der Timer eingerichtet wird.


    1. Was macht der Reset-Befehl?

    Der Aufruf wird vom entsprechenden Handler verarbeitet.
    Der Wert wird aus dem zugehörigen JSON-Feld gelesen und an eine Funktion übergeben, die einen einmaligen Timer einrichtet.

    Der Timer verwendet den übergebenen Wert in Millisekunden und rechnet ihn für den hardwarenahen Timer-Aufruf in Mikrosekunden um:

    timeout_us = reset × 1000

    Nach Ablauf des Timers wird die interne Neustart-Routine des ESP32 aufgerufen.
    In meinen Tests wurde bei einem eingestellten Wert von 1500 ms anschließend die MQTT-Verbindung unterbrochen und nach dem Neustart wieder aufgebaut.

    2. Was wird durch den Software-Neustart zurückgesetzt?

    Bereich / Komponente Wirkung / Zustand nach dem Soft-Reset
    ESP32-Prozessor und Tasks Der Prozessor wird neu gestartet; Tasks werden beendet und beim Booten neu angelegt.
    RAM und flüchtige Variablen Flüchtige Zustände werden neu initialisiert.
    Timer und laufende Abläufe Laufende Timer und Abläufe werden beendet und beim Booten neu eingerichtet.
    WLAN-Verbindung Wird unterbrochen und beim Neustart erneut aufgebaut.
    MQTT-Verbindung Wird unterbrochen und nach dem Neustart erneut aufgebaut.
    Laufende BLE-Verbindung Wird unterbrochen.
    Persistente Konfiguration (NVS) Wird durch den Reset-Befehl nicht gezielt gelöscht.
    Firmware und gespeicherte Einstellungen Bleiben grundsätzlich erhalten, sofern sie nicht durch einen separaten Vorgang verändert werden.

    Wichtig für die bisherigen Untersuchungen:
    Der Befehl ist kein Ersatz für einen Factory-Reset.
    Im untersuchten Reset-Pfad ist kein Aufruf zum Löschen der NVS Konfiguration erkennbar.
    Persistente Einstellungen, darunter gespeicherte Verbindungsdaten, bleiben erhalten.
    Flüchtige Zustände können nach dem Neustart andere Werte haben, weil die Firmware ihre Initialisierung erneut durchläuft.

    3. Welche Werte für den Reset sind zulässig?

    Die untersuchte Handler-Logik enthält keine erkennbare explizite Mindest- oder Höchstwertprüfung für den Verzögerungswert.
    Dieser wird aus dem JSON-Zahlenfeld gelesen und an die Timer-Funktion übergeben.

    Die Multiplikation mit 1000 erfolgt mit einer 32-Bit-Operation.
    Bei sehr großen Werten kann es deshalb zu einem arithmetischen Überlauf kommen.
    Welche Grenzwerte sich daraus ergeben, hängt davon ab, ob der Wert vorzeichenbehaftet oder vorzeichenlos verarbeitet wird.

    • 0 ms: Kann einen praktisch sofortigen Neustart auslösen. Nicht empfehlenswert.
    • 100 ms: Sehr kurze Verzögerung; wird auch im HTTP-Konfigurationspfad verwendet.
    • 1500 ms: Von mir erfolgreich getestet und für den gezielten Reset geeignet.
    • 3000 ms: Eine längere Verzögerung, wenn vor dem Neustart noch Zeit für Debug-Ausgaben oder andere laufende Vorgänge benötigt wird.
    • 60000 ms: Liegt innerhalb des 32-Bit-Bereichs, sofern die Multiplikation vorzeichenlos erfolgt. Ob dieser Wert als regulärer Anwendungsfall vorgesehen ist, ist nicht belegt.
    • Negative Werte und sehr große positive Werte: Nicht verwenden. Ihr Verhalten hängt von der Typkonvertierung und der Überlaufbehandlung ab. Ein Überlauf kann eine unerwartete Verzögerung / Fehlverhalten verursachen.

    Empfehlung:
    Für den praktischen Einsatz bei 1500 bis 3000 ms bleiben.
    Diese Werte wurden getestet beziehungsweise liegen in einem gut handhabbaren Bereich.

    4. Gibt es denselben Befehl auch über HTTP?

    Im untersuchten Firmware-Code ist die entsprechende Verarbeitung im HTTP-Handler HA.Mqtt.SetConfig zu finden.
    Nach erfolgreicher Verarbeitung der Konfiguration ruft der Handler dieselbe interne Reset-Timer-Funktion mit einem fest eingetragenen Wert von 100 ms auf.

    Damit ist die Verbindung im Code belegt:
    Der HTTP-Konfigurationsweg und der eigenständige MQTT-Reset verwenden dieselbe interne Timer-Funktion. Sie unterscheiden sich beim übergebenen Verzögerungswert.

    • HTTP HA.Mqtt.SetConfig: Löst nach erfolgreicher Konfigurationsverarbeitung einen Reset mit fest codierten 100 ms Verzögerung aus.
    • MQTT-Reset: Verwendet dieselbe Timer-Funktion; die Verzögerung wird über den übergebenen Wert in Millisekunden bestimmt.

    5. Schreib-Schutzmechanismus bei identischen Konfigurationswerten

    Bei der Analyse der Firmware ESP32_v8251 habe ich im Storage-Pfad eine entscheidende Prüfung gefunden:
    Vorhandene Einträge werden anhand des Schlüssels gesucht und mit dem neu übergebenen Wert verglichen.

    Bei den untersuchten Datentypen berücksichtigt die Vergleichsfunktion auch Länge und Inhalt.
    Bei identischen Werten liefert sie den Rückgabewert 0.
    Der Storage-Setter verzweigt daraufhin direkt zum Funktionsende, anstatt die nachgelagerte Schreiboperation aufzurufen.

    Der HTTP-Handler übergibt die Konfigurationswerte an diese Storage-Schicht.
    Werden die betreffenden Werte unverändert übermittelt, greift der beschriebene Short-Circuit.

    Ergebnis für zenSDK HTTP - HA.Mqtt.SetConfig:
    Bei identischen Werten wird die nachgelagerte Schreiboperation dieses Storage-Pfads übersprungen.
    Damit ist belegt, dass für unveränderte Werte kein neuer Schreibvorgang über diesen Pfad angefordert wird.

    Präzisierung:
    Der Short-Circuit ist im untersuchten Codepfad nachvollzogen.
    Die Aussage bezieht sich deshalb auf den nachgelagerten Schreibpfad der untersuchten Storage-Schicht.

    6. Gesamtergebnis der Analyse

    • zenSDK HTTP – HA.Mqtt.SetConfig:
      Werden identische Konfigurationsparameter gesendet, erkennt die Storage-Schicht die unveränderten Werte und überspringt den nachgelagerten Schreibpfad. Anschließend wird ein Soft-Reset mit 100 ms Verzögerung ausgelöst.

    • Zendure-App:
      Die App bietet für MQTT nur die Optionen Ein und Aus. Beim Ausschalten wird enable:false, beim Einschalten enable:true gesetzt. Auch wenn beim Einschalten keine weiteren Konfigurationsparameter geändert werden, unterscheidet sich der Enable-Wert vom vorherigen Zustand. Dadurch greift der Short-Circuit für identische Werte nicht; der Konfigurations-Schreibpfad wird durchlaufen und die Konfiguration in NVS (FLASH) gespeichert.

    • MQTT: Über einen separaten Reset-Befehl lässt sich ein Soft-Reset beispielsweise mit 1500 ms Verzögerung gezielt auslösen. Das ist ein eigenständiger Befehl, dafür muss keine Konfiguration geändert werden und schreibt nichts in NVS (Flash).

    Kurzfassung: Sowohl über HTTP mit HA.Mqtt.SetConfig und identischen Werten als auch über den direkten MQTT-Reset-Befehl lässt sich ein Soft-Reset auslösen, ohne dass dafür ein neuer Konfigurationswert geschrieben werden muss.

    Einschränkung: Dies schließt andere, gegebenenfalls interne Flash-Schreibvorgänge nicht aus, die während eines Neustarts generell stattfinden könnten.
    Belegt ist, dass bei diesen beiden Wegen kein neuer Konfigurationswert über den beschriebenen Schreibpfad (FLASH) geschrieben werden muss.

    Wie ein Soft-Reset ohne Flash-Schreibvorgang über das zenSDK möglich ist, ist hier nun veröffentlicht.
    Das genaue MQTT-Topic und das Payload habe ich per Reverse Engineering exklusiv freigelegt und gebe beides zum Schutz vor "Übernahme" und direktem Kopieren ausschließlich auf Anfrage per E-Mail oder PN weiter.

    maxclaudiM
    maxclaudiM
    maxclaudi
    schrieb am zuletzt editiert von maxclaudi
    #661

    Wer zusätzlich einen Reset-Button bzw. einen Datenpunkt für einen Soft-Reset anlegen möchte.
    Hier ist das bisherige zenSDK-Steuerungs-Script, erweitert um eine Soft-Reset-Funktion für das Gerät.
    Der Reset bzw. Neustart wird ausgeführt, ohne in den Flash-Speicher zu schreiben.

    Dazu wird unter control ein neuer Datenpunkt angelegt:
    control.deviceReset

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

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

    neue Zendure Geräte Cloudless: zURLtool V2.0

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

      Nachdem ich jetzt ein paar Tage den Fehler Zendure IP überprüfen beobachtet habe, kann ich folgendes feststelln.

      Der Fehler tritt immer auf, wenn das Gerät am Abend mit Sonnenuntergang bzw. PV Eingang 0 Watt hat und wegen dem Wetter so wenig ertrag kam, das er auch gleich auf SOC min ist. Somit werden über sehr lange Zeit keine Steuerbefehle mehr gesendet und ich vermute, das das Gerät dann nach ca.13 Stunden in einen Tiefschlaf geht.
      Es meldet sich dann vom WLAN ab und erst nachdem wieder ein bisschen PV kommt wieder an.
      Die anderen 2 Tage habe ich länger eingespeist und dann erschien der Fehler am nächsten morgen nicht.

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

      maxclaudiM 1 Antwort Letzte Antwort
      1
      • D Daniel 8

        Nachdem ich jetzt ein paar Tage den Fehler Zendure IP überprüfen beobachtet habe, kann ich folgendes feststelln.

        Der Fehler tritt immer auf, wenn das Gerät am Abend mit Sonnenuntergang bzw. PV Eingang 0 Watt hat und wegen dem Wetter so wenig ertrag kam, das er auch gleich auf SOC min ist. Somit werden über sehr lange Zeit keine Steuerbefehle mehr gesendet und ich vermute, das das Gerät dann nach ca.13 Stunden in einen Tiefschlaf geht.
        Es meldet sich dann vom WLAN ab und erst nachdem wieder ein bisschen PV kommt wieder an.
        Die anderen 2 Tage habe ich länger eingespeist und dann erschien der Fehler am nächsten morgen nicht.

        maxclaudiM
        maxclaudiM
        maxclaudi
        schrieb am zuletzt editiert von maxclaudi
        #663

        @Daniel-8 sagte:

        Nachdem ich jetzt ein paar Tage den Fehler Zendure IP überprüfen beobachtet habe, kann ich folgendes feststelln.

        Der Fehler tritt immer auf, wenn das Gerät am Abend mit Sonnenuntergang bzw. PV Eingang 0 Watt hat und wegen dem Wetter so wenig ertrag kam, das er auch gleich auf SOC min ist. Somit werden über sehr lange Zeit keine Steuerbefehle mehr gesendet und ich vermute, das das Gerät dann nach ca.13 Stunden in einen Tiefschlaf geht.

        Das ist absolut nachvollziehbar.
        Das Abschalten des WLAN-Moduls oder das Versetzen der MCU in einen tiefen Schlafmodus ist eine gängige Schutzfunktion, da die Netzwerkkommunikation einen nicht unerheblichen Teil des Eigenstrombedarfs des Geräts ausmacht.
        Wenn über Stunden keine Aktivität (weder PV-Ertrag noch Steuerbefehle) registriert wird, greift irgendwann das Energiemanagement, um eine Tiefentladung der Zellen zu verhindern.

        Interessant wäre nun zu wissen:
        • Auf wie viel % steht dein minSoc aktuell?
        • Welchen Wert hat/haben die Batterie(n) dabei bei minVol (niedrigste Zellspannung)?

        Zusätzlich wäre es gut zu beobachten, wie sich die originale App in diesem Zustand verhält:

        • Meldet die App das Gerät zu diesem Zeitpunkt noch als online?
        • Lassen sich über die App noch Werte verändern?

        Falls ja, wäre wichtig festzustellen, ob das Gerät durch die App-Aktivität kurzzeitig aus dem Tiefschlaf erwacht und diese Befehle dann auch tatsächlich ausführt.
        Das würde uns zeigen, ob das Gerät im Schlafmodus noch auf bestimmte Wake-up-Signale (z. B. via Bluetooth oder die Cloud-Schnittstelle) reagiert.

        edit/PS: ist die Akku-Heizung aktiv?

        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:

          Nachdem ich jetzt ein paar Tage den Fehler Zendure IP überprüfen beobachtet habe, kann ich folgendes feststelln.

          Der Fehler tritt immer auf, wenn das Gerät am Abend mit Sonnenuntergang bzw. PV Eingang 0 Watt hat und wegen dem Wetter so wenig ertrag kam, das er auch gleich auf SOC min ist. Somit werden über sehr lange Zeit keine Steuerbefehle mehr gesendet und ich vermute, das das Gerät dann nach ca.13 Stunden in einen Tiefschlaf geht.

          Das ist absolut nachvollziehbar.
          Das Abschalten des WLAN-Moduls oder das Versetzen der MCU in einen tiefen Schlafmodus ist eine gängige Schutzfunktion, da die Netzwerkkommunikation einen nicht unerheblichen Teil des Eigenstrombedarfs des Geräts ausmacht.
          Wenn über Stunden keine Aktivität (weder PV-Ertrag noch Steuerbefehle) registriert wird, greift irgendwann das Energiemanagement, um eine Tiefentladung der Zellen zu verhindern.

          Interessant wäre nun zu wissen:
          • Auf wie viel % steht dein minSoc aktuell?
          • Welchen Wert hat/haben die Batterie(n) dabei bei minVol (niedrigste Zellspannung)?

          Zusätzlich wäre es gut zu beobachten, wie sich die originale App in diesem Zustand verhält:

          • Meldet die App das Gerät zu diesem Zeitpunkt noch als online?
          • Lassen sich über die App noch Werte verändern?

          Falls ja, wäre wichtig festzustellen, ob das Gerät durch die App-Aktivität kurzzeitig aus dem Tiefschlaf erwacht und diese Befehle dann auch tatsächlich ausführt.
          Das würde uns zeigen, ob das Gerät im Schlafmodus noch auf bestimmte Wake-up-Signale (z. B. via Bluetooth oder die Cloud-Schnittstelle) reagiert.

          edit/PS: ist die Akku-Heizung aktiv?

          D
          D
          Daniel 8
          schrieb am zuletzt editiert von
          #664

          @maxclaudi sagte:

          @Daniel-8 sagte:

          Nachdem ich jetzt ein paar Tage den Fehler Zendure IP überprüfen beobachtet habe, kann ich folgendes feststelln.

          Der Fehler tritt immer auf, wenn das Gerät am Abend mit Sonnenuntergang bzw. PV Eingang 0 Watt hat und wegen dem Wetter so wenig ertrag kam, das er auch gleich auf SOC min ist. Somit werden über sehr lange Zeit keine Steuerbefehle mehr gesendet und ich vermute, das das Gerät dann nach ca.13 Stunden in einen Tiefschlaf geht.

          Das ist absolut nachvollziehbar.
          Das Abschalten des WLAN-Moduls oder das Versetzen der MCU in einen tiefen Schlafmodus ist eine gängige Schutzfunktion, da die Netzwerkkommunikation einen nicht unerheblichen Teil des Eigenstrombedarfs des Geräts ausmacht.
          Wenn über Stunden keine Aktivität (weder PV-Ertrag noch Steuerbefehle) registriert wird, greift irgendwann das Energiemanagement, um eine Tiefentladung der Zellen zu verhindern.

          Interessant wäre nun zu wissen:
          • Auf wie viel % steht dein minSoc aktuell?

          Der steht auf 50%

          • Welchen Wert hat/haben die Batterie(n) dabei bei minVol (niedrigste Zellspannung)?

          Das habe ich nicht geschaut. Wenn es dir wichtig ist, kann ich das gerne noch machen.

          Zusätzlich wäre es gut zu beobachten, wie sich die originale App in diesem Zustand verhält:

          • Meldet die App das Gerät zu diesem Zeitpunkt noch als online?

          Nein die App meldet ihn als Offline und versucht per Bluetooth zu verbinden was aber auch nicht funktioniert.

          • Lassen sich über die App noch Werte verändern?

          Ob man Werte verändern kann habe ich nicht getestet. Kann ich aber gerne nachholen.

          Falls ja, wäre wichtig festzustellen, ob das Gerät durch die App-Aktivität kurzzeitig aus dem Tiefschlaf erwacht und diese Befehle dann auch tatsächlich ausführt.
          Das würde uns zeigen, ob das Gerät im Schlafmodus noch auf bestimmte Wake-up-Signale (z. B. via Bluetooth oder die Cloud-Schnittstelle) reagiert.

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

          maxclaudiM 1 Antwort Letzte Antwort
          0
          • D Daniel 8

            @maxclaudi sagte:

            @Daniel-8 sagte:

            Nachdem ich jetzt ein paar Tage den Fehler Zendure IP überprüfen beobachtet habe, kann ich folgendes feststelln.

            Der Fehler tritt immer auf, wenn das Gerät am Abend mit Sonnenuntergang bzw. PV Eingang 0 Watt hat und wegen dem Wetter so wenig ertrag kam, das er auch gleich auf SOC min ist. Somit werden über sehr lange Zeit keine Steuerbefehle mehr gesendet und ich vermute, das das Gerät dann nach ca.13 Stunden in einen Tiefschlaf geht.

            Das ist absolut nachvollziehbar.
            Das Abschalten des WLAN-Moduls oder das Versetzen der MCU in einen tiefen Schlafmodus ist eine gängige Schutzfunktion, da die Netzwerkkommunikation einen nicht unerheblichen Teil des Eigenstrombedarfs des Geräts ausmacht.
            Wenn über Stunden keine Aktivität (weder PV-Ertrag noch Steuerbefehle) registriert wird, greift irgendwann das Energiemanagement, um eine Tiefentladung der Zellen zu verhindern.

            Interessant wäre nun zu wissen:
            • Auf wie viel % steht dein minSoc aktuell?

            Der steht auf 50%

            • Welchen Wert hat/haben die Batterie(n) dabei bei minVol (niedrigste Zellspannung)?

            Das habe ich nicht geschaut. Wenn es dir wichtig ist, kann ich das gerne noch machen.

            Zusätzlich wäre es gut zu beobachten, wie sich die originale App in diesem Zustand verhält:

            • Meldet die App das Gerät zu diesem Zeitpunkt noch als online?

            Nein die App meldet ihn als Offline und versucht per Bluetooth zu verbinden was aber auch nicht funktioniert.

            • Lassen sich über die App noch Werte verändern?

            Ob man Werte verändern kann habe ich nicht getestet. Kann ich aber gerne nachholen.

            Falls ja, wäre wichtig festzustellen, ob das Gerät durch die App-Aktivität kurzzeitig aus dem Tiefschlaf erwacht und diese Befehle dann auch tatsächlich ausführt.
            Das würde uns zeigen, ob das Gerät im Schlafmodus noch auf bestimmte Wake-up-Signale (z. B. via Bluetooth oder die Cloud-Schnittstelle) reagiert.

            maxclaudiM
            maxclaudiM
            maxclaudi
            schrieb am zuletzt editiert von
            #665

            @Daniel-8
            Deine Rückmeldung bestärkt meine Vermutung.
            Dein Akku steht ja draußen im Kalten?

            Wenn ja, tippe ich stark darauf, dass die integrierte Akkuheizung aktiv war.

            Die Heizung zieht extrem viel Strom.
            Wenn das Wetter tagsüber so schlecht war, dass der Akku ohnehin kaum geladen wurde, und nachts die Heizung anspringt, ist der Energiebedarf gigantisch.
            Um zu verhindern, dass die Heizung den Akku im Winter komplett leersaugt (selbst wenn du den Schutz auf 50 % minSoc gestellt hast), greift vermutlich der Sicherheitsmechanismus der Firmware:
            Bei Erreichen des Limits wird dem gesamten System - inklusive WLAN - komplett der Saft abgedreht, um die Zellen zu schützen.
            Ein SolarFlow 800 Pro geht normalerweise nicht einfach so grundlos in einen so tiefen Schlaf, dass selbst WLAN abgeschaltet wird – es sei denn, ein interner Großverbraucher wie die Heizung hat das System in den Not-Standby gezwungen.
            Da die App das Gerät als offline anzeigt, brauchst du auch keine Wertänderungen testen.
            Falls du doch mal die Gelegenheit hast, schau morgens (wenn das Gerät erwacht) mal auf die Werte und ob Heizung aktiv ist.
            Die Temperaturen und vor allem die Zellspannungen (minVol) wären dennoch extrem interessant.
            So kann man sehen, ob die tatsächlichen Werte mit den 50 % SoC übereinstimmen oder ob die SoC-Berechnung aufgrund einer fehlenden Kalibrierung einfach nicht mehr stimmt.

            Du solltest deinen Akku am besten einpacken (isolieren) oder anderweitig vor den tieferen Temperaturen schützen, um diesen massiven nächtlichen Energieverlust zu verhindern.

            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
              Deine Rückmeldung bestärkt meine Vermutung.
              Dein Akku steht ja draußen im Kalten?

              Wenn ja, tippe ich stark darauf, dass die integrierte Akkuheizung aktiv war.

              Die Heizung zieht extrem viel Strom.
              Wenn das Wetter tagsüber so schlecht war, dass der Akku ohnehin kaum geladen wurde, und nachts die Heizung anspringt, ist der Energiebedarf gigantisch.
              Um zu verhindern, dass die Heizung den Akku im Winter komplett leersaugt (selbst wenn du den Schutz auf 50 % minSoc gestellt hast), greift vermutlich der Sicherheitsmechanismus der Firmware:
              Bei Erreichen des Limits wird dem gesamten System - inklusive WLAN - komplett der Saft abgedreht, um die Zellen zu schützen.
              Ein SolarFlow 800 Pro geht normalerweise nicht einfach so grundlos in einen so tiefen Schlaf, dass selbst WLAN abgeschaltet wird – es sei denn, ein interner Großverbraucher wie die Heizung hat das System in den Not-Standby gezwungen.
              Da die App das Gerät als offline anzeigt, brauchst du auch keine Wertänderungen testen.
              Falls du doch mal die Gelegenheit hast, schau morgens (wenn das Gerät erwacht) mal auf die Werte und ob Heizung aktiv ist.
              Die Temperaturen und vor allem die Zellspannungen (minVol) wären dennoch extrem interessant.
              So kann man sehen, ob die tatsächlichen Werte mit den 50 % SoC übereinstimmen oder ob die SoC-Berechnung aufgrund einer fehlenden Kalibrierung einfach nicht mehr stimmt.

              Du solltest deinen Akku am besten einpacken (isolieren) oder anderweitig vor den tieferen Temperaturen schützen, um diesen massiven nächtlichen Energieverlust zu verhindern.

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

              @maxclaudi sagte:

              @Daniel-8
              Deine Rückmeldung bestärkt meine Vermutung.
              Dein Akku steht ja draußen im Kalten?

              Ja der steht draußen.

              Wenn ja, tippe ich stark darauf, dass die integrierte Akkuheizung aktiv war.

              Aber war es bis jetzt nicht so, das die Heizung erst angeht, wenn er unter 0° hat und laden will?

              Ich hatte noch nie gesehen, das die Akkuheizung nachts an war. Zudem diese ja sowieso nur mit AC funktioniert. Man kann die Heizung nicht über Solarenergie betreiben.

              Die Heizung zieht extrem viel Strom.
              Wenn das Wetter tagsüber so schlecht war, dass der Akku ohnehin kaum geladen wurde, und nachts die Heizung anspringt, ist der Energiebedarf gigantisch.
              Um zu verhindern, dass die Heizung den Akku im Winter komplett leersaugt (selbst wenn du den Schutz auf 50 % minSoc gestellt hast), greift vermutlich der Sicherheitsmechanismus der Firmware:
              Bei Erreichen des Limits wird dem gesamten System - inklusive WLAN - komplett der Saft abgedreht, um die Zellen zu schützen.
              Ein SolarFlow 800 Pro geht normalerweise nicht einfach so grundlos in einen so tiefen Schlaf, dass selbst WLAN abgeschaltet wird – es sei denn, ein interner Großverbraucher wie die Heizung hat das System in den Not-Standby gezwungen.
              Da die App das Gerät als offline anzeigt, brauchst du auch keine Wertänderungen testen.
              Falls du doch mal die Gelegenheit hast, schau morgens (wenn das Gerät erwacht) mal auf die Werte und ob Heizung aktiv ist.

              Auf welche werte meinst du?

              Die Temperaturen und vor allem die Zellspannungen (minVol) wären dennoch extrem interessant.
              So kann man sehen, ob die tatsächlichen Werte mit den 50 % SoC übereinstimmen oder ob die SoC-Berechnung aufgrund einer fehlenden Kalibrierung einfach nicht mehr stimmt.

              Kann ich mal schauen. Die Kalibrierung ist erst 5 Tage her.

              Du solltest deinen Akku am besten einpacken (isolieren) oder anderweitig vor den tieferen Temperaturen schützen, um diesen massiven nächtlichen Energieverlust zu verhindern.

              Ich habe nur ca. 2 Prozent über Nacht Verlust

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

              maxclaudiM 1 Antwort Letzte Antwort
              0
              • B
                B
                Berny-K
                schrieb am zuletzt editiert von
                #667

                Hallo guten Morgen,
                ich hatte mit meunemSF800Pro die Tage das gleiche Problem, dass der offline ging und ich erinnere mich, dass ich das letzten Winter auch schon mal hatte, die gleichen Symptome wie hier beschrieben. Ich habe dann den Netzstecker gezogen, die Module abgeklemmt und neu gestartet. Danach wieder alles abgeklemmt und dann hat wieder alles funktioniert. Mein SF800Pro steht allerdings im warmen, da läuft sicher keine Heizung.

                D maxclaudiM 2 Antworten Letzte Antwort
                0
                • B Berny-K

                  Hallo guten Morgen,
                  ich hatte mit meunemSF800Pro die Tage das gleiche Problem, dass der offline ging und ich erinnere mich, dass ich das letzten Winter auch schon mal hatte, die gleichen Symptome wie hier beschrieben. Ich habe dann den Netzstecker gezogen, die Module abgeklemmt und neu gestartet. Danach wieder alles abgeklemmt und dann hat wieder alles funktioniert. Mein SF800Pro steht allerdings im warmen, da läuft sicher keine Heizung.

                  D
                  D
                  Daniel 8
                  schrieb am zuletzt editiert von
                  #668

                  @Berny-K sagte:

                  Hallo guten Morgen,
                  ich hatte mit meunemSF800Pro die Tage das gleiche Problem, dass der offline ging und ich erinnere mich, dass ich das letzten Winter auch schon mal hatte, die gleichen Symptome wie hier beschrieben. Ich habe dann den Netzstecker gezogen, die Module abgeklemmt und neu gestartet. Danach wieder alles abgeklemmt und dann hat wieder alles funktioniert. Mein SF800Pro steht allerdings im warmen, da läuft sicher keine Heizung.

                  Für mich ist das auch kein Problem. Es war eigentlich nur eine feststellung und auch Mitteilung damit andere auch informiert sind und sich nicht über das verhalten wundern

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

                  maxclaudiM 2 Antworten Letzte Antwort
                  1
                  • D Daniel 8

                    @maxclaudi sagte:

                    @Daniel-8
                    Deine Rückmeldung bestärkt meine Vermutung.
                    Dein Akku steht ja draußen im Kalten?

                    Ja der steht draußen.

                    Wenn ja, tippe ich stark darauf, dass die integrierte Akkuheizung aktiv war.

                    Aber war es bis jetzt nicht so, das die Heizung erst angeht, wenn er unter 0° hat und laden will?

                    Ich hatte noch nie gesehen, das die Akkuheizung nachts an war. Zudem diese ja sowieso nur mit AC funktioniert. Man kann die Heizung nicht über Solarenergie betreiben.

                    Die Heizung zieht extrem viel Strom.
                    Wenn das Wetter tagsüber so schlecht war, dass der Akku ohnehin kaum geladen wurde, und nachts die Heizung anspringt, ist der Energiebedarf gigantisch.
                    Um zu verhindern, dass die Heizung den Akku im Winter komplett leersaugt (selbst wenn du den Schutz auf 50 % minSoc gestellt hast), greift vermutlich der Sicherheitsmechanismus der Firmware:
                    Bei Erreichen des Limits wird dem gesamten System - inklusive WLAN - komplett der Saft abgedreht, um die Zellen zu schützen.
                    Ein SolarFlow 800 Pro geht normalerweise nicht einfach so grundlos in einen so tiefen Schlaf, dass selbst WLAN abgeschaltet wird – es sei denn, ein interner Großverbraucher wie die Heizung hat das System in den Not-Standby gezwungen.
                    Da die App das Gerät als offline anzeigt, brauchst du auch keine Wertänderungen testen.
                    Falls du doch mal die Gelegenheit hast, schau morgens (wenn das Gerät erwacht) mal auf die Werte und ob Heizung aktiv ist.

                    Auf welche werte meinst du?

                    Die Temperaturen und vor allem die Zellspannungen (minVol) wären dennoch extrem interessant.
                    So kann man sehen, ob die tatsächlichen Werte mit den 50 % SoC übereinstimmen oder ob die SoC-Berechnung aufgrund einer fehlenden Kalibrierung einfach nicht mehr stimmt.

                    Kann ich mal schauen. Die Kalibrierung ist erst 5 Tage her.

                    Du solltest deinen Akku am besten einpacken (isolieren) oder anderweitig vor den tieferen Temperaturen schützen, um diesen massiven nächtlichen Energieverlust zu verhindern.

                    Ich habe nur ca. 2 Prozent über Nacht Verlust

                    maxclaudiM
                    maxclaudiM
                    maxclaudi
                    schrieb am zuletzt editiert von
                    #669

                    @Daniel-8 sagte:
                    Auf welche werte meinst du?

                    packData.xxxx.

                    • maxTemp
                    • maxVol
                    • minVol

                    properties:

                    • BatVolt
                    • electricLevel
                    • faultLevel
                    • heatState
                    • hyperTmp
                    • is_error
                    • socStatus
                    • dataReady

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

                    neue Zendure Geräte Cloudless: zURLtool V2.0

                    D 1 Antwort Letzte Antwort
                    0
                    • B Berny-K

                      Hallo guten Morgen,
                      ich hatte mit meunemSF800Pro die Tage das gleiche Problem, dass der offline ging und ich erinnere mich, dass ich das letzten Winter auch schon mal hatte, die gleichen Symptome wie hier beschrieben. Ich habe dann den Netzstecker gezogen, die Module abgeklemmt und neu gestartet. Danach wieder alles abgeklemmt und dann hat wieder alles funktioniert. Mein SF800Pro steht allerdings im warmen, da läuft sicher keine Heizung.

                      maxclaudiM
                      maxclaudiM
                      maxclaudi
                      schrieb am zuletzt editiert von
                      #670

                      @Berny-K
                      Nun, das ist – verzeih mir die Ausdrucksweise – Hardcore für die Hardware.
                      Das funktioniert und bekämpft die Symptome, aber es behebt nicht die Ursache ;-)

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

                      neue Zendure Geräte Cloudless: zURLtool V2.0

                      B 1 Antwort Letzte Antwort
                      0
                      • D Daniel 8

                        @Berny-K sagte:

                        Hallo guten Morgen,
                        ich hatte mit meunemSF800Pro die Tage das gleiche Problem, dass der offline ging und ich erinnere mich, dass ich das letzten Winter auch schon mal hatte, die gleichen Symptome wie hier beschrieben. Ich habe dann den Netzstecker gezogen, die Module abgeklemmt und neu gestartet. Danach wieder alles abgeklemmt und dann hat wieder alles funktioniert. Mein SF800Pro steht allerdings im warmen, da läuft sicher keine Heizung.

                        Für mich ist das auch kein Problem. Es war eigentlich nur eine feststellung und auch Mitteilung damit andere auch informiert sind und sich nicht über das verhalten wundern

                        maxclaudiM
                        maxclaudiM
                        maxclaudi
                        schrieb am zuletzt editiert von
                        #671

                        @Daniel-8

                        zum Vergleich:
                        aktuell ist das 2400ACpro-Testsystem auf der Terrasse mit
                        • hyperTmp 9°C
                        • maxTemp 8°C
                        Wie es dann in der Nacht aussah kann man sich vorstellen.

                        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

                          zum Vergleich:
                          aktuell ist das 2400ACpro-Testsystem auf der Terrasse mit
                          • hyperTmp 9°C
                          • maxTemp 8°C
                          Wie es dann in der Nacht aussah kann man sich vorstellen.

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

                          @maxclaudi sagte:

                          @Daniel-8

                          zum Vergleich:
                          aktuell ist das 2400ACpro-Testsystem auf der Terrasse mit
                          • hyperTmp 9°C

                          Bei mir aktuell 20°C

                          • maxTemp 8°C

                          Im Moment auch 7 und 6.

                          Wie es dann in der Nacht aussah kann man sich vorstellen.

                          Nachts vermutlich bisschen weniger. Hatten heute Morgen 2°C Außentemperatur.

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

                          1 Antwort Letzte Antwort
                          0
                          • D Daniel 8

                            @Berny-K sagte:

                            Hallo guten Morgen,
                            ich hatte mit meunemSF800Pro die Tage das gleiche Problem, dass der offline ging und ich erinnere mich, dass ich das letzten Winter auch schon mal hatte, die gleichen Symptome wie hier beschrieben. Ich habe dann den Netzstecker gezogen, die Module abgeklemmt und neu gestartet. Danach wieder alles abgeklemmt und dann hat wieder alles funktioniert. Mein SF800Pro steht allerdings im warmen, da läuft sicher keine Heizung.

                            Für mich ist das auch kein Problem. Es war eigentlich nur eine feststellung und auch Mitteilung damit andere auch informiert sind und sich nicht über das verhalten wundern

                            maxclaudiM
                            maxclaudiM
                            maxclaudi
                            schrieb am zuletzt editiert von maxclaudi
                            #673

                            @Daniel-8 sagte:
                            Für mich ist das auch kein Problem. Es war eigentlich nur eine feststellung und auch Mitteilung damit andere auch informiert sind und sich nicht über das verhalten wundern

                            Finde das für Alle eine wertvolle Information.

                            Wenn sowieso Standby ist und es absolut nichts zu tun gibt, dann ist dieses Verhalten der Firmware aus Sicht des Energiemanagements doch nahezu ideal, oder nicht?

                            WLAN und Bluetooth im Dauer-Standby ziehen über die vielen Stunden der Dunkelheit nur unnötig Strom aus den Zellen.
                            Die MCU komplett schlafen zu legen und morgens rein hardwareseitig über den ersten PV-Strom wieder zu wecken, ist die effizienteste Methode, um den Akku im Winter zu schonen. Man benötigt ja keine Live-Infos auf dem Dashboard, wenn es ohnehin nichts zu steuern gibt. Sobald das Gerät morgens wieder von allein erwacht, ist alles perfekt gelöst.

                            Hätte man es als Software-Architekt besser lösen können?
                            Persönlich denke ich: Nein, im Kern nicht. Das harte Abschalten ist die sicherste Methode. Wenn die Verbindung gekappt ist, kann kein (fehlerhaftes) externes Skript oder eine hängende Cloud-Verbindung das System nachts ungewollt wachhalten und den Akku leersaugen.

                            Das Einzige, was ich als Entwickler zusätzlich implementiert hätte, wäre eine saubere Status-Meldung vor dem Einschlafen: Dass das Gerät einen letzten MQTT- oder HTTP-Befehl absetzt ("Inaktivität - gehe in Deep-Sleep"), bevor es die Funkmodule abschaltet. Dann würde die App oder ein Skript nicht "Verbindung verloren / Fehler" melden, sondern den sauberen Status "Schläft tief (Warten auf Sonne)" ausgeben.

                            edit/PS:
                            ich hätte mir allerdings schon zusätzlich Gedanken gemacht, ob es nicht sinnvoll gewesen wäre, eine echte Sicherheitsabfrage einzubauen.
                            Ideal wäre eine Logik wie: „Standby vorgesehen in X Minuten/Sekunden“. Wenn innerhalb dieses definierten Zeitfensters kein aktiver Wachhaltebefehl (wie z. B. ein neuer Lade- oder Steuerbefehl) vom Skript oder der App reinkommt, fährt das System erst dann kontrolliert in den Deep-Sleep.

                            So wie es jetzt gelöst ist, ist es eben ein sehr harter, kompromissloser Schnitt der Firmware, ohne dem lokalen System noch eine Chance zur Reaktion zu geben.

                            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

                              @Berny-K
                              Nun, das ist – verzeih mir die Ausdrucksweise – Hardcore für die Hardware.
                              Das funktioniert und bekämpft die Symptome, aber es behebt nicht die Ursache ;-)

                              B
                              B
                              Berny-K
                              schrieb am zuletzt editiert von
                              #674

                              @maxclaudi
                              Da gib ich dir absolut recht aber ich wusste mir einfach nicht anders zu helfen. Werde das mal beobachten, wenn es wieder passiert, ob er von selbst wieder online geht sobald genügend PV-Leistung anliegt.

                              maxclaudiM 1 Antwort Letzte Antwort
                              0
                              • B Berny-K

                                @maxclaudi
                                Da gib ich dir absolut recht aber ich wusste mir einfach nicht anders zu helfen. Werde das mal beobachten, wenn es wieder passiert, ob er von selbst wieder online geht sobald genügend PV-Leistung anliegt.

                                maxclaudiM
                                maxclaudiM
                                maxclaudi
                                schrieb am zuletzt editiert von maxclaudi
                                #675

                                @Berny-K
                                Alles gut, man erschrickt ja erst mal, wenn das System plötzlich totzuspringen scheint.
                                Beobachte das beim nächsten Mal einfach ganz entspannt.
                                Sobald die Panels morgens wieder um die ca. 40 Watt liefern, sollte der Eigenverbrauch gedeckt sein und das Gerät taucht von alleine wieder im WLAN auf. Der harte Reboot sollte nicht nötig sein.

                                edit/PS:
                                Wundere dich aber nicht über die Zahlen in der App: Das System zieht den internen Eigenverbrauch ab und meldet dann meist nur ca. 10 Watt Einspeisung.

                                edit/PPS:
                                @Berny-K (und @Daniel-8)
                                Noch eine Vermutung von mir:
                                Ich denke nicht, dass so ein radikales Steckerziehen überhaupt nötig ist, wenn man das System mal zwischendurch manuell wecken möchte.
                                Theoretisch müsste es reichen, das System kurz aufzuwecken, indem man die physische IoT-Taste am Gerät kurz 1 oder 2x betätigt.
                                Das sollte die MCU und die Funkmodule (zumindest für paar Sekunden/Minuten) manuell triggern, ohne dass man die ganze Hardware unter Last abklemmen muss.
                                Hat das von euch schon mal jemand in diesem Zustand ausprobiert?

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

                                neue Zendure Geräte Cloudless: zURLtool V2.0

                                D B 2 Antworten Letzte Antwort
                                0
                                • maxclaudiM maxclaudi

                                  @Daniel-8 sagte:
                                  Für mich ist das auch kein Problem. Es war eigentlich nur eine feststellung und auch Mitteilung damit andere auch informiert sind und sich nicht über das verhalten wundern

                                  Finde das für Alle eine wertvolle Information.

                                  Wenn sowieso Standby ist und es absolut nichts zu tun gibt, dann ist dieses Verhalten der Firmware aus Sicht des Energiemanagements doch nahezu ideal, oder nicht?

                                  WLAN und Bluetooth im Dauer-Standby ziehen über die vielen Stunden der Dunkelheit nur unnötig Strom aus den Zellen.
                                  Die MCU komplett schlafen zu legen und morgens rein hardwareseitig über den ersten PV-Strom wieder zu wecken, ist die effizienteste Methode, um den Akku im Winter zu schonen. Man benötigt ja keine Live-Infos auf dem Dashboard, wenn es ohnehin nichts zu steuern gibt. Sobald das Gerät morgens wieder von allein erwacht, ist alles perfekt gelöst.

                                  Hätte man es als Software-Architekt besser lösen können?
                                  Persönlich denke ich: Nein, im Kern nicht. Das harte Abschalten ist die sicherste Methode. Wenn die Verbindung gekappt ist, kann kein (fehlerhaftes) externes Skript oder eine hängende Cloud-Verbindung das System nachts ungewollt wachhalten und den Akku leersaugen.

                                  Das Einzige, was ich als Entwickler zusätzlich implementiert hätte, wäre eine saubere Status-Meldung vor dem Einschlafen: Dass das Gerät einen letzten MQTT- oder HTTP-Befehl absetzt ("Inaktivität - gehe in Deep-Sleep"), bevor es die Funkmodule abschaltet. Dann würde die App oder ein Skript nicht "Verbindung verloren / Fehler" melden, sondern den sauberen Status "Schläft tief (Warten auf Sonne)" ausgeben.

                                  edit/PS:
                                  ich hätte mir allerdings schon zusätzlich Gedanken gemacht, ob es nicht sinnvoll gewesen wäre, eine echte Sicherheitsabfrage einzubauen.
                                  Ideal wäre eine Logik wie: „Standby vorgesehen in X Minuten/Sekunden“. Wenn innerhalb dieses definierten Zeitfensters kein aktiver Wachhaltebefehl (wie z. B. ein neuer Lade- oder Steuerbefehl) vom Skript oder der App reinkommt, fährt das System erst dann kontrolliert in den Deep-Sleep.

                                  So wie es jetzt gelöst ist, ist es eben ein sehr harter, kompromissloser Schnitt der Firmware, ohne dem lokalen System noch eine Chance zur Reaktion zu geben.

                                  D
                                  D
                                  Daniel 8
                                  schrieb am zuletzt editiert von
                                  #676

                                  @maxclaudi sagte:

                                  Wenn sowieso Standby ist und es absolut nichts zu tun gibt, dann ist dieses Verhalten der Firmware aus Sicht des Energiemanagements doch nahezu ideal, oder nicht?

                                  Ja ideal ist es mehr oder weniger gut Die Zeit mit ca. 13 Stunden ist schon lange. Er ist grad mal so ne Stunde im Tiefschlaf.

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

                                  1 Antwort Letzte Antwort
                                  0
                                  • maxclaudiM maxclaudi

                                    @Berny-K
                                    Alles gut, man erschrickt ja erst mal, wenn das System plötzlich totzuspringen scheint.
                                    Beobachte das beim nächsten Mal einfach ganz entspannt.
                                    Sobald die Panels morgens wieder um die ca. 40 Watt liefern, sollte der Eigenverbrauch gedeckt sein und das Gerät taucht von alleine wieder im WLAN auf. Der harte Reboot sollte nicht nötig sein.

                                    edit/PS:
                                    Wundere dich aber nicht über die Zahlen in der App: Das System zieht den internen Eigenverbrauch ab und meldet dann meist nur ca. 10 Watt Einspeisung.

                                    edit/PPS:
                                    @Berny-K (und @Daniel-8)
                                    Noch eine Vermutung von mir:
                                    Ich denke nicht, dass so ein radikales Steckerziehen überhaupt nötig ist, wenn man das System mal zwischendurch manuell wecken möchte.
                                    Theoretisch müsste es reichen, das System kurz aufzuwecken, indem man die physische IoT-Taste am Gerät kurz 1 oder 2x betätigt.
                                    Das sollte die MCU und die Funkmodule (zumindest für paar Sekunden/Minuten) manuell triggern, ohne dass man die ganze Hardware unter Last abklemmen muss.
                                    Hat das von euch schon mal jemand in diesem Zustand ausprobiert?

                                    D
                                    D
                                    Daniel 8
                                    schrieb am zuletzt editiert von
                                    #677

                                    @maxclaudi sagte:

                                    Theoretisch müsste es reichen, das System kurz aufzuwecken, indem man die physische IoT-Taste am Gerät kurz 1 oder 2x betätigt.
                                    Das sollte die MCU und die Funkmodule (zumindest für paar Sekunden/Minuten) manuell triggern, ohne dass man die ganze Hardware unter Last abklemmen muss.
                                    Hat das von euch schon mal jemand in diesem Zustand ausprobiert?

                                    Nein weil ich sehr schlecht hin komme wo der Speicher steht.

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

                                    maxclaudiM 1 Antwort Letzte Antwort
                                    0
                                    • D Daniel 8

                                      @maxclaudi sagte:

                                      Theoretisch müsste es reichen, das System kurz aufzuwecken, indem man die physische IoT-Taste am Gerät kurz 1 oder 2x betätigt.
                                      Das sollte die MCU und die Funkmodule (zumindest für paar Sekunden/Minuten) manuell triggern, ohne dass man die ganze Hardware unter Last abklemmen muss.
                                      Hat das von euch schon mal jemand in diesem Zustand ausprobiert?

                                      Nein weil ich sehr schlecht hin komme wo der Speicher steht.

                                      maxclaudiM
                                      maxclaudiM
                                      maxclaudi
                                      schrieb am zuletzt editiert von
                                      #678

                                      @Daniel-8 sagte:
                                      Nein weil ich sehr schlecht hin komme wo der Speicher steht.

                                      Für so ein Szenario fällt mir spontan ein kleiner Fingerbot mit Akku ein, den man einfach über das Handy oder ioBroker triggert! :-)))

                                      Der drückt dann per Funk den Knopf für dich, und du musst nicht jedes Mal in die schwer zugängliche Ecke krabbeln.
                                      Das wäre eine Smart-Home-Lösung für das "Deep-Sleep-Problem" im Winter :-)

                                      War nur scherzhaft ;-)

                                      Wobei - mich erinnert das Ganze an die Anfangszeit mit dem HUB2000 und dem ACE1500.
                                      Da gab es auch Fälle, in denen nur vor Ort ein physisches Aus- und wieder Einschalten half.
                                      Weil ich öfter nicht vor Ort bin, ist so etwas für mich der absolute Worst Case. Ich war damals tatsächlich kurz drauf und dran, einen Fingerbot zu verwenden.

                                      Aber der Hub ließ sich - im Gegensatz zu den neuen Geräten - noch zerstörungsfrei öffnen.
                                      Ich habe damals einfach einen kleinen ESP8266 mit Optokoppler parallel zur physischen IoT-Taste gelötet, um eine zuverlässige Fernsteuerung zu ermöglichen.

                                      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

                                        @Berny-K
                                        Alles gut, man erschrickt ja erst mal, wenn das System plötzlich totzuspringen scheint.
                                        Beobachte das beim nächsten Mal einfach ganz entspannt.
                                        Sobald die Panels morgens wieder um die ca. 40 Watt liefern, sollte der Eigenverbrauch gedeckt sein und das Gerät taucht von alleine wieder im WLAN auf. Der harte Reboot sollte nicht nötig sein.

                                        edit/PS:
                                        Wundere dich aber nicht über die Zahlen in der App: Das System zieht den internen Eigenverbrauch ab und meldet dann meist nur ca. 10 Watt Einspeisung.

                                        edit/PPS:
                                        @Berny-K (und @Daniel-8)
                                        Noch eine Vermutung von mir:
                                        Ich denke nicht, dass so ein radikales Steckerziehen überhaupt nötig ist, wenn man das System mal zwischendurch manuell wecken möchte.
                                        Theoretisch müsste es reichen, das System kurz aufzuwecken, indem man die physische IoT-Taste am Gerät kurz 1 oder 2x betätigt.
                                        Das sollte die MCU und die Funkmodule (zumindest für paar Sekunden/Minuten) manuell triggern, ohne dass man die ganze Hardware unter Last abklemmen muss.
                                        Hat das von euch schon mal jemand in diesem Zustand ausprobiert?

                                        B
                                        B
                                        Berny-K
                                        schrieb am zuletzt editiert von
                                        #679

                                        @maxclaudi
                                        Bei mir hat das mit der IoT Taste leider auch nicht funktioniert.Ich konnte ihn auch nach mehreren Versuchen damit nicht wieder aufwecken.

                                        1 Antwort Letzte Antwort
                                        1
                                        • maxclaudiM maxclaudi

                                          @Daniel-8 sagte:
                                          Nein weil ich sehr schlecht hin komme wo der Speicher steht.

                                          Für so ein Szenario fällt mir spontan ein kleiner Fingerbot mit Akku ein, den man einfach über das Handy oder ioBroker triggert! :-)))

                                          Der drückt dann per Funk den Knopf für dich, und du musst nicht jedes Mal in die schwer zugängliche Ecke krabbeln.
                                          Das wäre eine Smart-Home-Lösung für das "Deep-Sleep-Problem" im Winter :-)

                                          War nur scherzhaft ;-)

                                          Wobei - mich erinnert das Ganze an die Anfangszeit mit dem HUB2000 und dem ACE1500.
                                          Da gab es auch Fälle, in denen nur vor Ort ein physisches Aus- und wieder Einschalten half.
                                          Weil ich öfter nicht vor Ort bin, ist so etwas für mich der absolute Worst Case. Ich war damals tatsächlich kurz drauf und dran, einen Fingerbot zu verwenden.

                                          Aber der Hub ließ sich - im Gegensatz zu den neuen Geräten - noch zerstörungsfrei öffnen.
                                          Ich habe damals einfach einen kleinen ESP8266 mit Optokoppler parallel zur physischen IoT-Taste gelötet, um eine zuverlässige Fernsteuerung zu ermöglichen.

                                          D
                                          D
                                          Daniel 8
                                          schrieb am zuletzt editiert von
                                          #680

                                          @maxclaudi sagte:

                                          Für so ein Szenario fällt mir spontan ein kleiner Fingerbot mit Akku ein, den man einfach über das Handy oder ioBroker triggert! :-)))

                                          Sowas hätte ich fast schon mal gekauft aber nicht für den zendure sondern für den Growatt.

                                          Also ich habe den Tausch zu Zendure bis jetzt nicht bereut.

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

                                          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

                                          443

                                          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