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

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

Community Forum

donate donate
  1. ioBroker Community Home
  2. Deutsch
  3. Einsteigerfragen
  4. Einbindung von Geräten
  5. Hilfe: Adapter Fronius-Wattpilot kann nur mehr lesen

NEWS

  • Monatsrückblick Juli / August 2026 ist online!
    BluefoxB
    Bluefox
    9
    1
    439

  • Der neue Monatsrückblick für Mai und Juni 2026 ist online!
    BluefoxB
    Bluefox
    8
    1
    1.3k

  • wichtiges UPDATE für controller 7.2.2 im stable
    HomoranH
    Homoran
    10
    1
    3.8k

Hilfe: Adapter Fronius-Wattpilot kann nur mehr lesen

Geplant Angeheftet Gesperrt Verschoben Einbindung von Geräten
22 Beiträge 5 Kommentatoren 214 Aufrufe 3 Beobachtet
  • Älteste zuerst
  • Neuste zuerst
  • Meiste Stimmen
Antworten
  • In einem neuen Thema antworten
Anmelden zum Antworten
Dieses Thema wurde gelöscht. Nur Nutzer mit entsprechenden Rechten können es sehen.
  • mickemupM mickemup

    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?

    fuzzy1955F Online
    fuzzy1955F Online
    fuzzy1955
    schrieb am zuletzt editiert von
    #12

    @mickemup sagte:

    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!

    1 Antwort Letzte Antwort
    0
    • fuzzy1955F fuzzy1955

      @ste_glei sagte:

      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.

      Meister MopperM Online
      Meister MopperM Online
      Meister Mopper
      Most Active
      schrieb am zuletzt editiert von Meister Mopper
      #13

      @fuzzy1955 sagte:

      @ste_glei sagte:

      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.

      fuzzy1955F 1 Antwort Letzte Antwort
      0
      • Meister MopperM Meister Mopper

        @fuzzy1955 sagte:

        @ste_glei sagte:

        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.

        fuzzy1955F Online
        fuzzy1955F Online
        fuzzy1955
        schrieb am zuletzt editiert von
        #14

        @Meister-Mopper sagte:

        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!

        Meister MopperM 1 Antwort Letzte Antwort
        0
        • fuzzy1955F fuzzy1955

          @Meister-Mopper sagte:

          Internet benötige ich dafür nicht

          Aah, danke! Welchen OCPP-Provider (Host, Pfad) verwendest du da?

          Meister MopperM Online
          Meister MopperM Online
          Meister Mopper
          Most Active
          schrieb am zuletzt editiert von
          #15

          @fuzzy1955 sagte:

          @Meister-Mopper sagte:

          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.

          fuzzy1955F 1 Antwort Letzte Antwort
          0
          • Meister MopperM Meister Mopper

            @fuzzy1955 sagte:

            @Meister-Mopper sagte:

            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.

            fuzzy1955F Online
            fuzzy1955F Online
            fuzzy1955
            schrieb am zuletzt editiert von
            #16

            @Meister-Mopper sagte:

            Deine Wallbox beherrscht nach meiner Recherche das Protokoll ebenfalls

            Ja, das kann sie. Wie hast du diese Seite konfiguriert?

            dc4c7876-d4a3-4919-ba05-251b1306add5-image.jpeg

            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!

            Meister MopperM 1 Antwort Letzte Antwort
            0
            • fuzzy1955F fuzzy1955

              @Meister-Mopper sagte:

              Deine Wallbox beherrscht nach meiner Recherche das Protokoll ebenfalls

              Ja, das kann sie. Wie hast du diese Seite konfiguriert?

              dc4c7876-d4a3-4919-ba05-251b1306add5-image.jpeg

              Und hättest du ein JavaScript- Beispiel?

              Meister MopperM Online
              Meister MopperM Online
              Meister Mopper
              Most Active
              schrieb am zuletzt editiert von Meister Mopper
              #17

              @fuzzy1955

              Ich habe eine EVBox.

              Die verbindet sich mit ws://IPdesioBroker:Port ohne Benutzername und Passwort mit dem ioBroker. Der Port wird im Adapter festgelegt.

              6d2284a5-f73a-4318-a118-ec1fcc316075-image.jpeg

              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.

              fuzzy1955F 2 Antworten Letzte Antwort
              0
              • Meister MopperM Meister Mopper

                @fuzzy1955

                Ich habe eine EVBox.

                Die verbindet sich mit ws://IPdesioBroker:Port ohne Benutzername und Passwort mit dem ioBroker. Der Port wird im Adapter festgelegt.

                6d2284a5-f73a-4318-a118-ec1fcc316075-image.jpeg

                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);
                });
                
                

                fuzzy1955F Online
                fuzzy1955F Online
                fuzzy1955
                schrieb am zuletzt editiert von
                #18

                @Meister-Mopper sagte:

                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!

                1 Antwort Letzte Antwort
                0
                • Meister MopperM Meister Mopper

                  @fuzzy1955

                  Ich habe eine EVBox.

                  Die verbindet sich mit ws://IPdesioBroker:Port ohne Benutzername und Passwort mit dem ioBroker. Der Port wird im Adapter festgelegt.

                  6d2284a5-f73a-4318-a118-ec1fcc316075-image.jpeg

                  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);
                  });
                  
                  

                  fuzzy1955F Online
                  fuzzy1955F Online
                  fuzzy1955
                  schrieb zuletzt editiert von
                  #19

                  @Meister-Mopper sagte:

                  Die verbindet sich mit ws://IPdesioBroker:Port ohne Benutzername und Passwort mit dem ioBroker.

                  Hallo Meister-Mopper,

                  jetzt habe ich Stunden herumprobiert, aber es scheint, dass Fronius beim Wattpilot Go 11 J 2.0 mit der Firmware 42.5 die API UND OCPP deaktiviert hat.

                  Tests mit

                  ws://192.168.0.xxx:9220/ocpp/Wattpilot_xxxxxxxx oder 
                  ws://192.168.0.xxx:9220/Wattpilot_xxxxxxxx oder 
                  ws://192.168.0.xxx:9220/Wattpilot_xxxxxxxx/ocpp
                  
                  sudo tcpdump -i any port 9220
                  

                  brachten kein Ergebnis. Was tun?

                  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!

                  Meister MopperM 2 Antworten Letzte Antwort
                  0
                  • fuzzy1955F fuzzy1955

                    @Meister-Mopper sagte:

                    Die verbindet sich mit ws://IPdesioBroker:Port ohne Benutzername und Passwort mit dem ioBroker.

                    Hallo Meister-Mopper,

                    jetzt habe ich Stunden herumprobiert, aber es scheint, dass Fronius beim Wattpilot Go 11 J 2.0 mit der Firmware 42.5 die API UND OCPP deaktiviert hat.

                    Tests mit

                    ws://192.168.0.xxx:9220/ocpp/Wattpilot_xxxxxxxx oder 
                    ws://192.168.0.xxx:9220/Wattpilot_xxxxxxxx oder 
                    ws://192.168.0.xxx:9220/Wattpilot_xxxxxxxx/ocpp
                    
                    sudo tcpdump -i any port 9220
                    

                    brachten kein Ergebnis. Was tun?

                    Meister MopperM Online
                    Meister MopperM Online
                    Meister Mopper
                    Most Active
                    schrieb zuletzt editiert von
                    #20

                    @fuzzy1955

                    Nach allem, was ich lese, wird ocpp weiterhin unterstützt. Mach das Teil mal stromlos (am besten länger über Nacht).

                    Proxmox und HA ...
                    Ich schreibe den Code nicht mehr selbst – ich schimpfe mit der KI, bis es funktioniert.

                    1 Antwort Letzte Antwort
                    0
                    • fuzzy1955F fuzzy1955

                      @Meister-Mopper sagte:

                      Die verbindet sich mit ws://IPdesioBroker:Port ohne Benutzername und Passwort mit dem ioBroker.

                      Hallo Meister-Mopper,

                      jetzt habe ich Stunden herumprobiert, aber es scheint, dass Fronius beim Wattpilot Go 11 J 2.0 mit der Firmware 42.5 die API UND OCPP deaktiviert hat.

                      Tests mit

                      ws://192.168.0.xxx:9220/ocpp/Wattpilot_xxxxxxxx oder 
                      ws://192.168.0.xxx:9220/Wattpilot_xxxxxxxx oder 
                      ws://192.168.0.xxx:9220/Wattpilot_xxxxxxxx/ocpp
                      
                      sudo tcpdump -i any port 9220
                      

                      brachten kein Ergebnis. Was tun?

                      Meister MopperM Online
                      Meister MopperM Online
                      Meister Mopper
                      Most Active
                      schrieb zuletzt editiert von
                      #21

                      @fuzzy1955 sagte:

                      ws://192.168.0.xxx:9220/ocpp/Wattpilot_xxxxxxxx oder
                      ws://192.168.0.xxx:9220/Wattpilot_xxxxxxxx oder
                      ws://192.168.0.xxx:9220/Wattpilot_xxxxxxxx/ocpp

                      Wo gibst du das ein?

                      ws://192.168.0.xxx:9220 sagt der Wallbox, wohin sie ihr json senden und auf Befehle lauschen soll, mehr braucht es nicht (OCPP).

                      Übrigens:
                      Das Anonymisieren des Quadrupel ist hier unnötig, weils es lediglich im LAN definiert ist.

                      Proxmox und HA ...
                      Ich schreibe den Code nicht mehr selbst – ich schimpfe mit der KI, bis es funktioniert.

                      fuzzy1955F 1 Antwort Letzte Antwort
                      0
                      • Meister MopperM Meister Mopper

                        @fuzzy1955 sagte:

                        ws://192.168.0.xxx:9220/ocpp/Wattpilot_xxxxxxxx oder
                        ws://192.168.0.xxx:9220/Wattpilot_xxxxxxxx oder
                        ws://192.168.0.xxx:9220/Wattpilot_xxxxxxxx/ocpp

                        Wo gibst du das ein?

                        ws://192.168.0.xxx:9220 sagt der Wallbox, wohin sie ihr json senden und auf Befehle lauschen soll, mehr braucht es nicht (OCPP).

                        Übrigens:
                        Das Anonymisieren des Quadrupel ist hier unnötig, weils es lediglich im LAN definiert ist.

                        fuzzy1955F Online
                        fuzzy1955F Online
                        fuzzy1955
                        schrieb zuletzt editiert von
                        #22

                        @Meister-Mopper sagte:

                        Wo gibst du das ein

                        In der App der Wallbox. 192.168.100 ist der Raspi-IOBroker.

                        9dd55a76-5b59-4039-8ff1-075e01b5ba16-image.jpeg

                        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!

                        1 Antwort Letzte Antwort
                        0

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

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

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

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


                        Support us

                        ioBroker
                        Community Adapters
                        Donate

                        316

                        Online

                        33.0k

                        Benutzende

                        83.7k

                        Themen

                        1.3m

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

                        • Du hast noch kein Konto? Registrieren

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