NEWS
Zendure zenSDK Lokal API, SmartMode, SolarFlow AC 800 Pro 2
-
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.
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(); -
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. -
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.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?
-
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?
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. -
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.@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.
-
@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.
@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
-
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. -
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.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
-
@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
-
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. -
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
-
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.zum Vergleich:
aktuell ist das 2400ACpro-Testsystem auf der Terrasse mit
• hyperTmp 9°CBei 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.
-
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
@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 wundernFinde 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.
-
@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 ;-)@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. -
@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.@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? -
@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 wundernFinde 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.
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.
-
@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?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.
-
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.
@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. -
@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?@maxclaudi
Bei mir hat das mit der IoT Taste leider auch nicht funktioniert.Ich konnte ihn auch nach mehreren Versuchen damit nicht wieder aufwecken. -
@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.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.
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