- ioBroker Community Home
- Deutsch
- Einsteigerfragen
- Einbindung von Geräten
- Hilfe: Adapter Fronius-Wattpilot kann nur mehr lesen
Hilfe: Adapter Fronius-Wattpilot kann nur mehr lesen
-
Hmm, und wenn du den Adapter auf Debug stellst und einen Wert schreibst, kommt dann irgendeine (Fehler)-Meldung?
Adapter auf Debug stellst und einen Wert schreibst
Protokoll beim Laden = ein:
fronius-wattpilot.0
2026-08-28 13:58:22.450 debug Received message: {"type":"deltaStatus","status":{"ehs":265212,"efh":117360,"efh32":117360,"efh8":86536,"utc":"2026-08-28T11:58:20.653","loc":"2026-08-28T13:58:20.654 +02:00","rbt":105136696,"nrg":[225.9900055,226.6100006,226.6100006,2.170000076,0,0,0,0,0,0,0,0,0,0,0,0],"fhz":55.01200104}}fronius-wattpilot.0
2026-08-28 13:58:21.454 debug Received message: {"type":"deltaStatus","status":{"utc":"2026-08-28T11:58:19.655","loc":"2026-08-28T13:58:19.656 +02:00","rbt":105135700,"nrg":[226.9199982,227.2299957,226.3000031,2.170000076,0,0,0,0,0,0,0,0,0,0,0,0],"fhz":55.01300049}}fronius-wattpilot.0
2026-08-28 13:58:20.454 debug Received message: {"type":"deltaStatus","status":{"ehs":265148,"efh":117100,"efh32":117100,"efh8":86276,"utc":"2026-08-28T11:58:18.655","loc":"2026-08-28T13:58:18.656 +02:00","rbt":105134698,"nrg":[226.3000031,226.3000031,226.9199982,2.170000076,0,0,0,0,0,0,0,0,0,0,0,0],"fhz":55.01200104}}fronius-wattpilot.0
2026-08-28 13:58:19.453 debug Received message: {"type":"deltaStatus","status":{"ehs":265212,"efh":117364,"efh32":117364,"efh8":86540,"emhb":73716,"utc":"2026-08-28T11:58:17.655","loc":"2026-08-28T13:58:17.656 +02:00","rbt":105133700,"nrg":[226.3000031,226.9199982,226.3000031,2.170000076,0,0,0,0,0,0,0,0,0,0,0,0],"fhz":55.01100159}}fronius-wattpilot.0
2026-08-28 13:58:18.508 debug Command successful: {"amp":12}fronius-wattpilot.0
2026-08-28 13:58:18.508 debug Received message: {"type":"response","requestId":"7","success":true,"status":{"amp":12}}fronius-wattpilot.0
2026-08-28 13:58:18.493 debug Received message: {"type":"deltaStatus","status":{"ehs":265196,"efh":113248,"efh32":113248,"efh8":82424,"emhb":69620,"utc":"2026-08-28T11:58:16.695","loc":"2026-08-28T13:58:16.696 +02:00","rbt":105132738,"nrg":[225.9900055,226.6100006,226.6100006,2.170000076,0,0,0,0,0,0,0,0,0,0,0,0]}}fronius-wattpilot.0
2026-08-28 13:58:18.379 debug Sending secure command: {"type":"securedMsg","data":"{"type":"setValue","requestId":7,"key":"amp","value":12}","requestId":"7sm","hmac":"442557278d87f3377abb9db26e3a35019b4ae236a2b063cc387003e1d0d8fd6b"}fronius-wattpilot.0
2026-08-28 13:58:18.378 debug State change command received for fronius-wattpilot.0.set_power: {"val":12,"ack":false,"ts":1787918298374,"q":0,"c":"script.js.common._Tests.Fronius_Wallbox","from":"system.adapter.javascript.1","user":"system.user.admin","lc":1787917944709}javascript.1
2026-08-28 13:58:18.374 info script.js.common._Tests.Fronius_Wallbox: Status = true, Modus = 3, LeistungIst=0 kW, StromIst=0.0 A
Protokoll beim Laden = aus:
fronius-wattpilot.0
2026-08-28 14:02:07.451 debug Received message: {"type":"deltaStatus","status":{"utc":"2026-08-28T12:02:05.651","loc":"2026-08-28T14:02:05.652 +02:00","rbt":105361696,"nrg":[225.9900055,226.3000031,226.9199982,2.170000076,0,0,0,0,0,0,0,0,0,0,0,0]}}fronius-wattpilot.0
2026-08-28 14:02:06.463 debug Received message: {"type":"deltaStatus","status":{"efh":117096,"efh32":117096,"efh8":86272,"utc":"2026-08-28T12:02:04.661","loc":"2026-08-28T14:02:04.663 +02:00","rbt":105360705,"nrg":[226.6100006,227.2299957,225.9900055,2.170000076,0,0,0,0,0,0,0,0,0,0,0,0],"fhz":55.01200104}}fronius-wattpilot.0
2026-08-28 14:02:05.464 debug Received message: {"type":"deltaStatus","status":{"ehs":265140,"efh":117084,"efh32":117084,"efh8":86260,"utc":"2026-08-28T12:02:03.665","loc":"2026-08-28T14:02:03.666 +02:00","rbt":105359708,"nrg":[226.6100006,226.3000031,226.9199982,2.170000076,0,0,0,0,0,0,0,0,0,0,0,0],"fhz":55.01100159}}fronius-wattpilot.0
2026-08-28 14:02:04.465 debug Received message: {"type":"deltaStatus","status":{"utc":"2026-08-28T12:02:02.665","loc":"2026-08-28T14:02:02.667 +02:00","rbt":105358709,"tma":[37.5],"nrg":[225.9900055,226.9199982,226.3000031,2.170000076,0,0,0,0,0,0,0,0,0,0,0,0],"fhz":55.01200104}}fronius-wattpilot.0
2026-08-28 14:02:03.466 debug Received message: {"type":"deltaStatus","status":{"ehs":265196,"efh":117328,"efh32":117328,"efh8":86504,"utc":"2026-08-28T12:02:01.667","loc":"2026-08-28T14:02:01.668 +02:00","rbt":105357710,"tma":[38],"nrg":[226.9199982,226.9199982,226.6100006,2.170000076,0,0,0,0,0,0,0,0,0,0,0,0],"fhz":55.01300049}}fronius-wattpilot.0
2026-08-28 14:02:03.321 debug Command successful: {"amp":12}fronius-wattpilot.0
2026-08-28 14:02:03.321 debug Received message: {"type":"response","requestId":"1","success":true,"status":{"amp":12}}fronius-wattpilot.0
2026-08-28 14:02:03.272 debug Sending secure command: {"type":"securedMsg","data":"{"type":"setValue","requestId":1,"key":"amp","value":12}","requestId":"1sm","hmac":"e83cec0268b1bea004d1c760203329e4123347b1233beef8c5aeb3168f0b0d1d"}fronius-wattpilot.0
2026-08-28 14:02:03.272 debug State change command received for fronius-wattpilot.0.set_power: {"val":12,"ack":false,"ts":1787918523267,"q":0,"c":"script.js.common._Tests.Fronius_Wallbox","from":"system.adapter.javascript.1","user":"system.user.admin","lc":1787917944709}javascript.1
2026-08-28 14:02:03.267 info script.js.common._Tests.Fronius_Wallbox: Status = Wait, Modus = 0, LeistungIst=0 kW, StromIst=0.0 A
Siehst du etwas auffälliges?
Smart-Home-Automation unter IOB auf RASPI5, 16GB RAM, 50GB SSD, MariaSQL, VIS-2.
Anfang 2025 war ich Newbie in Sachen RaspBerry, Linux und IOB.
Jetzt läuft alles konstant gut und ich gebe gern meine Erfahrungen und mein Wissen an die Forums-User/Innen weiter.
Großes Danke an alle tatkräftigen Helfer im Forum! -
Bin da nicht der Experte: aber das sieht nicht soo verkehrt aus:
fronius-wattpilot.0 2026-08-28 14:02:03.321 debug Command successful: {"amp":12} fronius-wattpilot.0 2026-08-28 14:02:03.321 debug Received message: {"type":"response","requestId":"1","success":true,"status":{"amp":12}}Und in der APP ändert sich nichts an der Ampere Zahl?
Oder umgekehrt: Wenn du die Ampere-Zahl in der APP änderst, ändert sich der DP im Adapter? -
Bin da nicht der Experte: aber das sieht nicht soo verkehrt aus:
fronius-wattpilot.0 2026-08-28 14:02:03.321 debug Command successful: {"amp":12} fronius-wattpilot.0 2026-08-28 14:02:03.321 debug Received message: {"type":"response","requestId":"1","success":true,"status":{"amp":12}}Und in der APP ändert sich nichts an der Ampere Zahl?
Oder umgekehrt: Wenn du die Ampere-Zahl in der APP änderst, ändert sich der DP im Adapter?Und in der APP ändert sich nichts an der Ampere Zahl
Nein, der Datenpunkt wird gesetzt, aber in der App bleibt alles gleich.
Oder umgekehrt: Wenn du die Ampere-Zahl in der APP änderst, ändert sich der DP im Adapter
Ja, der Datenpunkt im IOB ändert sich, wenn ich die Ampere-Zahl in der APP ändere (ist ja nur lesend).
Smart-Home-Automation unter IOB auf RASPI5, 16GB RAM, 50GB SSD, MariaSQL, VIS-2.
Anfang 2025 war ich Newbie in Sachen RaspBerry, Linux und IOB.
Jetzt läuft alles konstant gut und ich gebe gern meine Erfahrungen und mein Wissen an die Forums-User/Innen weiter.
Großes Danke an alle tatkräftigen Helfer im Forum! -
Als schnelle Lösung könntest du den OCCP Adapter (OCCP im Wattpilot aktivieren) verwenden
Danke! OCCP funktioniert aber nur mit Internet und das
habe ich aus Sicherheitsgründen gesperrt. möchte ich nicht.Meine Wallbox läuft mit dem OCPP-Adapter. Internet benötige ich dafür nicht.
Proxmox und HA ...
Ich schreibe den Code nicht mehr selbst – ich schimpfe mit der KI, bis es funktioniert. -
Als schnelle Lösung könntest du den OCCP Adapter (OCCP im Wattpilot aktivieren) verwenden
Danke! OCCP funktioniert aber nur mit Internet und das
habe ich aus Sicherheitsgründen gesperrt. möchte ich nicht.Meine Wallbox läuft mit dem OCPP-Adapter. Internet benötige ich dafür nicht.
Internet benötige ich dafür nicht
Aah, danke! Welchen OCPP-Provider (Host, Pfad) verwendest du da?
Smart-Home-Automation unter IOB auf RASPI5, 16GB RAM, 50GB SSD, MariaSQL, VIS-2.
Anfang 2025 war ich Newbie in Sachen RaspBerry, Linux und IOB.
Jetzt läuft alles konstant gut und ich gebe gern meine Erfahrungen und mein Wissen an die Forums-User/Innen weiter.
Großes Danke an alle tatkräftigen Helfer im Forum! -
Internet benötige ich dafür nicht
Aah, danke! Welchen OCPP-Provider (Host, Pfad) verwendest du da?
Internet benötige ich dafür nicht
Aah, danke! Welchen OCPP-Provider (Host, Pfad) verwendest du da?
Da benötige ich keinen Provider. Die Wallbox spricht via ocpp mit ioBroker über websocket (IP und Port).
Deine Wallbox beherrscht nach meiner Recherche das Protokoll ebenfalls.Proxmox und HA ...
Ich schreibe den Code nicht mehr selbst – ich schimpfe mit der KI, bis es funktioniert. -
Internet benötige ich dafür nicht
Aah, danke! Welchen OCPP-Provider (Host, Pfad) verwendest du da?
Da benötige ich keinen Provider. Die Wallbox spricht via ocpp mit ioBroker über websocket (IP und Port).
Deine Wallbox beherrscht nach meiner Recherche das Protokoll ebenfalls.Deine Wallbox beherrscht nach meiner Recherche das Protokoll ebenfalls
Ja, das kann sie. Wie hast du diese Seite konfiguriert?

Und hättest du ein JavaScript- Beispiel?
Smart-Home-Automation unter IOB auf RASPI5, 16GB RAM, 50GB SSD, MariaSQL, VIS-2.
Anfang 2025 war ich Newbie in Sachen RaspBerry, Linux und IOB.
Jetzt läuft alles konstant gut und ich gebe gern meine Erfahrungen und mein Wissen an die Forums-User/Innen weiter.
Großes Danke an alle tatkräftigen Helfer im Forum! -
Deine Wallbox beherrscht nach meiner Recherche das Protokoll ebenfalls
Ja, das kann sie. Wie hast du diese Seite konfiguriert?

Und hättest du ein JavaScript- Beispiel?
Ich habe eine EVBox.
Die verbindet sich mit ws://IPdesioBroker:Port ohne Benutzername und Passwort mit dem ioBroker. Der Port wird im Adapter festgelegt.

Mein Ladeskript hat mehr als 700 Zeilen, aber bitte:
/* eslint-env es2022 */ /** * Name: EV3 Charge-Master * Purpose: Start/stop management for Kia EV3 wallbox charging (fixed 6A / 3.96 kW). * Modes: 1. Manual: User triggered via VIS * 2. PV Auto: Automated based on PV surplus and home battery SoC * Features: Home battery protection, debounced status handling, forced transaction stops. */ // --- 1. SETUP: DIGITALE ZENTRALE (21 DATENPUNKTE) --- const VIN = `bluelink.0.${getState("0_userdata.0.Energie.Kia_e_niro.vin")?.val}`; const PATH_USER = "0_userdata.0.Energie.Kia_e_niro"; const IDS = { // Wallbox (Hardware via OCPP) wbStat: "ocpp.0.http://192_168_178_80:9220/EVB-P21312507.1.status", // [1] Status (Charging, Preparing...) wbTrans: "ocpp.0.http://192_168_178_80:9220/EVB-P21312507.1.transactionActive", // [2] Steuert den Stromfluss (Transaktion aktiv) wbAvail: "ocpp.0.http://192_168_178_80:9220/EVB-P21312507.1.availability", // [3] Reset / Verfügbarkeit wbConn: "ocpp.0.http://192_168_178_80:9220/EVB-P21312507.connected", // Verbindung zum ioBroker unifiReconnect: "unifi-network.0.clients.users.60:09:c3:2f:46:49.reconnect", // [22] Neu verbinden über UniFi // Fahrzeugdaten (Cloud) soc: `${VIN}.vehicleStatusRaw.Green.BatteryManagement.BatteryRemain.Ratio`, // [4] Ladestand % bat12v: `${VIN}.vehicleStatusRaw.Electronics.Battery.Level`, // [5] 12V-Batterieschutz conn: `${VIN}.vehicleStatusRaw.Green.ChargingInformation.ConnectorFastening.State`, // [6] Steckerstatus remTime: `${VIN}.vehicleStatusRaw.Green.ChargingInformation.Charging.RemainTime`, // [7] Restzeit in Min (vom Fahrzeug gemeldet) targetSocSrv: `${VIN}.control.charge_limit_slow`, // [23] Ladeziel (AC) vom Fahrzeug (Steuerungspunkt) refreshCar: `${VIN}.control.force_refresh_from_car`, // [8] Fahrzeug aufwecken (Live-Daten) refreshSrv: `${VIN}.control.force_refresh_from_server`, // [8b] Server-Status abrufen // Energie-Zentrale (Hardware-Werte) pvPower: "solax.0.data.acpower", // [9] Aktuelle PV-Leistung in Watt pvAverage: "0_userdata.0.Energie.PV.Durchschnitt", // [10] Geglätteter Wert (EMA) netPower: "0_userdata.0.Energie.PV.Netzbezug", // [11] Hauszähler (+Bezug/-Einspeisung) hausCons: "0_userdata.0.Energie.PV.Hausverbrauch", // [12] Hausverbrauch batSocPV: "modbus.0.inputRegisters.100.843_Battery_State_of_Charge_(System)", // [13] Heimspeicher % minSocSet: "modbus.0.holdingRegisters.100.2901_ESS_Minimum_SoC_(unless_grid_fails)", minSocRead: "modbus.0.inputRegisters.100.2901_ESS_Minimum_SoC_(unless_grid_fails)", // Steuerung & Statistiken (VIS) u_auto: `${PATH_USER}.autoladen`, // [14] Schalter: PV-Automatik an/aus (Boolean) u_limit: `${PATH_USER}.Ladeprozent`, // [15] Ziel-SoC Schieberegler u_smooth: `${PATH_USER}.Glaettung_Zeit`, // [16] Trägheits-Schieberegler für EMA u_power: `${PATH_USER}.Ladeleistung`, // [17] Anzeige Ladeleistung (fix 3960W) u_timeDay: `${PATH_USER}.Ladezeit`, // [18] Lademinuten heute u_rest: `${PATH_USER}.Restladezeit`, // [19] HH:MM Anzeige aliasKm: "alias.0.umrechnen.kia_ladekm", // [20] Gewonnene Reichweite aliasDur: "alias.0.umrechnen.kia_ladezeit", // [21] Zeit-Objekt u_startChargeRequest: `${PATH_USER}.Start_Charge_Request`, // [NEU] Anforderung zum Starten des Ladevorgangs u_startTs: `${PATH_USER}.LastStartTimestamp`, // [PERSISTENCE] Merker für Startzeit u_origSoc: `${PATH_USER}.LastOriginalMinSoc`, // [PERSISTENCE] Merker für Batterie-Schutz }; // --- PARAMETER --- const PV_START_LIMIT = 4600; // Startgrenze (Sonne muss > 4.6kW + Puffer liefern) const PV_STOP_LIMIT = 4000; // Stoppgrenze (Ladevorgang pausieren, wenn Überschuss sinkt) const FIXED_CHARGE_W = 3960; // Fixe Leistung bei 6A (220V * 3 Phasen * 6A) const CAR_CAPACITY_KWH = 81.4; const RANGE_SUMMER = 550; const RANGE_WINTER = 450; // --- TIMING KONSTANTEN --- const DEBOUNCE_STOP_MS = 45000; // 45 Sek. Wartezeit vor endgültigem Stopp const RECONNECT_WB_MS = 180000; // 3 Min. Wartezeit vor WLAN-Neuverbindung const START_REFRESH_DELAY_MS = 75000; // 75 Sek. nach Ladestart: Fahrzeug aufwecken für Restzeit & SoC const STOP_REFRESH_DELAY_MS = 30000; // 30 Sek. nach bestätigtem Ladestopp: Finalen SoC & Status abrufen const START_VERIFY_TIMEOUT_MS = 35000; // 35 Sek. maximale Wartezeit auf "Charging"-Status nach Startbefehl const START_AVAILABILITY_TOGGLE_DELAY_MS = 2000; // 2 Sek. Pause bei Verfügbarkeitswechsel const START_HANDSHAKE_DELAY_MS = 4000; // 4 Sek. Puffer für OCPP-Handshake nach Re-Aktivierung // [NEU] Verzögerung vor dem erneuten Versuch des Stopp-Befehls nach dem ersten Versuch. const FORCE_STOP_RETRY_DELAY_MS = 5000; // [NEU] Verzögerung während des Verfügbarkeitswechsels, um der Wallbox Zeit zur Verarbeitung zu geben. const FORCE_STOP_AVAILABILITY_TOGGLE_DELAY_MS = 2000; let startZeitLaden = null; // Merker für Statistik let originalMinSoc = null; // Merker für Min-SoC bei manuellem Laden let stopTimer = null; // Timer zur Entprellung von kurzen Lade-Unterbrechungen let startRefreshTimer = null; // Timer für Statusabfrage nach Ladestart let stopRefreshTimer = null; // Timer für Statusabfrage nach Ladeende let reconnectTimer = null; // Timer für Wallbox-Recovery let wasOfflineReported = false; // Status für Anti-Spam Meldungen let hasWarnedOcppOffline = false; // Flag to prevent repeated warning logs per script run // [NEU] Sperrvariable zur Vermeidung von Race Conditions während der Startsequenz. let isStartingSequenceActive = false; // [NEU] Sperrvariable zur Vermeidung mehrerer gleichzeitiger Ausführungen von erzwungenen Stopps. let isForceStopping = false; // --- 2. INITIALISIERUNG --- async function initLadeSystem() { // Erstellt nur noch die für diese Version nötigen Punkte if (!existsState(IDS.u_auto)) await createStateAsync(IDS.u_auto, true, { type: "boolean", name: "PV Automatic", }); if (!existsState(IDS.u_smooth)) await createStateAsync(IDS.u_smooth, 10, { type: "number", name: "EMA Smoothing", }); if (!existsState(IDS.u_limit)) await createStateAsync(IDS.u_limit, 80, { type: "number", name: "Charge Target", }); // NEU: Datenpunkt für die Ladestart-Anforderung if (!existsState(IDS.u_startChargeRequest)) await createStateAsync(IDS.u_startChargeRequest, false, { type: "boolean", name: "Start Charging (Request)", role: "button", }); if (!existsState(IDS.u_startTs)) await createStateAsync(IDS.u_startTs, 0, { type: "number", name: "Start Timestamp" }); if (!existsState(IDS.u_origSoc)) await createStateAsync(IDS.u_origSoc, 0, { type: "number", name: "Original MinSoc Backup" }); // Laufende Prozesse nach Skript-Neustart wiederherstellen if (getState(IDS.wbTrans)?.val === true) { startZeitLaden = getState(IDS.u_startTs)?.val || Date.now(); const savedSoc = getState(IDS.u_origSoc)?.val; originalMinSoc = savedSoc !== null && savedSoc !== 0 ? savedSoc : null; if (getState(IDS.wbStat)?.val === "Charging") { setState(IDS.u_power, FIXED_CHARGE_W, true); } } } initLadeSystem(); // --- 2.1 HELPER: ASYNCHRONE PAUSE & MESSWERTE --- /** * Asynchroner Timer zur Ablaufverzögerung in async-Funktionen. * @param {number} ms Millisekunden */ const wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms)); /** * Wartet bis der Wallbox-Status auf "Charging" wechselt oder der Timeout abläuft. * @param {number} timeoutMs Maximale Wartezeit in Millisekunden * @returns {Promise<boolean>} True falls "Charging" erreicht wurde, sonst False */ async function waitForChargingState(timeoutMs) { const startTime = Date.now(); while (Date.now() - startTime < timeoutMs) { if (getState(IDS.wbStat)?.val === "Charging") { return true; } await wait(1000); } return getState(IDS.wbStat)?.val === "Charging"; } /** * Sammelt alle relevanten Leistungs- und SoC-Werte aus den Datenpunkten. * Stellt sicher, dass die Werte Zahlen sind. */ function getPowerMetrics() { return { pvPower: Math.max(0, Number(getState(IDS.pvPower)?.val) || 0), pvAverage: Number(getState(IDS.pvAverage)?.val) || 0, batSoc: Number(getState(IDS.batSocPV)?.val) || 0, evSoc: Number(getState(IDS.soc)?.val) || 0, }; } // --- 3. KOMMUNIKATION --- /** * Führt die intelligente Startsequenz der Wallbox inklusive Verifikations-Watchdog * und automatischem Retry-Mechanismus aus. * @param {string} reason Grund für den Start (z. B. "PV-Automatik", "VIS-Manual") */ async function triggerStartSequence(reason = "PV-Surplus") { if (isStartingSequenceActive) { console.log(`[EV3 Master] Start sequence already active, skipping (${reason}).`); return; } const wbStatus = getState(IDS.wbStat)?.val; if (wbStatus === "Charging") { console.log(`[EV3 Master] Wallbox is already charging. Skipping start sequence.`); return; } const readyToStart = ["Preparing", "Finishing", "SuspendedEVSE", "SuspendedEV"].includes( wbStatus, ); if (!readyToStart) { if (wbStatus === "Available") { // console.warn(`[EV3 Master] Start (${reason}) aborted: No vehicle detected.`); return; } } isStartingSequenceActive = true; console.log( `[EV3 Master] Starting wallbox sequence for: ${reason} (Current Status: ${wbStatus})`, ); try { // --- VERSUCH 1 --- console.log("[EV3 Master] Start attempt 1: Toggling availability and setting wbTrans = true."); setState(IDS.wbAvail, false); await wait(START_AVAILABILITY_TOGGLE_DELAY_MS); setState(IDS.wbAvail, true); await wait(START_HANDSHAKE_DELAY_MS); // Erhöhter Puffer für den OCPP-Handshake setState(IDS.wbTrans, true); let started = await waitForChargingState(START_VERIFY_TIMEOUT_MS); // --- VERSUCH 2 (AUTOMATISCHER RETRY BEI TIMEOUT / FEHLSCHLAG) --- if (!started) { const currentStatus = getState(IDS.wbStat)?.val; console.warn( `[EV3 Master] Start attempt 1 timed out. Wallbox status is '${currentStatus}'. Retrying reset sequence...`, ); setState(IDS.wbTrans, false); await wait(FORCE_STOP_RETRY_DELAY_MS); setState(IDS.wbAvail, false); await wait(START_AVAILABILITY_TOGGLE_DELAY_MS); setState(IDS.wbAvail, true); await wait(START_HANDSHAKE_DELAY_MS); setState(IDS.wbTrans, true); started = await waitForChargingState(START_VERIFY_TIMEOUT_MS); } // --- ERGEBNIS-AUSWERTUNG --- if (started) { console.log(`[EV3 Master] Wallbox start verified successfully (${reason}).`); ev3Notify(`🔋 EV3-Ladung gestartet via ${reason} mit 6A`); } else { const finalStatus = getState(IDS.wbStat)?.val; console.error( `[EV3 Master] Start failed after retry. Wallbox did not reach 'Charging' state (Status: ${finalStatus}). Reverting wbTrans.`, ); setState(IDS.wbTrans, false); ev3Notify( `⚠️ EV3 Ladestart fehlgeschlagen: Wallbox reagiert nicht (Status: ${finalStatus})`, 4, ); } } catch (e) { console.error(`[EV3 Master] Error during start sequence: ${e.message}`); setState(IDS.wbTrans, false); ev3Notify(`❌ Fehler bei Ladestart-Sequenz: ${e.message}`, 5); } finally { isStartingSequenceActive = false; } } /** * [NEU] forceStopCharging() * Versucht, den Ladevorgang zu beenden, selbst wenn der Status "hängt". * Nutzt den Verfügbarkeitswechsel (Availability-Toggle) als letzten Ausweg. * Diese Funktion wird aufgerufen, wenn `transactionActive` auf `false` gesetzt wurde, * die Wallbox aber immer noch den Status `Charging` meldet. */ async function forceStopCharging() { // Gleichzeitige Ausführung verhindern if (isForceStopping) { console.log("[EV3 Master] Force stop already active, skipping."); return; } isForceStopping = true; console.warn("[EV3 Master] Initiating forced charging stop sequence."); try { // Versuch 1: Sende transactionActive: false erneut // Standardweg, um eine Session zu beenden. console.log("[EV3 Master] Force stop attempt 1: Setting wbTrans to false."); setState(IDS.wbTrans, false); // Warten, damit die Wallbox Zeit zur Verarbeitung hat. await wait(FORCE_STOP_RETRY_DELAY_MS); if (getState(IDS.wbStat)?.val === "Charging") { console.warn( "[EV3 Master] Force stop attempt 1 failed. Proceeding with Availability Toggle.", ); // Versuch 2: Verfügbarkeitswechsel (Availability-Toggle) console.log("[EV3 Master] Force stop attempt 2: Toggling wbAvail (false -> true)."); setState(IDS.wbAvail, false); // Nach dem Deaktivieren warten await wait(FORCE_STOP_AVAILABILITY_TOGGLE_DELAY_MS); setState(IDS.wbAvail, true); // Vor dem erneuten Stopp-Versuch warten await wait(FORCE_STOP_AVAILABILITY_TOGGLE_DELAY_MS); setState(IDS.wbTrans, false); ev3Notify("⚠️ Wallbox Ladestop erzwungen (Availability Reset)", 3); console.log("[EV3 Master] Force stop attempt 2 completed."); } else { console.log("[EV3 Master] Forced charging stop successful on first attempt."); } } catch (e) { console.error(`[EV3 Master] Error during forced charging stop: ${e.message}`); ev3Notify(`❌ Fehler beim erzwungenen Ladestopp: ${e.message}`, 5); } finally { isForceStopping = false; if (stopTimer) { clearTimeout(stopTimer); stopTimer = null; console.log("[EV3 Master] Cleared stopTimer after forced stop."); } if (startRefreshTimer) { clearTimeout(startRefreshTimer); startRefreshTimer = null; } setState(IDS.u_power, 0, true); if (startZeitLaden) { const stats = updateChargeStatistics(Date.now() - startZeitLaden); setState(IDS.u_timeDay, stats.totalMinToday, true); ev3Notify( `❌ Ladung beendet (forced). Heute geladen: ${stats.formattedTime} (+approx. ${stats.kmToday} km)`, 1, ); startZeitLaden = null; setState(IDS.u_startTs, 0, true); } if (originalMinSoc !== null) { setState(IDS.minSocSet, Math.max(0, originalMinSoc)); ev3Notify(`🔌 Hausbatterie MinSoc auf ${originalMinSoc}% nach erzwungenem Stop eingestellt`); originalMinSoc = null; setState(IDS.u_origSoc, 0, true); } if (stopRefreshTimer) clearTimeout(stopRefreshTimer); stopRefreshTimer = setTimeout(() => { console.log( "[EV3 Master] Triggering post-stop vehicle refresh for final SoC and statistics.", ); if (existsState(IDS.refreshCar)) { setState(IDS.refreshCar, true); } stopRefreshTimer = null; }, STOP_REFRESH_DELAY_MS); } } function ev3Notify(text, prio = 1, spoken = null) { sendTo("telegram", "send", { text: text }); // An Telegram senden // Token dynamisch auflösen für erhöhte Robustheit const gotifyToken = getState("0_userdata.0.gotifytoken.iobroker")?.val; if (gotifyToken) { const url = `https://mygotify.meistermopper.de/message?token=${gotifyToken}`; const payload = { title: "EV3 Master", message: text, priority: prio }; const options = { headers: { "Content-Type": "application/json" }, timeout: 10000, }; httpPost(url, payload, options, (err) => { if (err) console.error(`[EV3 Master] Gotify Error: ${err}`); }); } else { console.warn("[EV3 Master] Gotify Token ist nicht definiert oder leer."); } // Sprachausgabe tagsüber if (compareTime("08:00", "20:00", "between")) { // Wenn ein spezieller Sprechtext übergeben wurde (spoken), nutzen wir diesen. // Andernfalls nehmen wir den Standardtext. let voice = spoken || text; voice = voice .replace(/%/g, " Prozent") .replace(/SOC/gi, "Ladestand") .replace(/🔋|🔌|⚠️|🚗|❌/g, ""); // sendTo("sayit", "say", { text: voice }); } } // --- 4. INTELLIGENTE PV-GLÄTTUNG (EMA) --- /** * Berechnet die durchschnittliche PV-Leistung zur Stabilisierung der Regelung. * Reagiert schnell auf Einbrüche, langsam auf Erhöhungen. */ schedule("* * * * *", async () => { const { pvPower: current, pvAverage: oldAvg, batSoc } = getPowerMetrics(); const inertia = Number(getState(IDS.u_smooth)?.val) || 10; let alpha; if (current < oldAvg) { // ABFALL: Wenn die Hausbatterie voll ist (>85%), langsamer reagieren (0,2 statt 0,5). // Dies verhindert unnötige Ladestopps bei kurzen Wolken. alpha = batSoc > 85 ? 0.2 : 0.5; } else { // ANSTIEG: Wenn die Hausbatterie noch leer ist (<50%), länger auf stabile Sonne warten. // Wenn die Batterie voll ist, die Sonne schneller nutzen. const dynamicInertia = batSoc > 75 ? Math.max(2, inertia / 2) : inertia; alpha = 1 / dynamicInertia; } const newAvg = alpha * current + (1 - alpha) * oldAvg; setState(IDS.pvAverage, Math.round(newAvg), true); }); // --- 5. AUTOMATISIERUNGS-LOGIK (PV-ÜBERSCHUSS) --- /** * Überwacht den PV-Durchschnitt und schaltet das Laden automatisch, * vorausgesetzt der Automatik-Schalter in VIS ist aktiv. */ function checkPvAutomation() { const isAuto = !!getState(IDS.u_auto)?.val; // Automatik-Schalter const { pvAverage: mittel, batSoc, evSoc } = getPowerMetrics(); // Aktuelle Leistungswerte // Abbrechen, wenn Wallbox offline ist const isConnected = !!getState(IDS.wbConn)?.val; if (!isConnected && mittel > PV_START_LIMIT) { if (!hasWarnedOcppOffline) { console.warn("[EV3 Master] Start not possible: Wallbox connection missing (OCPP Offline)"); hasWarnedOcppOffline = true; } } if (!isAuto || !isConnected) return; const isTransActive = !!getState(IDS.wbTrans)?.val; const _wbStatus = getState(IDS.wbStat)?.val; const limitCar = getState(IDS.targetSocSrv)?.val || 100; // Diagnose-Log für ausreichenden Überschuss, falls nicht geladen wird //if (!isTransActive && (mittel > (PV_START_LIMIT - 500))) { // console.log(`[EV3 Master] Status: ${wbStatus} | PV-Avg: ${mittel}W | Bat-SoC: ${batSoc}% | EV-SoC: ${evSoc}% / Ziel: ${limitCar}%`); //} // START: Genug Sonne (>4.6kW) und Hausspeicher gut gefüllt (>75%) if ( !isTransActive && !isStartingSequenceActive && mittel > PV_START_LIMIT && batSoc > 75 && evSoc < limitCar ) { triggerStartSequence("PV-Automatik"); } // STOP: Überschuss sinkt unter Ladeleistung (Pause) // oder Ladeziel erreicht // oder Wallbox-Verbindung verloren (oben durch !isConnected abgefangen, hier als Redundanz) else if (isTransActive && (mittel < PV_STOP_LIMIT || evSoc >= limitCar)) { // Detailliertes Logging des Stopp-Grunds let reason = ""; if (mittel < PV_STOP_LIMIT) reason = `Insufficient PV power (${mittel}W < ${PV_STOP_LIMIT}W)`; else if (evSoc >= limitCar) reason = `Vehicle charging target reached (${evSoc}% >= ${limitCar}%)`; if (reason) { console.log(`[EV3 Master] Automatically stopped: ${reason}`); } setState(IDS.wbTrans, false); //ev3Notify("EV3 charging ended"); } } // Trigger bei neuen PV-Werten sowie bei Wiederherstellung der Verbindung on({ id: IDS.pvAverage, change: "ne" }, checkPvAutomation); on({ id: IDS.soc, change: "ne" }, checkPvAutomation); on({ id: IDS.wbConn, val: true, change: "ne" }, checkPvAutomation); // [NEU] Listener für den Datenpunkt wbTrans. // Dieser Listener ist entscheidend für das Erkennen und Beheben von "hängenden" Ladestatuse. // Wenn `wbTrans` auf `false` wechselt (Stopp-Befehl gesendet), prüfen wir nach einer Verzögerung // von 10 Sekunden, ob `wbStat` immer noch `Charging` ist, und rufen in diesem Fall `forceStopCharging()` auf. on({ id: IDS.wbTrans, change: "ne" }, (obj) => { if (obj.state.val === false) { // 10 Sekunden Verzögerung, um der Wallbox Zeit zur Verarbeitung zu geben setTimeout(async () => { if (getState(IDS.wbStat)?.val === "Charging") { console.warn( "[EV3 Master] Wallbox hängt im Status 'Charging' trotz beendeter Transaktion. Erzwinge Stopp.", ); await forceStopCharging(); } }, 10000); } }); // --- 6. ÜBERWACHUNG & STATISTIKEN --- /** * Erfasst die Ladedauer und stellt die Leistungsanzeige ein. * Erfasst die Ladedauer, stellt die Leistungsanzeige ein und schützt die Hausbatterie * vor Entladung beim manuellen Laden. * Berechnet die Statistiken für den aktuellen oder abgeschlossenen Ladevorgang. */ function updateChargeStatistics(sessionDurationMs) { const dauerMin = Math.max(1, Math.round(sessionDurationMs / 60000)); // Mindestens 1 Minute zählen const currentTotalMin = getState(IDS.u_timeDay)?.val || 0; const totalMinToday = currentTotalMin + dauerMin; // Energie und Reichweite const energyKWh = (totalMinToday / 60) * (FIXED_CHARGE_W / 1000); const month = new Date().getMonth(); const rangeMax = month >= 3 && month <= 10 ? RANGE_SUMMER : RANGE_WINTER; const kmToday = Math.round((energyKWh / CAR_CAPACITY_KWH) * rangeMax); const h = Math.floor(totalMinToday / 60); const m = totalMinToday % 60; const formattedTime = h > 0 ? `${h}:${m < 10 ? `0${m}` : m} Std` : `${m} Min`; return { totalMinToday, formattedTime, kmToday, spokenTime: h > 0 ? `${h} Stunden, ${m} Minuten` : `${m} Minuten`, }; } // Haupt-Überwacher für den Wallbox-Status (z. B. Charging, Preparing, SuspendedEV...) on({ id: IDS.wbStat, change: "ne" }, (obj) => { const status = String(obj.state.val); const isAuto = !!getState(IDS.u_auto)?.val; // FALL 1: Das Fahrzeug lädt aktiv if (status === "Charging") { // Falls ein Stop-Timer läuft (kurze Ladeunterbrechung durch z.B. Wolken): // Timer abbrechen, da die Ladung direkt fortgesetzt wurde (Hiccup-Schutz). if (stopTimer) { clearTimeout(stopTimer); stopTimer = null; console.log("[EV3 Master] Short interruption ended, resuming charge"); return; } // Startzeitpunkt merken, sofern noch nicht gesetzt (für die tägliche Statistik) if (!startZeitLaden) { startZeitLaden = Date.now(); setState(IDS.u_startTs, startZeitLaden, true); // 75 Sek. nach Ladestart Fahrzeug aufwecken, um verbleibende Ladedauer & SoC präzise abzurufen if (startRefreshTimer) clearTimeout(startRefreshTimer); startRefreshTimer = setTimeout(() => { if (getState(IDS.wbStat)?.val === "Charging") { console.log( "[EV3 Master] Triggering post-start vehicle refresh to update SoC and remaining time.", ); if (existsState(IDS.refreshCar)) { setState(IDS.refreshCar, true); } } startRefreshTimer = null; }, START_REFRESH_DELAY_MS); } // Da die Box starr mit 6A lädt (3 Phasen * 230V * 6A = ~3,96 kW), setzen wir den festen Watt-Wert zur Anzeige setState(IDS.u_power, FIXED_CHARGE_W, true); // BATTERIESCHUTZ bei manuellem Laden (Automatik AUS): // Verhindert, dass der Hausspeicher entleert wird, indem der Min-SoC der Hausbatterie temporär // auf den aktuellen SoC-Wert gesetzt wird. if (!isAuto && originalMinSoc === null) { originalMinSoc = getState(IDS.minSocRead)?.val; setState(IDS.u_origSoc, originalMinSoc, true); const currentBatSoc = getState(IDS.batSocPV)?.val; // MinSoc sicherheitshalber nicht unter 0 setzen setState(IDS.minSocSet, Math.max(0, currentBatSoc)); const msg = `Manuelles Laden gestartet. MinSoc auf ${currentBatSoc}% (vorher: ${originalMinSoc}%)`; console.log(`[EV3 Master] ${msg}`); ev3Notify(`🔋 ${msg}`); } } // FALL 2: Das Fahrzeug stoppt (durch Kabel ziehen, Auto-Ladeziel erreicht, Pause durch PV-Regelung etc.) else if ( startZeitLaden && (status === "Finishing" || status === "Available" || status === "SuspendedEV" || status === "SuspendedEVSE") ) { if (startRefreshTimer) { clearTimeout(startRefreshTimer); startRefreshTimer = null; } // Um bei kurzen Unterbrechungen nicht sofort abzubrechen, warten wir 45 Sekunden (Entprellzeit). // Wechselt der Status in dieser Zeit zurück auf "Charging", läuft die Ladung oben nahtlos weiter. if (stopTimer) clearTimeout(stopTimer); stopTimer = setTimeout(() => { // 1. Batterieschutz aufheben: Min-SoC des Hausspeichers wieder auf den ursprünglichen Wert zurückstellen if (!isAuto && originalMinSoc !== null) { setState(IDS.minSocSet, Math.max(0, originalMinSoc)); const msg = `EV3 Manuelles Laden beendet. Hausbatterie MinSoc wieder auf ${originalMinSoc}% gestellt`; console.log(`[EV3 Master] ${msg}`); ev3Notify(`🔌 ${msg}`); originalMinSoc = null; setState(IDS.u_origSoc, 0, true); } // 2. Ladestatistik berechnen (Dauer in Min, geladene kWh und hinzugewonnene Kilometer je nach Jahreszeit) const stats = updateChargeStatistics(Date.now() - startZeitLaden); setState(IDS.u_timeDay, stats.totalMinToday, true); // 3. Prüfen, ob das Auto das Ladeziel erreicht hat: // Vergleicht den aktuellen SoC des Kia mit dem eingestellten Ladeziel der Wallbox/des Fahrzeugs const evSoc = Number(getState(IDS.soc)?.val) || 0; const targetSoc = Number(getState(IDS.targetSocSrv)?.val) || 100; let msgText = `❌ EV3 Ladung beendet. Heute geladen: ${stats.formattedTime} (+approx. ${stats.kmToday} km)`; let spokenText = `Ladung beendet. Heute geladen: ${stats.spokenTime}. Reichweite approx. ${stats.kmToday} Kilometer.`; // Wenn das Ladeziel erreicht wurde, ergänzen wir die Benachrichtigungen if (evSoc >= targetSoc) { msgText += ` - Das Ladeziel von ${targetSoc}% wurde erreicht`; spokenText += ` Das Ladeziel von ${targetSoc} Prozent wurde erreicht`; } // Benachrichtigung senden (Telegram, Gotify, Alexa/SayIt) ev3Notify(msgText, 1, spokenText); // 4. Ladevariablen zurücksetzen & Ladevorgang sauber abschließen startZeitLaden = null; setState(IDS.u_startTs, 0, true); setState(IDS.u_power, 0, true); // WICHTIG: Setzt transactionActive auf false, damit die Wallbox die Transaktion beendet // und die VIS-Visualisierung nicht mehr "lädt..." anzeigt (Logikfehler-Behebung). setState(IDS.wbTrans, false); stopTimer = null; // Finalen Status nach Ladeende vom Fahrzeug abrufen if (stopRefreshTimer) clearTimeout(stopRefreshTimer); stopRefreshTimer = setTimeout(() => { console.log( "[EV3 Master] Triggering post-stop vehicle refresh for final SoC and statistics.", ); if (existsState(IDS.refreshCar)) { setState(IDS.refreshCar, true); } stopRefreshTimer = null; }, STOP_REFRESH_DELAY_MS); }, DEBOUNCE_STOP_MS); } }); // Dynamischer Batterieschutz bei manuellem Laden: // Erhöht sich der SoC des Heimspeichers während des manuellen Ladens (z. B. durch PV-Ertrag), // wird der Min-SoC des Wechselrichters entsprechend nach oben angepasst. on({ id: IDS.batSocPV, change: "ne" }, (obj) => { const isAuto = !!getState(IDS.u_auto)?.val; const isCharging = getState(IDS.wbStat)?.val === "Charging"; if (!isAuto && isCharging) { const newSoc = Number(obj.state.val) || 0; const oldSoc = Number(obj.oldState?.val) || 0; if (newSoc > oldSoc) { if (originalMinSoc === null) { originalMinSoc = getState(IDS.minSocRead)?.val; setState(IDS.u_origSoc, originalMinSoc, true); } setState(IDS.minSocSet, Math.max(0, newSoc)); console.log( `[EV3 Master] Manual charging: Home battery SoC increased from ${oldSoc}% to ${newSoc}%. Updated MinSoC to ${newSoc}%.`, ); } } }); // --- 7. ZUSÄTZLICHE FUNKTIONEN --- /** * Verbindungs-Watchdog: Überwacht die Erreichbarkeit der Wallbox. * Meldet Statusänderungen (Anti-Spam) und löst nach 3 Min. Offline-Zeit einen Reconnect über den UniFi-AP aus. */ on({ id: IDS.wbConn, change: "ne" }, (obj) => { const isConnected = !!obj.state.val; if (!isConnected) { // Nur beim ersten Mal warnen if (!wasOfflineReported) { //console.warn("[EV3 Master] Wallbox-Verbindung verloren. Reconnect-Timer (3 Min) gestartet."); wasOfflineReported = true; } // Reconnect-Timer starten (falls nicht schon einer läuft) if (!reconnectTimer) { reconnectTimer = setTimeout(() => { //console.log("[EV3 Master] Führe WLAN-Neuverbindung über UniFi AP aus..."); setState(IDS.unifiReconnect, true); reconnectTimer = null; }, RECONNECT_WB_MS); } } else { // Wieder online: Status zurücksetzen und Timer stoppen if (wasOfflineReported) console.log("[EV3 Master] Wallbox connection restored."); wasOfflineReported = false; if (reconnectTimer) { clearTimeout(reconnectTimer); reconnectTimer = null; } } }); /** * Formatiert die verbleibende Ladezeit für die Anzeige in VIS. */ on({ id: IDS.remTime, change: "any" }, (obj) => { const m = obj.state.val; let t = "0:00"; if (m > 0) { const hh = Math.floor(m / 60); const mm = m % 60; t = `${hh}:${mm < 10 ? `0${mm}` : mm}`; } setState(IDS.u_rest, t, true); }); // Synchronisiert den VIS-Anzeige-Datenpunkt mit dem echten Ladeziel des Autos on({ id: IDS.targetSocSrv, change: "ne" }, (obj) => { console.log(`[EV3 Master] Charge target sync: Setting VIS slider to ${obj.state.val}%.`); setState(IDS.u_limit, obj.state.val, true); }); // Handler für manuelle Start-Anforderungen on({ id: IDS.u_startChargeRequest, val: true, change: "any" }, () => { console.log("[EV3 Master] Manual start request received via VIS."); triggerStartSequence("VIS-Manual"); setTimeout(() => { setState(IDS.u_startChargeRequest, false, true); }, 1000); }); // Täglicher Reset der Ladestatistik um 02:05 Uhr schedule("5 2 * * *", () => { setState(IDS.u_timeDay, 0, true); }); // Schutz der Kia 12V-Starterbatterie on({ id: IDS.bat12v, change: "ne" }, (obj) => { if (obj.state.val <= 50) ev3Notify(`⚠️ Kia 12V battery critical!`, 5); });Proxmox und HA ...
Ich schreibe den Code nicht mehr selbst – ich schimpfe mit der KI, bis es funktioniert. -
Ich habe eine EVBox.
Die verbindet sich mit ws://IPdesioBroker:Port ohne Benutzername und Passwort mit dem ioBroker. Der Port wird im Adapter festgelegt.

Mein Ladeskript hat mehr als 700 Zeilen, aber bitte:
/* eslint-env es2022 */ /** * Name: EV3 Charge-Master * Purpose: Start/stop management for Kia EV3 wallbox charging (fixed 6A / 3.96 kW). * Modes: 1. Manual: User triggered via VIS * 2. PV Auto: Automated based on PV surplus and home battery SoC * Features: Home battery protection, debounced status handling, forced transaction stops. */ // --- 1. SETUP: DIGITALE ZENTRALE (21 DATENPUNKTE) --- const VIN = `bluelink.0.${getState("0_userdata.0.Energie.Kia_e_niro.vin")?.val}`; const PATH_USER = "0_userdata.0.Energie.Kia_e_niro"; const IDS = { // Wallbox (Hardware via OCPP) wbStat: "ocpp.0.http://192_168_178_80:9220/EVB-P21312507.1.status", // [1] Status (Charging, Preparing...) wbTrans: "ocpp.0.http://192_168_178_80:9220/EVB-P21312507.1.transactionActive", // [2] Steuert den Stromfluss (Transaktion aktiv) wbAvail: "ocpp.0.http://192_168_178_80:9220/EVB-P21312507.1.availability", // [3] Reset / Verfügbarkeit wbConn: "ocpp.0.http://192_168_178_80:9220/EVB-P21312507.connected", // Verbindung zum ioBroker unifiReconnect: "unifi-network.0.clients.users.60:09:c3:2f:46:49.reconnect", // [22] Neu verbinden über UniFi // Fahrzeugdaten (Cloud) soc: `${VIN}.vehicleStatusRaw.Green.BatteryManagement.BatteryRemain.Ratio`, // [4] Ladestand % bat12v: `${VIN}.vehicleStatusRaw.Electronics.Battery.Level`, // [5] 12V-Batterieschutz conn: `${VIN}.vehicleStatusRaw.Green.ChargingInformation.ConnectorFastening.State`, // [6] Steckerstatus remTime: `${VIN}.vehicleStatusRaw.Green.ChargingInformation.Charging.RemainTime`, // [7] Restzeit in Min (vom Fahrzeug gemeldet) targetSocSrv: `${VIN}.control.charge_limit_slow`, // [23] Ladeziel (AC) vom Fahrzeug (Steuerungspunkt) refreshCar: `${VIN}.control.force_refresh_from_car`, // [8] Fahrzeug aufwecken (Live-Daten) refreshSrv: `${VIN}.control.force_refresh_from_server`, // [8b] Server-Status abrufen // Energie-Zentrale (Hardware-Werte) pvPower: "solax.0.data.acpower", // [9] Aktuelle PV-Leistung in Watt pvAverage: "0_userdata.0.Energie.PV.Durchschnitt", // [10] Geglätteter Wert (EMA) netPower: "0_userdata.0.Energie.PV.Netzbezug", // [11] Hauszähler (+Bezug/-Einspeisung) hausCons: "0_userdata.0.Energie.PV.Hausverbrauch", // [12] Hausverbrauch batSocPV: "modbus.0.inputRegisters.100.843_Battery_State_of_Charge_(System)", // [13] Heimspeicher % minSocSet: "modbus.0.holdingRegisters.100.2901_ESS_Minimum_SoC_(unless_grid_fails)", minSocRead: "modbus.0.inputRegisters.100.2901_ESS_Minimum_SoC_(unless_grid_fails)", // Steuerung & Statistiken (VIS) u_auto: `${PATH_USER}.autoladen`, // [14] Schalter: PV-Automatik an/aus (Boolean) u_limit: `${PATH_USER}.Ladeprozent`, // [15] Ziel-SoC Schieberegler u_smooth: `${PATH_USER}.Glaettung_Zeit`, // [16] Trägheits-Schieberegler für EMA u_power: `${PATH_USER}.Ladeleistung`, // [17] Anzeige Ladeleistung (fix 3960W) u_timeDay: `${PATH_USER}.Ladezeit`, // [18] Lademinuten heute u_rest: `${PATH_USER}.Restladezeit`, // [19] HH:MM Anzeige aliasKm: "alias.0.umrechnen.kia_ladekm", // [20] Gewonnene Reichweite aliasDur: "alias.0.umrechnen.kia_ladezeit", // [21] Zeit-Objekt u_startChargeRequest: `${PATH_USER}.Start_Charge_Request`, // [NEU] Anforderung zum Starten des Ladevorgangs u_startTs: `${PATH_USER}.LastStartTimestamp`, // [PERSISTENCE] Merker für Startzeit u_origSoc: `${PATH_USER}.LastOriginalMinSoc`, // [PERSISTENCE] Merker für Batterie-Schutz }; // --- PARAMETER --- const PV_START_LIMIT = 4600; // Startgrenze (Sonne muss > 4.6kW + Puffer liefern) const PV_STOP_LIMIT = 4000; // Stoppgrenze (Ladevorgang pausieren, wenn Überschuss sinkt) const FIXED_CHARGE_W = 3960; // Fixe Leistung bei 6A (220V * 3 Phasen * 6A) const CAR_CAPACITY_KWH = 81.4; const RANGE_SUMMER = 550; const RANGE_WINTER = 450; // --- TIMING KONSTANTEN --- const DEBOUNCE_STOP_MS = 45000; // 45 Sek. Wartezeit vor endgültigem Stopp const RECONNECT_WB_MS = 180000; // 3 Min. Wartezeit vor WLAN-Neuverbindung const START_REFRESH_DELAY_MS = 75000; // 75 Sek. nach Ladestart: Fahrzeug aufwecken für Restzeit & SoC const STOP_REFRESH_DELAY_MS = 30000; // 30 Sek. nach bestätigtem Ladestopp: Finalen SoC & Status abrufen const START_VERIFY_TIMEOUT_MS = 35000; // 35 Sek. maximale Wartezeit auf "Charging"-Status nach Startbefehl const START_AVAILABILITY_TOGGLE_DELAY_MS = 2000; // 2 Sek. Pause bei Verfügbarkeitswechsel const START_HANDSHAKE_DELAY_MS = 4000; // 4 Sek. Puffer für OCPP-Handshake nach Re-Aktivierung // [NEU] Verzögerung vor dem erneuten Versuch des Stopp-Befehls nach dem ersten Versuch. const FORCE_STOP_RETRY_DELAY_MS = 5000; // [NEU] Verzögerung während des Verfügbarkeitswechsels, um der Wallbox Zeit zur Verarbeitung zu geben. const FORCE_STOP_AVAILABILITY_TOGGLE_DELAY_MS = 2000; let startZeitLaden = null; // Merker für Statistik let originalMinSoc = null; // Merker für Min-SoC bei manuellem Laden let stopTimer = null; // Timer zur Entprellung von kurzen Lade-Unterbrechungen let startRefreshTimer = null; // Timer für Statusabfrage nach Ladestart let stopRefreshTimer = null; // Timer für Statusabfrage nach Ladeende let reconnectTimer = null; // Timer für Wallbox-Recovery let wasOfflineReported = false; // Status für Anti-Spam Meldungen let hasWarnedOcppOffline = false; // Flag to prevent repeated warning logs per script run // [NEU] Sperrvariable zur Vermeidung von Race Conditions während der Startsequenz. let isStartingSequenceActive = false; // [NEU] Sperrvariable zur Vermeidung mehrerer gleichzeitiger Ausführungen von erzwungenen Stopps. let isForceStopping = false; // --- 2. INITIALISIERUNG --- async function initLadeSystem() { // Erstellt nur noch die für diese Version nötigen Punkte if (!existsState(IDS.u_auto)) await createStateAsync(IDS.u_auto, true, { type: "boolean", name: "PV Automatic", }); if (!existsState(IDS.u_smooth)) await createStateAsync(IDS.u_smooth, 10, { type: "number", name: "EMA Smoothing", }); if (!existsState(IDS.u_limit)) await createStateAsync(IDS.u_limit, 80, { type: "number", name: "Charge Target", }); // NEU: Datenpunkt für die Ladestart-Anforderung if (!existsState(IDS.u_startChargeRequest)) await createStateAsync(IDS.u_startChargeRequest, false, { type: "boolean", name: "Start Charging (Request)", role: "button", }); if (!existsState(IDS.u_startTs)) await createStateAsync(IDS.u_startTs, 0, { type: "number", name: "Start Timestamp" }); if (!existsState(IDS.u_origSoc)) await createStateAsync(IDS.u_origSoc, 0, { type: "number", name: "Original MinSoc Backup" }); // Laufende Prozesse nach Skript-Neustart wiederherstellen if (getState(IDS.wbTrans)?.val === true) { startZeitLaden = getState(IDS.u_startTs)?.val || Date.now(); const savedSoc = getState(IDS.u_origSoc)?.val; originalMinSoc = savedSoc !== null && savedSoc !== 0 ? savedSoc : null; if (getState(IDS.wbStat)?.val === "Charging") { setState(IDS.u_power, FIXED_CHARGE_W, true); } } } initLadeSystem(); // --- 2.1 HELPER: ASYNCHRONE PAUSE & MESSWERTE --- /** * Asynchroner Timer zur Ablaufverzögerung in async-Funktionen. * @param {number} ms Millisekunden */ const wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms)); /** * Wartet bis der Wallbox-Status auf "Charging" wechselt oder der Timeout abläuft. * @param {number} timeoutMs Maximale Wartezeit in Millisekunden * @returns {Promise<boolean>} True falls "Charging" erreicht wurde, sonst False */ async function waitForChargingState(timeoutMs) { const startTime = Date.now(); while (Date.now() - startTime < timeoutMs) { if (getState(IDS.wbStat)?.val === "Charging") { return true; } await wait(1000); } return getState(IDS.wbStat)?.val === "Charging"; } /** * Sammelt alle relevanten Leistungs- und SoC-Werte aus den Datenpunkten. * Stellt sicher, dass die Werte Zahlen sind. */ function getPowerMetrics() { return { pvPower: Math.max(0, Number(getState(IDS.pvPower)?.val) || 0), pvAverage: Number(getState(IDS.pvAverage)?.val) || 0, batSoc: Number(getState(IDS.batSocPV)?.val) || 0, evSoc: Number(getState(IDS.soc)?.val) || 0, }; } // --- 3. KOMMUNIKATION --- /** * Führt die intelligente Startsequenz der Wallbox inklusive Verifikations-Watchdog * und automatischem Retry-Mechanismus aus. * @param {string} reason Grund für den Start (z. B. "PV-Automatik", "VIS-Manual") */ async function triggerStartSequence(reason = "PV-Surplus") { if (isStartingSequenceActive) { console.log(`[EV3 Master] Start sequence already active, skipping (${reason}).`); return; } const wbStatus = getState(IDS.wbStat)?.val; if (wbStatus === "Charging") { console.log(`[EV3 Master] Wallbox is already charging. Skipping start sequence.`); return; } const readyToStart = ["Preparing", "Finishing", "SuspendedEVSE", "SuspendedEV"].includes( wbStatus, ); if (!readyToStart) { if (wbStatus === "Available") { // console.warn(`[EV3 Master] Start (${reason}) aborted: No vehicle detected.`); return; } } isStartingSequenceActive = true; console.log( `[EV3 Master] Starting wallbox sequence for: ${reason} (Current Status: ${wbStatus})`, ); try { // --- VERSUCH 1 --- console.log("[EV3 Master] Start attempt 1: Toggling availability and setting wbTrans = true."); setState(IDS.wbAvail, false); await wait(START_AVAILABILITY_TOGGLE_DELAY_MS); setState(IDS.wbAvail, true); await wait(START_HANDSHAKE_DELAY_MS); // Erhöhter Puffer für den OCPP-Handshake setState(IDS.wbTrans, true); let started = await waitForChargingState(START_VERIFY_TIMEOUT_MS); // --- VERSUCH 2 (AUTOMATISCHER RETRY BEI TIMEOUT / FEHLSCHLAG) --- if (!started) { const currentStatus = getState(IDS.wbStat)?.val; console.warn( `[EV3 Master] Start attempt 1 timed out. Wallbox status is '${currentStatus}'. Retrying reset sequence...`, ); setState(IDS.wbTrans, false); await wait(FORCE_STOP_RETRY_DELAY_MS); setState(IDS.wbAvail, false); await wait(START_AVAILABILITY_TOGGLE_DELAY_MS); setState(IDS.wbAvail, true); await wait(START_HANDSHAKE_DELAY_MS); setState(IDS.wbTrans, true); started = await waitForChargingState(START_VERIFY_TIMEOUT_MS); } // --- ERGEBNIS-AUSWERTUNG --- if (started) { console.log(`[EV3 Master] Wallbox start verified successfully (${reason}).`); ev3Notify(`🔋 EV3-Ladung gestartet via ${reason} mit 6A`); } else { const finalStatus = getState(IDS.wbStat)?.val; console.error( `[EV3 Master] Start failed after retry. Wallbox did not reach 'Charging' state (Status: ${finalStatus}). Reverting wbTrans.`, ); setState(IDS.wbTrans, false); ev3Notify( `⚠️ EV3 Ladestart fehlgeschlagen: Wallbox reagiert nicht (Status: ${finalStatus})`, 4, ); } } catch (e) { console.error(`[EV3 Master] Error during start sequence: ${e.message}`); setState(IDS.wbTrans, false); ev3Notify(`❌ Fehler bei Ladestart-Sequenz: ${e.message}`, 5); } finally { isStartingSequenceActive = false; } } /** * [NEU] forceStopCharging() * Versucht, den Ladevorgang zu beenden, selbst wenn der Status "hängt". * Nutzt den Verfügbarkeitswechsel (Availability-Toggle) als letzten Ausweg. * Diese Funktion wird aufgerufen, wenn `transactionActive` auf `false` gesetzt wurde, * die Wallbox aber immer noch den Status `Charging` meldet. */ async function forceStopCharging() { // Gleichzeitige Ausführung verhindern if (isForceStopping) { console.log("[EV3 Master] Force stop already active, skipping."); return; } isForceStopping = true; console.warn("[EV3 Master] Initiating forced charging stop sequence."); try { // Versuch 1: Sende transactionActive: false erneut // Standardweg, um eine Session zu beenden. console.log("[EV3 Master] Force stop attempt 1: Setting wbTrans to false."); setState(IDS.wbTrans, false); // Warten, damit die Wallbox Zeit zur Verarbeitung hat. await wait(FORCE_STOP_RETRY_DELAY_MS); if (getState(IDS.wbStat)?.val === "Charging") { console.warn( "[EV3 Master] Force stop attempt 1 failed. Proceeding with Availability Toggle.", ); // Versuch 2: Verfügbarkeitswechsel (Availability-Toggle) console.log("[EV3 Master] Force stop attempt 2: Toggling wbAvail (false -> true)."); setState(IDS.wbAvail, false); // Nach dem Deaktivieren warten await wait(FORCE_STOP_AVAILABILITY_TOGGLE_DELAY_MS); setState(IDS.wbAvail, true); // Vor dem erneuten Stopp-Versuch warten await wait(FORCE_STOP_AVAILABILITY_TOGGLE_DELAY_MS); setState(IDS.wbTrans, false); ev3Notify("⚠️ Wallbox Ladestop erzwungen (Availability Reset)", 3); console.log("[EV3 Master] Force stop attempt 2 completed."); } else { console.log("[EV3 Master] Forced charging stop successful on first attempt."); } } catch (e) { console.error(`[EV3 Master] Error during forced charging stop: ${e.message}`); ev3Notify(`❌ Fehler beim erzwungenen Ladestopp: ${e.message}`, 5); } finally { isForceStopping = false; if (stopTimer) { clearTimeout(stopTimer); stopTimer = null; console.log("[EV3 Master] Cleared stopTimer after forced stop."); } if (startRefreshTimer) { clearTimeout(startRefreshTimer); startRefreshTimer = null; } setState(IDS.u_power, 0, true); if (startZeitLaden) { const stats = updateChargeStatistics(Date.now() - startZeitLaden); setState(IDS.u_timeDay, stats.totalMinToday, true); ev3Notify( `❌ Ladung beendet (forced). Heute geladen: ${stats.formattedTime} (+approx. ${stats.kmToday} km)`, 1, ); startZeitLaden = null; setState(IDS.u_startTs, 0, true); } if (originalMinSoc !== null) { setState(IDS.minSocSet, Math.max(0, originalMinSoc)); ev3Notify(`🔌 Hausbatterie MinSoc auf ${originalMinSoc}% nach erzwungenem Stop eingestellt`); originalMinSoc = null; setState(IDS.u_origSoc, 0, true); } if (stopRefreshTimer) clearTimeout(stopRefreshTimer); stopRefreshTimer = setTimeout(() => { console.log( "[EV3 Master] Triggering post-stop vehicle refresh for final SoC and statistics.", ); if (existsState(IDS.refreshCar)) { setState(IDS.refreshCar, true); } stopRefreshTimer = null; }, STOP_REFRESH_DELAY_MS); } } function ev3Notify(text, prio = 1, spoken = null) { sendTo("telegram", "send", { text: text }); // An Telegram senden // Token dynamisch auflösen für erhöhte Robustheit const gotifyToken = getState("0_userdata.0.gotifytoken.iobroker")?.val; if (gotifyToken) { const url = `https://mygotify.meistermopper.de/message?token=${gotifyToken}`; const payload = { title: "EV3 Master", message: text, priority: prio }; const options = { headers: { "Content-Type": "application/json" }, timeout: 10000, }; httpPost(url, payload, options, (err) => { if (err) console.error(`[EV3 Master] Gotify Error: ${err}`); }); } else { console.warn("[EV3 Master] Gotify Token ist nicht definiert oder leer."); } // Sprachausgabe tagsüber if (compareTime("08:00", "20:00", "between")) { // Wenn ein spezieller Sprechtext übergeben wurde (spoken), nutzen wir diesen. // Andernfalls nehmen wir den Standardtext. let voice = spoken || text; voice = voice .replace(/%/g, " Prozent") .replace(/SOC/gi, "Ladestand") .replace(/🔋|🔌|⚠️|🚗|❌/g, ""); // sendTo("sayit", "say", { text: voice }); } } // --- 4. INTELLIGENTE PV-GLÄTTUNG (EMA) --- /** * Berechnet die durchschnittliche PV-Leistung zur Stabilisierung der Regelung. * Reagiert schnell auf Einbrüche, langsam auf Erhöhungen. */ schedule("* * * * *", async () => { const { pvPower: current, pvAverage: oldAvg, batSoc } = getPowerMetrics(); const inertia = Number(getState(IDS.u_smooth)?.val) || 10; let alpha; if (current < oldAvg) { // ABFALL: Wenn die Hausbatterie voll ist (>85%), langsamer reagieren (0,2 statt 0,5). // Dies verhindert unnötige Ladestopps bei kurzen Wolken. alpha = batSoc > 85 ? 0.2 : 0.5; } else { // ANSTIEG: Wenn die Hausbatterie noch leer ist (<50%), länger auf stabile Sonne warten. // Wenn die Batterie voll ist, die Sonne schneller nutzen. const dynamicInertia = batSoc > 75 ? Math.max(2, inertia / 2) : inertia; alpha = 1 / dynamicInertia; } const newAvg = alpha * current + (1 - alpha) * oldAvg; setState(IDS.pvAverage, Math.round(newAvg), true); }); // --- 5. AUTOMATISIERUNGS-LOGIK (PV-ÜBERSCHUSS) --- /** * Überwacht den PV-Durchschnitt und schaltet das Laden automatisch, * vorausgesetzt der Automatik-Schalter in VIS ist aktiv. */ function checkPvAutomation() { const isAuto = !!getState(IDS.u_auto)?.val; // Automatik-Schalter const { pvAverage: mittel, batSoc, evSoc } = getPowerMetrics(); // Aktuelle Leistungswerte // Abbrechen, wenn Wallbox offline ist const isConnected = !!getState(IDS.wbConn)?.val; if (!isConnected && mittel > PV_START_LIMIT) { if (!hasWarnedOcppOffline) { console.warn("[EV3 Master] Start not possible: Wallbox connection missing (OCPP Offline)"); hasWarnedOcppOffline = true; } } if (!isAuto || !isConnected) return; const isTransActive = !!getState(IDS.wbTrans)?.val; const _wbStatus = getState(IDS.wbStat)?.val; const limitCar = getState(IDS.targetSocSrv)?.val || 100; // Diagnose-Log für ausreichenden Überschuss, falls nicht geladen wird //if (!isTransActive && (mittel > (PV_START_LIMIT - 500))) { // console.log(`[EV3 Master] Status: ${wbStatus} | PV-Avg: ${mittel}W | Bat-SoC: ${batSoc}% | EV-SoC: ${evSoc}% / Ziel: ${limitCar}%`); //} // START: Genug Sonne (>4.6kW) und Hausspeicher gut gefüllt (>75%) if ( !isTransActive && !isStartingSequenceActive && mittel > PV_START_LIMIT && batSoc > 75 && evSoc < limitCar ) { triggerStartSequence("PV-Automatik"); } // STOP: Überschuss sinkt unter Ladeleistung (Pause) // oder Ladeziel erreicht // oder Wallbox-Verbindung verloren (oben durch !isConnected abgefangen, hier als Redundanz) else if (isTransActive && (mittel < PV_STOP_LIMIT || evSoc >= limitCar)) { // Detailliertes Logging des Stopp-Grunds let reason = ""; if (mittel < PV_STOP_LIMIT) reason = `Insufficient PV power (${mittel}W < ${PV_STOP_LIMIT}W)`; else if (evSoc >= limitCar) reason = `Vehicle charging target reached (${evSoc}% >= ${limitCar}%)`; if (reason) { console.log(`[EV3 Master] Automatically stopped: ${reason}`); } setState(IDS.wbTrans, false); //ev3Notify("EV3 charging ended"); } } // Trigger bei neuen PV-Werten sowie bei Wiederherstellung der Verbindung on({ id: IDS.pvAverage, change: "ne" }, checkPvAutomation); on({ id: IDS.soc, change: "ne" }, checkPvAutomation); on({ id: IDS.wbConn, val: true, change: "ne" }, checkPvAutomation); // [NEU] Listener für den Datenpunkt wbTrans. // Dieser Listener ist entscheidend für das Erkennen und Beheben von "hängenden" Ladestatuse. // Wenn `wbTrans` auf `false` wechselt (Stopp-Befehl gesendet), prüfen wir nach einer Verzögerung // von 10 Sekunden, ob `wbStat` immer noch `Charging` ist, und rufen in diesem Fall `forceStopCharging()` auf. on({ id: IDS.wbTrans, change: "ne" }, (obj) => { if (obj.state.val === false) { // 10 Sekunden Verzögerung, um der Wallbox Zeit zur Verarbeitung zu geben setTimeout(async () => { if (getState(IDS.wbStat)?.val === "Charging") { console.warn( "[EV3 Master] Wallbox hängt im Status 'Charging' trotz beendeter Transaktion. Erzwinge Stopp.", ); await forceStopCharging(); } }, 10000); } }); // --- 6. ÜBERWACHUNG & STATISTIKEN --- /** * Erfasst die Ladedauer und stellt die Leistungsanzeige ein. * Erfasst die Ladedauer, stellt die Leistungsanzeige ein und schützt die Hausbatterie * vor Entladung beim manuellen Laden. * Berechnet die Statistiken für den aktuellen oder abgeschlossenen Ladevorgang. */ function updateChargeStatistics(sessionDurationMs) { const dauerMin = Math.max(1, Math.round(sessionDurationMs / 60000)); // Mindestens 1 Minute zählen const currentTotalMin = getState(IDS.u_timeDay)?.val || 0; const totalMinToday = currentTotalMin + dauerMin; // Energie und Reichweite const energyKWh = (totalMinToday / 60) * (FIXED_CHARGE_W / 1000); const month = new Date().getMonth(); const rangeMax = month >= 3 && month <= 10 ? RANGE_SUMMER : RANGE_WINTER; const kmToday = Math.round((energyKWh / CAR_CAPACITY_KWH) * rangeMax); const h = Math.floor(totalMinToday / 60); const m = totalMinToday % 60; const formattedTime = h > 0 ? `${h}:${m < 10 ? `0${m}` : m} Std` : `${m} Min`; return { totalMinToday, formattedTime, kmToday, spokenTime: h > 0 ? `${h} Stunden, ${m} Minuten` : `${m} Minuten`, }; } // Haupt-Überwacher für den Wallbox-Status (z. B. Charging, Preparing, SuspendedEV...) on({ id: IDS.wbStat, change: "ne" }, (obj) => { const status = String(obj.state.val); const isAuto = !!getState(IDS.u_auto)?.val; // FALL 1: Das Fahrzeug lädt aktiv if (status === "Charging") { // Falls ein Stop-Timer läuft (kurze Ladeunterbrechung durch z.B. Wolken): // Timer abbrechen, da die Ladung direkt fortgesetzt wurde (Hiccup-Schutz). if (stopTimer) { clearTimeout(stopTimer); stopTimer = null; console.log("[EV3 Master] Short interruption ended, resuming charge"); return; } // Startzeitpunkt merken, sofern noch nicht gesetzt (für die tägliche Statistik) if (!startZeitLaden) { startZeitLaden = Date.now(); setState(IDS.u_startTs, startZeitLaden, true); // 75 Sek. nach Ladestart Fahrzeug aufwecken, um verbleibende Ladedauer & SoC präzise abzurufen if (startRefreshTimer) clearTimeout(startRefreshTimer); startRefreshTimer = setTimeout(() => { if (getState(IDS.wbStat)?.val === "Charging") { console.log( "[EV3 Master] Triggering post-start vehicle refresh to update SoC and remaining time.", ); if (existsState(IDS.refreshCar)) { setState(IDS.refreshCar, true); } } startRefreshTimer = null; }, START_REFRESH_DELAY_MS); } // Da die Box starr mit 6A lädt (3 Phasen * 230V * 6A = ~3,96 kW), setzen wir den festen Watt-Wert zur Anzeige setState(IDS.u_power, FIXED_CHARGE_W, true); // BATTERIESCHUTZ bei manuellem Laden (Automatik AUS): // Verhindert, dass der Hausspeicher entleert wird, indem der Min-SoC der Hausbatterie temporär // auf den aktuellen SoC-Wert gesetzt wird. if (!isAuto && originalMinSoc === null) { originalMinSoc = getState(IDS.minSocRead)?.val; setState(IDS.u_origSoc, originalMinSoc, true); const currentBatSoc = getState(IDS.batSocPV)?.val; // MinSoc sicherheitshalber nicht unter 0 setzen setState(IDS.minSocSet, Math.max(0, currentBatSoc)); const msg = `Manuelles Laden gestartet. MinSoc auf ${currentBatSoc}% (vorher: ${originalMinSoc}%)`; console.log(`[EV3 Master] ${msg}`); ev3Notify(`🔋 ${msg}`); } } // FALL 2: Das Fahrzeug stoppt (durch Kabel ziehen, Auto-Ladeziel erreicht, Pause durch PV-Regelung etc.) else if ( startZeitLaden && (status === "Finishing" || status === "Available" || status === "SuspendedEV" || status === "SuspendedEVSE") ) { if (startRefreshTimer) { clearTimeout(startRefreshTimer); startRefreshTimer = null; } // Um bei kurzen Unterbrechungen nicht sofort abzubrechen, warten wir 45 Sekunden (Entprellzeit). // Wechselt der Status in dieser Zeit zurück auf "Charging", läuft die Ladung oben nahtlos weiter. if (stopTimer) clearTimeout(stopTimer); stopTimer = setTimeout(() => { // 1. Batterieschutz aufheben: Min-SoC des Hausspeichers wieder auf den ursprünglichen Wert zurückstellen if (!isAuto && originalMinSoc !== null) { setState(IDS.minSocSet, Math.max(0, originalMinSoc)); const msg = `EV3 Manuelles Laden beendet. Hausbatterie MinSoc wieder auf ${originalMinSoc}% gestellt`; console.log(`[EV3 Master] ${msg}`); ev3Notify(`🔌 ${msg}`); originalMinSoc = null; setState(IDS.u_origSoc, 0, true); } // 2. Ladestatistik berechnen (Dauer in Min, geladene kWh und hinzugewonnene Kilometer je nach Jahreszeit) const stats = updateChargeStatistics(Date.now() - startZeitLaden); setState(IDS.u_timeDay, stats.totalMinToday, true); // 3. Prüfen, ob das Auto das Ladeziel erreicht hat: // Vergleicht den aktuellen SoC des Kia mit dem eingestellten Ladeziel der Wallbox/des Fahrzeugs const evSoc = Number(getState(IDS.soc)?.val) || 0; const targetSoc = Number(getState(IDS.targetSocSrv)?.val) || 100; let msgText = `❌ EV3 Ladung beendet. Heute geladen: ${stats.formattedTime} (+approx. ${stats.kmToday} km)`; let spokenText = `Ladung beendet. Heute geladen: ${stats.spokenTime}. Reichweite approx. ${stats.kmToday} Kilometer.`; // Wenn das Ladeziel erreicht wurde, ergänzen wir die Benachrichtigungen if (evSoc >= targetSoc) { msgText += ` - Das Ladeziel von ${targetSoc}% wurde erreicht`; spokenText += ` Das Ladeziel von ${targetSoc} Prozent wurde erreicht`; } // Benachrichtigung senden (Telegram, Gotify, Alexa/SayIt) ev3Notify(msgText, 1, spokenText); // 4. Ladevariablen zurücksetzen & Ladevorgang sauber abschließen startZeitLaden = null; setState(IDS.u_startTs, 0, true); setState(IDS.u_power, 0, true); // WICHTIG: Setzt transactionActive auf false, damit die Wallbox die Transaktion beendet // und die VIS-Visualisierung nicht mehr "lädt..." anzeigt (Logikfehler-Behebung). setState(IDS.wbTrans, false); stopTimer = null; // Finalen Status nach Ladeende vom Fahrzeug abrufen if (stopRefreshTimer) clearTimeout(stopRefreshTimer); stopRefreshTimer = setTimeout(() => { console.log( "[EV3 Master] Triggering post-stop vehicle refresh for final SoC and statistics.", ); if (existsState(IDS.refreshCar)) { setState(IDS.refreshCar, true); } stopRefreshTimer = null; }, STOP_REFRESH_DELAY_MS); }, DEBOUNCE_STOP_MS); } }); // Dynamischer Batterieschutz bei manuellem Laden: // Erhöht sich der SoC des Heimspeichers während des manuellen Ladens (z. B. durch PV-Ertrag), // wird der Min-SoC des Wechselrichters entsprechend nach oben angepasst. on({ id: IDS.batSocPV, change: "ne" }, (obj) => { const isAuto = !!getState(IDS.u_auto)?.val; const isCharging = getState(IDS.wbStat)?.val === "Charging"; if (!isAuto && isCharging) { const newSoc = Number(obj.state.val) || 0; const oldSoc = Number(obj.oldState?.val) || 0; if (newSoc > oldSoc) { if (originalMinSoc === null) { originalMinSoc = getState(IDS.minSocRead)?.val; setState(IDS.u_origSoc, originalMinSoc, true); } setState(IDS.minSocSet, Math.max(0, newSoc)); console.log( `[EV3 Master] Manual charging: Home battery SoC increased from ${oldSoc}% to ${newSoc}%. Updated MinSoC to ${newSoc}%.`, ); } } }); // --- 7. ZUSÄTZLICHE FUNKTIONEN --- /** * Verbindungs-Watchdog: Überwacht die Erreichbarkeit der Wallbox. * Meldet Statusänderungen (Anti-Spam) und löst nach 3 Min. Offline-Zeit einen Reconnect über den UniFi-AP aus. */ on({ id: IDS.wbConn, change: "ne" }, (obj) => { const isConnected = !!obj.state.val; if (!isConnected) { // Nur beim ersten Mal warnen if (!wasOfflineReported) { //console.warn("[EV3 Master] Wallbox-Verbindung verloren. Reconnect-Timer (3 Min) gestartet."); wasOfflineReported = true; } // Reconnect-Timer starten (falls nicht schon einer läuft) if (!reconnectTimer) { reconnectTimer = setTimeout(() => { //console.log("[EV3 Master] Führe WLAN-Neuverbindung über UniFi AP aus..."); setState(IDS.unifiReconnect, true); reconnectTimer = null; }, RECONNECT_WB_MS); } } else { // Wieder online: Status zurücksetzen und Timer stoppen if (wasOfflineReported) console.log("[EV3 Master] Wallbox connection restored."); wasOfflineReported = false; if (reconnectTimer) { clearTimeout(reconnectTimer); reconnectTimer = null; } } }); /** * Formatiert die verbleibende Ladezeit für die Anzeige in VIS. */ on({ id: IDS.remTime, change: "any" }, (obj) => { const m = obj.state.val; let t = "0:00"; if (m > 0) { const hh = Math.floor(m / 60); const mm = m % 60; t = `${hh}:${mm < 10 ? `0${mm}` : mm}`; } setState(IDS.u_rest, t, true); }); // Synchronisiert den VIS-Anzeige-Datenpunkt mit dem echten Ladeziel des Autos on({ id: IDS.targetSocSrv, change: "ne" }, (obj) => { console.log(`[EV3 Master] Charge target sync: Setting VIS slider to ${obj.state.val}%.`); setState(IDS.u_limit, obj.state.val, true); }); // Handler für manuelle Start-Anforderungen on({ id: IDS.u_startChargeRequest, val: true, change: "any" }, () => { console.log("[EV3 Master] Manual start request received via VIS."); triggerStartSequence("VIS-Manual"); setTimeout(() => { setState(IDS.u_startChargeRequest, false, true); }, 1000); }); // Täglicher Reset der Ladestatistik um 02:05 Uhr schedule("5 2 * * *", () => { setState(IDS.u_timeDay, 0, true); }); // Schutz der Kia 12V-Starterbatterie on({ id: IDS.bat12v, change: "ne" }, (obj) => { if (obj.state.val <= 50) ev3Notify(`⚠️ Kia 12V battery critical!`, 5); });Mein Ladeskript hat mehr als 700 Zeilen, aber bitte
Danke, das hat es in sich ;-) Ich probier' mal etwas davon aus.
Smart-Home-Automation unter IOB auf RASPI5, 16GB RAM, 50GB SSD, MariaSQL, VIS-2.
Anfang 2025 war ich Newbie in Sachen RaspBerry, Linux und IOB.
Jetzt läuft alles konstant gut und ich gebe gern meine Erfahrungen und mein Wissen an die Forums-User/Innen weiter.
Großes Danke an alle tatkräftigen Helfer im Forum!
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