Navigation

    Logo
    • Register
    • Login
    • Search
    • Recent
    • Tags
    • Unread
    • Categories
    • Unreplied
    • Popular
    • GitHub
    • Docu
    • Hilfe
    1. Home
    2. Gismoh

    NEWS

    • Neues Video über Aliase, virtuelle Geräte und Kategorien

    • Wir empfehlen: Node.js 22.x

    • Neuer Blog: Fotos und Eindrücke aus Solingen

    G
    • Profile
    • Following 1
    • Followers 0
    • Topics 25
    • Posts 356
    • Best 6
    • Groups 2

    Gismoh

    @Gismoh

    6
    Reputation
    28
    Profile views
    356
    Posts
    0
    Followers
    1
    Following
    Joined Last Online

    Gismoh Follow
    Pro Starter

    Best posts made by Gismoh

    • RE: diverse Probleme mit deConz >2.05.84

      @Markus84
      Super, besten Dank! 👍

      Werde mich dann gleich mal dransetzen 😉

      Nachtrag:
      Und hat geklappt 😉 - Besten Dank nochmal!
      Habe es mit

      wget http://deconz.dresden-elektronik.de/raspbian/stable/deconz-2.05.84-qt5.deb
      

      direkt auf dem Raspi heruntergeladen.

      posted in ioBroker Allgemein
      G
      Gismoh
    • RE: Test Adapter zigbee2mqtt

      Ahhh, ist das eine Wohltat, es funktioniert wieder. 👍

      Lag wohl wirklich nur daran, das sich ein Gerät irgendwie in der yaml mit "homeassistant" eingetragen hat - und dies nachträglich.
      Denn dieses Gerät (Bei mir eine Hue-Birne) war schon lange integriert gewesen.

      Sollte also jemand das selbe Problem haben/bekommen, dies hat bei mir geholfen:

      nano /opt/zigbee2mqtt/data/configuration.yaml
      

      Weiter runterscollen, dann werden die Geräte aufgelistet.
      Dann nach einem Gerät suchen, wo "homeassistant" Einträge vorhanden sind.
      Dort diese Einträge von "homeassistant" in der yaml löschen (waren bei mir drei Zeilen).

      War bei mir bei einem Gerät der Fall, und zwar genau bei diesem, bei welchem bereits gemeckert wurde:

      nach:

      cd /opt/zigbee2mqtt
      npm start
      
      - devices/0x00178801061f8b7b/homeassistant/name must be string
      

      Wenn man es nun weis, war es eigentlich ganz einfach,
      die verschiedenen Fehlermeldung (s.o.) haben mich "etwas" als Anfänger irritiert und mich daher auf Irrwege geschickt.

      posted in Tester
      G
      Gismoh
    • RE: JS für Überwachung der Luftfeuchtigkeit in Räumen

      @homoran
      Die Lüftungsempfehlungen kommen auch noch, dies ist dafür die "Vorarbeit".
      Wenn ich nun mal dransitze, ziehe ich es nun auch durch 😉

      Hatte ein altes Script in Benutzung, mit welchem ich nicht mehr zufrieden war, deswegen wollte ich einfach mal schauen, was nun dabei rauskommt 😉

      posted in JavaScript
      G
      Gismoh
    • RE: Error admin0 (783) socket.io [init] No user found in cookies

      @mcm1957
      Ja, wieder etwas dazugelernt.
      Da nur max. 7.6.3 direkt auswählbar war, hatte ich es über die "Katze" versucht.
      Erst danach durch probieren gesehen, das man auch wenn die 7.6.4 nicht "Direkt" aus wählbar ist,
      wenn man im Adapter über "bestimmte Version installieren" geht -
      Trotzdem die 7.6.4 eintragen kann - und diese dann installiert wird.

      Daher - wieder minimal schlauer geworden xD
      Merci!

      posted in ioBroker Allgemein
      G
      Gismoh
    • RE: JS für Überwachung der Luftfeuchtigkeit in Räumen

      @meister-mopper

      Der zweite Punkt ist mir aktuell nicht nur zu teuer, sondern auch zu unansehnlich (zwei Sensoren am Fenster).

      Die Sensoren für den ersten Punkt muss ich mir noch ansehen.

      Beim dritten Punkt musste ich gerade laut lachen.... 😉
      Wo meine Schwiegereltern noch die alten Fenster hatten, wo es einen separaten Hebel für die Kippfunktion gab,
      hatte ich denen diesen einfach an alle Fenster demontiert.
      Diese waren zwar am schimpfen über mich, ABER, hatten seit dem keinen Schimmel mehr an den Fenster Laibungen XD

      posted in JavaScript
      G
      Gismoh
    • JS für Überwachung der Luftfeuchtigkeit in Räumen

      Hallo zusammen,

      ich möchte euch heute mein neues ioBroker-Skript vorstellen, das Lüftungsempfehlungen basierend auf dem Raumklima gibt.
      Das Skript liest für mehrere Räume die aktuellen Sensorwerte (relative Luftfeuchte und Temperatur) ein und berechnet daraus zwei wichtige Empfehlungen:

      Feste Schwellenwert-Berechnung:
      Hier wird geprüft, ob die relative Luftfeuchte in einem Raum einen definierten Schwellenwert (z. B. 57 % oder individuell pro Raum) erreicht oder überschritten hat. Überschreitet der Wert den Schwellenwert, wird empfohlen, den Raum zu lüften.

      Absolute Luftfeuchte-Berechnung:
      Mittels der Formel
      AH = (6.112 * exp((17.67 * T) / (T + 243.5)) * RH * 2.1674) / (T + 273.15)
      wird die absolute Luftfeuchte (in g/m³) sowohl für die Innen- als auch die Außenluft berechnet. Für die Außendaten wird zusätzlich der Luftdruck als Korrekturfaktor einbezogen, sodass
      AH_out = AH * (P / 1013.25)
      entsteht. Vergleicht man nun die absolute Luftfeuchte im Raum (AH_in) mit der Außenluft (AH_out) – ggf. unter Berücksichtigung eines Offsets –, kann ermittelt werden, ob Lüften die Luftfeuchte senken würde.

      Das Skript ist dabei sehr modular aufgebaut und lässt sich über eine zentrale Konfiguration (config) steuern:

      Aktivierung einzelner Berechnungen:
      Ihr könnt separat die feste Schwellenwert-Berechnung und die absolute Luftfeuchte-Berechnung ein- bzw. ausschalten.

      Raumdefinition:
      Für jeden Raum sind die zugehörigen Sensoren (Datenpunkte für Temperatur und Luftfeuchte) und die Ziel-Datenpunkte (in die die Empfehlung geschrieben wird) hinterlegt. Zudem kann pro Raum ein individueller Schwellenwert definiert werden, andernfalls wird der globale Wert genutzt.

      Sensorverfügbarkeit und Sensoralter:
      Optional prüft das Skript, ob alle Sensoren erreichbar sind und ob sie innerhalb eines einstellbaren Zeitraums (z. B. 3 Stunden) aktualisiert wurden. Fehlende oder veraltete Sensorwerte werden mit Warnmeldungen (grafisch hervorgehoben durch das Symbol ⚠️) im Debug-Log ausgegeben.

      Zeitgesteuerte Ausführung:
      Um eine effiziente Auswertung zu gewährleisten, wird das Skript je nach Tageszeit unterschiedlich oft ausgeführt:
      Zwischen 06:00 und 22:59 läuft es alle 10 Minuten.
      Zwischen 23:00 und 05:59 wird es alle 30 Minuten ausgeführt.

      Zusätzlich wird das Skript beim Start einmalig initial ausgeführt.

      Debug-Log:
      Mit aktiviertem Debug-Modus werden sämtliche Schritte und Berechnungen – inklusive der Sensorprüfungen und Altersermittlungen – detailliert im ioBroker-Log ausgegeben. Dies erleichtert die Fehlersuche und Anpassung an eure Umgebung.

      Das Skript ist so konzipiert, dass es flexibel anpassen lässt. Alle wichtigen Parameter (wie Schwellenwerte, Offsets, Sensor-Alter) sind zentral einstellbar, sodass ihr schnell und unkompliziert Änderungen vornehmen könnt.

      /**
       * Skript: Lüftungsempfehlung basierend auf Raumklima
       *
       * Beschreibung:
       * Dieses Skript liest für mehrere Räume die aktuellen Sensorwerte für relative Luftfeuchte und Temperatur ein.
       * Es berechnet daraus zum einen:
       *   - Eine Lüftungsempfehlung auf Basis eines festen Schwellenwertes (z. B. 57 % relative Luftfeuchte).
       *   - Eine Empfehlung, ob eine Lüftung durch Austausch der Luft die absolute Luftfeuchte senken würde.
       * 
       * Für die absolute Luftfeuchte wird folgende Formel verwendet:
       *   AH = (6.112 * exp((17.67 * T)/(T + 243.5)) * RH * 2.1674) / (T + 273.15)
       * Für Außensensoren wird zusätzlich der Luftdruck als Korrekturfaktor einbezogen:
       *   AH_out = AH * (P / 1013.25)
       *
       * Beide Berechnungen können über die Konfiguration (enableFixedCalculation und
       * enableAbsoluteHumidityCalculation) ein- bzw. ausgeschaltet werden.
       *
       * Für jeden Raum werden die ermittelten Empfehlungen (true/false) in die definierten Datenpunkte geschrieben.
       *
       * Zusätzlich:
       * - Es gibt einen Debug-Log, der detaillierte Informationen zu den Berechnungen ausgibt,
       *   wenn debug auf true gesetzt ist.
       * - Optional wird geprüft, ob alle Sensoren erreichbar sind. Fehlende Sensoren werden im Debug-Log ausgegeben.
       * - Zusätzlich wird (optional) überprüft, ob Sensoren länger als ein einstellbarer Zeitraum (in Stunden)
       *   keinen aktualisierten Wert gesendet haben. Diese Warnung wird grafisch (z. B. mit "⚠️") im Debug-Log angezeigt.
       *
       * Die Berechnungen werden je nach Tageszeit unterschiedlich oft ausgeführt:
       *   - Zwischen 06:00 und 22:59 alle 10 Minuten
       *   - Zwischen 23:00 und 05:59 alle 30 Minuten
       */
      
      // =============================
      // Konfiguration
      // =============================
      var config = {
          // Berechnungen ein- bzw. ausschalten:
          enableFixedCalculation: true,              // Aktiviert die Berechnung der Lüftungsempfehlung basierend auf einem festen RH-Schwellenwert.
          enableAbsoluteHumidityCalculation: true,   // Aktiviert die Berechnung, ob Lüften (Austausch mit Außenluft) die absolute Luftfeuchte senkt.
          globalRHThreshold: 40,                       // Global einstellbarer Schwellenwert für relative Luftfeuchte in %.
          absoluteHumidityOffset: 0,                   // Optionaler Offset in g/m³, der beim Vergleich der absoluten Luftfeuchte berücksichtigt wird.
          debug: true,                                 // Schaltet die ausführliche Debug-Ausgabe an (true) oder aus (false).
      
          // Optionale Prüfung der Sensorverfügbarkeit:
          enableSensorCheck: true,
      
          // Optionale Prüfung des Sensoralters:
          // Es wird geprüft, ob ein Sensor länger als maxAgeHours keinen neuen Wert gesendet hat.
          sensorAgeCheck: {
              enabled: true,
              maxAgeHours: 3  // Maximale erlaubte Zeitspanne (in Stunden) ohne Aktualisierung.
          },
      
          // Definition der Räume:
          rooms: {
              "Wohnzimmer": {
                  enabled: true,
                  indoorRH: "Euer.Datenpunkt.des.Objekts.Feuchtigkeit",
                  indoorTemp: "Euer.Datenpunkt.des.Objekts.Temperatur",
                  fixedDP: "0_userdata.0.EigeneDatenpunkte.Raumklima.Luefungsempfehlung.FesterWert-ueberschritten.Wohnzimmer",
                  reduceDP: "0_userdata.0.EigeneDatenpunkte.Raumklima.Luefungsempfehlung.LF-Senken-moeglich.GG-Wohnzimmer",
                  threshold: null  // Falls null, wird der globale Schwellenwert verwendet.
              },
              "Arbeitszimmer": {
                  enabled: true,
                  indoorRH: "Euer.Datenpunkt.des.Objekts.Feuchtigkeit",
                  indoorTemp: "Euer.Datenpunkt.des.Objekts.Temperatur",
                  fixedDP: "0_userdata.0.EigeneDatenpunkte.Raumklima.Luefungsempfehlung.FesterWert-ueberschritten.Arbeitszimmer",
                  reduceDP: "0_userdata.0.EigeneDatenpunkte.Raumklima.Luefungsempfehlung.LF-Senken-moeglich.Arbeitszimmer",
                  threshold: null
              },
              "Badezimmer": {
                  enabled: true,
                  indoorRH: "Euer.Datenpunkt.des.Objekts.Feuchtigkeit",
                  indoorTemp: "Euer.Datenpunkt.des.Objekts.Temperatur",
                  fixedDP: "0_userdata.0.EigeneDatenpunkte.Raumklima.Luefungsempfehlung.FesterWert-ueberschritten.Badezimmer",
                  reduceDP: "0_userdata.0.EigeneDatenpunkte.Raumklima.Luefungsempfehlung.LF-Senken-moeglich.Badezimmer",
                  threshold: null
              },
              "Buegelzimmer": {
                  enabled: true,
                  indoorRH: "Euer.Datenpunkt.des.Objekts.Feuchtigkeit",
                  indoorTemp: "Euer.Datenpunkt.des.Objekts.Temperatur",
                  fixedDP: "0_userdata.0.EigeneDatenpunkte.Raumklima.Luefungsempfehlung.FesterWert-ueberschritten.Buegelzimmer",
                  reduceDP: "0_userdata.0.EigeneDatenpunkte.Raumklima.Luefungsempfehlung.LF-Senken-moeglich.Buegelzimmer",
                  threshold: null
              },
              "Essflur": {
                  enabled: true,
                  indoorRH: "Euer.Datenpunkt.des.Objekts.Feuchtigkeit",
                  indoorTemp: "Euer.Datenpunkt.des.Objekts.Temperatur",
                  fixedDP: "0_userdata.0.EigeneDatenpunkte.Raumklima.Luefungsempfehlung.FesterWert-ueberschritten.Essflur",
                  reduceDP: "0_userdata.0.EigeneDatenpunkte.Raumklima.Luefungsempfehlung.LF-Senken-moeglich.Essflur",
                  threshold: null
              },
              "Schlafzimmer": {
                  enabled: true,
                  indoorRH: "Euer.Datenpunkt.des.Objekts.Feuchtigkeit",
                  indoorTemp: "Euer.Datenpunkt.des.Objekts.Temperatur",
                  fixedDP: "0_userdata.0.EigeneDatenpunkte.Raumklima.Luefungsempfehlung.FesterWert-ueberschritten.Schlafzimmer",
                  reduceDP: "0_userdata.0.EigeneDatenpunkte.Raumklima.Luefungsempfehlung.LF-Senken-moeglich.Schlafzimmer",
                  threshold: null
              },
              "Waschkueche": {
                  enabled: true,
                  indoorRH: "Euer.Datenpunkt.des.Objekts.Feuchtigkeit",
                  indoorTemp: "Euer.Datenpunkt.des.Objekts.Temperatur",
                  fixedDP: "0_userdata.0.EigeneDatenpunkte.Raumklima.Luefungsempfehlung.FesterWert-ueberschritten.Waschkueche",
                  reduceDP: "0_userdata.0.EigeneDatenpunkte.Raumklima.Luefungsempfehlung.LF-Senken-moeglich.Waschkueche",
                  threshold: null
              },
              "GG-Kueche": {
                  enabled: true,
                  indoorRH: "Euer.Datenpunkt.des.Objekts.Feuchtigkeit",
                  indoorTemp: "Euer.Datenpunkt.des.Objekts.Temperatur",
                  fixedDP: "0_userdata.0.EigeneDatenpunkte.Raumklima.Luefungsempfehlung.FesterWert-ueberschritten.GG-Kueche",
                  reduceDP: "0_userdata.0.EigeneDatenpunkte.Raumklima.Luefungsempfehlung.LF-Senken-moeglich.GG-Kueche",
                  threshold: null
              },
              "GG-Schlafzimmer": {
                  enabled: true,
                  indoorRH: "Euer.Datenpunkt.des.Objekts.Feuchtigkeit",
                  indoorTemp: "Euer.Datenpunkt.des.Objekts.Temperatur",
                  fixedDP: "0_userdata.0.EigeneDatenpunkte.Raumklima.Luefungsempfehlung.FesterWert-ueberschritten.GG-Schlafzimmer",
                  reduceDP: "0_userdata.0.EigeneDatenpunkte.Raumklima.Luefungsempfehlung.LF-Senken-moeglich.GG-Schlafzimmer",
                  threshold: null
              },
              "GG-Wohnzimmer": {
                  enabled: true,
                  indoorRH: "Euer.Datenpunkt.des.Objekts.Feuchtigkeit",
                  indoorTemp: "Euer.Datenpunkt.des.Objekts.Temperatur",
                  fixedDP: "0_userdata.0.EigeneDatenpunkte.Raumklima.Luefungsempfehlung.FesterWert-ueberschritten.GG-Wohnzimmer",
                  reduceDP: "0_userdata.0.EigeneDatenpunkte.Raumklima.Luefungsempfehlung.LF-Senken-moeglich.GG-Wohnzimmer",
                  threshold: null
              },
              "GG-Bad": {
                  enabled: false,
                  indoorRH: "Euer.Datenpunkt.des.Objekts.Feuchtigkeit",
                  indoorTemp: "Euer.Datenpunkt.des.Objekts.Temperatur",
                  fixedDP: "0_userdata.0.EigeneDatenpunkte.Raumklima.Luefungsempfehlung.FesterWert-ueberschritten.GG-Bad",
                  reduceDP: "0_userdata.0.EigeneDatenpunkte.Raumklima.Luefungsempfehlung.LF-Senken-moeglich.GG-Bad",
                  threshold: null
              }
          },
      
          // Definition der Außensensoren:
          outside: {
              rh: "Euer.Datenpunkt.des.Objekts.Luftfeuchte",
              temp: "Euer.Datenpunkt.des.Objekts.Temperatur",
              pressure: "Euer.Datenpunkt.des.Objekts.Luftdruck"
          }
      };
      
      // =============================
      // Funktion: calcAbsoluteHumidity
      // =============================
      /**
       * Berechnet die absolute Luftfeuchte (g/m³) basierend auf Temperatur, relativer Luftfeuchte
       * und optional dem Luftdruck.
       *
       * Formel:
       *   AH = (6.112 * exp((17.67 * T) / (T + 243.5)) * RH * 2.1674) / (T + 273.15)
       *
       * Falls der optionale Parameter "pressure" (Luftdruck in hPa) übergeben wird, wird der Wert
       * als Korrekturfaktor (bezogen auf 1013.25 hPa) einbezogen:
       *   AH = AH * (pressure / 1013.25)
       *
       * @param {number} T - Temperatur in °C.
       * @param {number} RH - Relative Luftfeuchte in %.
       * @param {number} [pressure] - (Optional) Luftdruck in hPa.
       * @returns {number} Berechnete absolute Luftfeuchte in g/m³.
       */
      function calcAbsoluteHumidity(T, RH, pressure) {
          var AH = (6.112 * Math.exp((17.67 * T) / (T + 243.5)) * RH * 2.1674) / (T + 273.15);
          if (pressure !== undefined && pressure > 0) {
              AH = AH * (pressure / 1013.25);
          }
          return AH;
      }
      
      // =============================
      // Funktion: checkSensorAge
      // =============================
      /**
       * Prüft, ob ein Sensor seit mehr als der konfigurierten Zeit (in Stunden) keinen neuen Wert gesendet hat.
       *
       * Falls der Sensor zu alt ist, wird eine Warnung mit grafischer Kennzeichnung (⚠️) im Debug-Log ausgegeben.
       *
       * @param {object} sensorState - Der Zustand des Sensors, der u.a. den Zeitstempel (ts) enthalten sollte.
       * @param {string} sensorName - Bezeichner des Sensors (z. B. Datenpunktname), der in der Warnung angezeigt wird.
       */
      function checkSensorAge(sensorState, sensorName) {
          if (config.sensorAgeCheck && config.sensorAgeCheck.enabled && sensorState && sensorState.ts) {
              var now = Date.now();
              var age = (now - sensorState.ts) / 3600000; // Zeitdifferenz in Stunden
              if (age > config.sensorAgeCheck.maxAgeHours) {
                  // Grafische Kennzeichnung mit dem Warnsymbol ⚠️
                  if (config.debug) {
                      log("⚠️⚠️⚠️ Sensor " + sensorName + " hat seit " + age.toFixed(2) +
                          " Stunden keinen neuen Wert gesendet!", "warn");
                  }
              }
          }
      }
      
      // =============================
      // Funktion: runCalculation
      // =============================
      /**
       * Führt die vollständige Berechnung der Lüftungsempfehlungen durch.
       *
       * Ablauf:
       * 1. Außensensoren auslesen und (optional) auf Verfügbarkeit sowie das Sensoralter prüfen.
       * 2. Berechne die absolute Luftfeuchte für die Außenluft.
       * 3. Für jeden aktivierten Raum:
       *    a. Raum-Sensoren (Temperatur und RH) auslesen, optional deren Verfügbarkeit und Alter prüfen.
       *    b. Berechne die absolute Luftfeuchte im Raum.
       *    c. Falls aktiviert: Vergleiche den gemessenen RH-Wert mit dem Schwellenwert und speichere das Ergebnis in fixedDP.
       *    d. Falls aktiviert: Vergleiche die absolute Luftfeuchte im Raum (AH_in) mit der Außenluft (AH_out) (plus Offset)
       *       und speichere das Ergebnis in reduceDP.
       *
       * Bei fehlenden kritischen Sensoren (sowohl außen als auch pro Raum) wird eine Warnung im Debug-Log ausgegeben
       * und die Berechnung (bzw. für den Raum) abgebrochen.
       */
      function runCalculation() {
          // Außensensoren auslesen
          var outsideTempState = getState(config.outside.temp);
          var outsideRHState = getState(config.outside.rh);
          var outsidePressureState = getState(config.outside.pressure);
      
          // Optionale Prüfung der Außensensoren (Verfügbarkeit)
          if (config.enableSensorCheck) {
              var missingOutsideSensors = [];
              if (!outsideTempState) missingOutsideSensors.push("Außentemperatur (" + config.outside.temp + ")");
              if (!outsideRHState) missingOutsideSensors.push("Außen RH (" + config.outside.rh + ")");
              if (!outsidePressureState) missingOutsideSensors.push("Außen Druck (" + config.outside.pressure + ")");
              if (missingOutsideSensors.length > 0) {
                  log("Fehlende Außensensoren: " + missingOutsideSensors.join(", "), "warn");
              }
          }
      
          // Sensor-Alter-Prüfung für Außensensoren
          if (config.sensorAgeCheck && config.sensorAgeCheck.enabled) {
              checkSensorAge(outsideTempState, config.outside.temp);
              checkSensorAge(outsideRHState, config.outside.rh);
              checkSensorAge(outsidePressureState, config.outside.pressure);
          }
      
          // Kritische Außensensoren müssen vorhanden sein; andernfalls wird die Berechnung abgebrochen.
          if (!outsideTempState || !outsideRHState || !outsidePressureState) {
              log("Kritische Außensensoren nicht vollständig verfügbar! Berechnung wird abgebrochen.", "error");
              return;
          }
      
          // Umwandlung der Außensensorwerte in Zahlen
          var outsideTemp = parseFloat(outsideTempState.val);
          var outsideRH = parseFloat(outsideRHState.val);
          var outsidePressure = parseFloat(outsidePressureState.val);
          var AH_out = calcAbsoluteHumidity(outsideTemp, outsideRH, outsidePressure);
      
          // Debug-Ausgabe der Außensensorwerte und der berechneten absoluten Luftfeuchte
          if (config.debug) {
              log("Außenwerte: Temp=" + outsideTemp + "°C, RH=" + outsideRH + "%, Druck=" + outsidePressure +
                  " hPa, AH_out=" + AH_out.toFixed(2) + " g/m³");
          }
      
          // Iteration über alle Räume
          for (var roomName in config.rooms) {
              var room = config.rooms[roomName];
              if (!room.enabled) {
                  if (config.debug) log("Raum " + roomName + " ist deaktiviert.");
                  continue;
              }
              
              // Raum-Sensoren auslesen
              var indoorTempState = getState(room.indoorTemp);
              var indoorRHState = getState(room.indoorRH);
      
              // Optionale Prüfung der Raum-Sensoren (Verfügbarkeit)
              if (config.enableSensorCheck) {
                  var missingRoomSensors = [];
                  if (!indoorTempState) missingRoomSensors.push("Temperatur (" + room.indoorTemp + ")");
                  if (!indoorRHState) missingRoomSensors.push("Luftfeuchte (" + room.indoorRH + ")");
                  if (missingRoomSensors.length > 0) {
                      log("Fehlende Sensoren für " + roomName + ": " + missingRoomSensors.join(", "), "warn");
                  }
              }
      
              // Sensor-Alter-Prüfung für Raum-Sensoren
              if (config.sensorAgeCheck && config.sensorAgeCheck.enabled) {
                  checkSensorAge(indoorTempState, room.indoorTemp + " in " + roomName);
                  checkSensorAge(indoorRHState, room.indoorRH + " in " + roomName);
              }
              
              // Falls kritische Sensoren im Raum fehlen, wird dieser übersprungen.
              if (!indoorTempState || !indoorRHState) {
                  if (config.debug) log("Berechnung für " + roomName + " wird übersprungen, da Sensoren fehlen.", "warn");
                  continue;
              }
              
              // Umwandlung der Raumwerte in Zahlen und Berechnung der absoluten Luftfeuchte im Raum
              var indoorTemp = parseFloat(indoorTempState.val);
              var indoorRH = parseFloat(indoorRHState.val);
              var AH_in = calcAbsoluteHumidity(indoorTemp, indoorRH);
              
              // -----------------------------
              // Feste Schwellenwert-Berechnung (Fixed Calculation)
              // -----------------------------
              if (config.enableFixedCalculation) {
                  var threshold = (room.threshold !== null) ? room.threshold : config.globalRHThreshold;
                  var fixedRecommendation = (indoorRH >= threshold);
                  setState(room.fixedDP, fixedRecommendation, true);
                  if (config.debug) {
                      log("[" + roomName + "] Fixed Calculation: RH_in=" + indoorRH + "%, Threshold=" + threshold +
                          " -> Empfehlung Lüften: " + fixedRecommendation);
                  }
              }
              
              // -----------------------------
              // Berechnung, ob Lüften die absolute Luftfeuchte senken kann (Absolute Humidity Calculation)
              // -----------------------------
              if (config.enableAbsoluteHumidityCalculation) {
                  var reduceRec = (AH_in > (AH_out + config.absoluteHumidityOffset));
                  setState(room.reduceDP, reduceRec, true);
                  if (config.debug) {
                      log("[" + roomName + "] Absolute Humidity Calculation: AH_in=" + AH_in.toFixed(2) + " g/m³, AH_out=" +
                          AH_out.toFixed(2) + " g/m³, Offset=" + config.absoluteHumidityOffset +
                          " -> Lüften senkt Luftfeuchte: " + reduceRec);
                  }
              }
          }
      }
      
      // =============================
      // Zeitgesteuerte Ausführung (Scheduling)
      // =============================
      //
      // Der erste Cron-Job führt runCalculation() alle 10 Minuten im Zeitfenster von 06:00 bis 22:59 aus.
      // Der zweite Cron-Job führt runCalculation() alle 30 Minuten im Zeitfenster von 23:00 bis 05:59 aus.
      // Beim Laden des Skripts wird runCalculation() einmalig initial aufgerufen.
      
      // Von 06:00 bis 22:59 alle 10 Minuten:
      schedule("*/10 6-22 * * *", function() {
          runCalculation();
          if (config.debug) log("runCalculation ausgelöst (10-Minuten-Intervall)", "info");
      });
      
      // Von 23:00 bis 05:59 alle 30 Minuten:
      schedule("*/30 23,0-5 * * *", function() {
          runCalculation();
          if (config.debug) log("runCalculation ausgelöst (30-Minuten-Intervall)", "info");
      });
      
      // Initialer Aufruf beim Laden des Skripts:
      runCalculation();
      
      

      Um noch die Datenpunkte "onthefly" erstellen zu können, dieses Anpassbare Script:

      /**
       * Skript: Ziel-Datenpunkte erstellen
       *
       * Beschreibung:
       * Dieses Skript erstellt alle in der Konfiguration definierten Ziel-Datenpunkte, 
       * in denen später die Lüftungsempfehlungen abgelegt werden.
       * Für jeden Raum werden zwei Datenpunkte erzeugt:
       *   - fixedDP: Für die Lüftungsempfehlung basierend auf einem festen Schwellenwert der relativen Luftfeuchte.
       *   - reduceDP: Für die Empfehlung, ob Lüften die absolute Luftfeuchte senken kann.
       *
       * Die Datenpunkte werden als Boolean (true/false) erstellt.
       *
       * Hinweis: Falls ein Datenpunkt bereits existiert, wird er nicht erneut erstellt.
       *
       * Konfiguration:
       * Die Räume und die zugehörigen Datenpunkt-Namen werden in der Variable "config" definiert.
       */
      
      // =============================
      // Konfiguration
      // =============================
      var config = {
          rooms: {
              "Wohnzimmer": {
                  fixedDP: "0_userdata.0.EigeneDatenpunkte.Raumklima.Luefungsempfehlung.FesterWert-ueberschritten.Wohnzimmer",
                  reduceDP: "0_userdata.0.EigeneDatenpunkte.Raumklima.Luefungsempfehlung.LF-Senken-moeglich.GG-Wohnzimmer"
              },
              "Arbeitszimmer": {
                  fixedDP: "0_userdata.0.EigeneDatenpunkte.Raumklima.Luefungsempfehlung.FesterWert-ueberschritten.Arbeitszimmer",
                  reduceDP: "0_userdata.0.EigeneDatenpunkte.Raumklima.Luefungsempfehlung.LF-Senken-moeglich.Arbeitszimmer"
              },
              "Badezimmer": {
                  fixedDP: "0_userdata.0.EigeneDatenpunkte.Raumklima.Luefungsempfehlung.FesterWert-ueberschritten.Badezimmer",
                  reduceDP: "0_userdata.0.EigeneDatenpunkte.Raumklima.Luefungsempfehlung.LF-Senken-moeglich.Badezimmer"
              },
              "Buegelzimmer": {
                  fixedDP: "0_userdata.0.EigeneDatenpunkte.Raumklima.Luefungsempfehlung.FesterWert-ueberschritten.Buegelzimmer",
                  reduceDP: "0_userdata.0.EigeneDatenpunkte.Raumklima.Luefungsempfehlung.LF-Senken-moeglich.Buegelzimmer"
              },
              "Essflur": {
                  fixedDP: "0_userdata.0.EigeneDatenpunkte.Raumklima.Luefungsempfehlung.FesterWert-ueberschritten.Essflur",
                  reduceDP: "0_userdata.0.EigeneDatenpunkte.Raumklima.Luefungsempfehlung.LF-Senken-moeglich.Essflur"
              },
              "Schlafzimmer": {
                  fixedDP: "0_userdata.0.EigeneDatenpunkte.Raumklima.Luefungsempfehlung.FesterWert-ueberschritten.Schlafzimmer",
                  reduceDP: "0_userdata.0.EigeneDatenpunkte.Raumklima.Luefungsempfehlung.LF-Senken-moeglich.Schlafzimmer"
              },
              "Waschkueche": {
                  fixedDP: "0_userdata.0.EigeneDatenpunkte.Raumklima.Luefungsempfehlung.FesterWert-ueberschritten.Waschkueche",
                  reduceDP: "0_userdata.0.EigeneDatenpunkte.Raumklima.Luefungsempfehlung.LF-Senken-moeglich.Waschkueche"
              },
              "GG-Kueche": {
                  fixedDP: "0_userdata.0.EigeneDatenpunkte.Raumklima.Luefungsempfehlung.FesterWert-ueberschritten.GG-Kueche",
                  reduceDP: "0_userdata.0.EigeneDatenpunkte.Raumklima.Luefungsempfehlung.LF-Senken-moeglich.GG-Kueche"
              },
              "GG-Schlafzimmer": {
                  fixedDP: "0_userdata.0.EigeneDatenpunkte.Raumklima.Luefungsempfehlung.FesterWert-ueberschritten.GG-Schlafzimmer",
                  reduceDP: "0_userdata.0.EigeneDatenpunkte.Raumklima.Luefungsempfehlung.LF-Senken-moeglich.GG-Schlafzimmer"
              },
              "GG-Wohnzimmer": {
                  fixedDP: "0_userdata.0.EigeneDatenpunkte.Raumklima.Luefungsempfehlung.FesterWert-ueberschritten.GG-Wohnzimmer",
                  reduceDP: "0_userdata.0.EigeneDatenpunkte.Raumklima.Luefungsempfehlung.LF-Senken-moeglich.GG-Wohnzimmer"
              },
              "GG-Bad": {
                  fixedDP: "0_userdata.0.EigeneDatenpunkte.Raumklima.Luefungsempfehlung.FesterWert-ueberschritten.GG-Bad",
                  reduceDP: "0_userdata.0.EigeneDatenpunkte.Raumklima.Luefungsempfehlung.LF-Senken-moeglich.GG-Bad"
              }
          }
      };
      
      // Optionaler Debug-Modus (true = ausführliche Logausgaben)
      var debug = true;
      
      /**
       * Erstellt einen Datenpunkt, falls dieser noch nicht existiert.
       *
       * @param {string} dp - Der vollständige Name des Datenpunkts.
       * @param {object} options - Optionen für den Datenpunkt (z. B. Typ, Rolle, Beschreibung).
       */
      function createTargetState(dp, options) {
          if (!existsState(dp)) {
              // Erstelle den Datenpunkt mit initialem Wert false.
              createState(dp, false, options);
              if (debug) log("Datenpunkt erstellt: " + dp, "info");
          } else {
              if (debug) log("Datenpunkt existiert bereits: " + dp, "info");
          }
      }
      
      // =============================
      // Hauptteil: Ziel-Datenpunkte erstellen
      // =============================
      for (var roomName in config.rooms) {
          var room = config.rooms[roomName];
      
          // Erstelle den Datenpunkt für die feste Schwellenwert-Berechnung (fixedDP)
          createTargetState(room.fixedDP, {
              name: "Lüftungsempfehlung (Fester Wert) für " + roomName,
              type: "boolean",
              role: "indicator"
          });
      
          // Erstelle den Datenpunkt für die absolute Luftfeuchte-Berechnung (reduceDP)
          createTargetState(room.reduceDP, {
              name: "Lüftungsempfehlung (LF senken möglich) für " + roomName,
              type: "boolean",
              role: "indicator"
          });
      }
      
      log("Alle Ziel-Datenpunkte wurden überprüft und ggf. erstellt.", "info");
      
      

      Ihr müsst natürlich die Sensoren durch eure ersetzen und ggf. andere gewünschte Einstellungen anpassen.

      Bei Fragen oder Optimierungsideen, gerne her damit 😉

      posted in JavaScript
      G
      Gismoh

    Latest posts made by Gismoh

    • RE: Shelly 3PM Pro Emulator

      @ottelo @steffen-5

      danke für eure Rückmeldungen.

      Ich bin da aktuell noch mitten in der Umsetzung.
      Grundsätzlich funktioniert die reine Skriptlösung – man braucht also keinen zusätzlichen Docker oder andere externe Tools. JS-Skripte im ioBroker reichen völlig aus.

      Derzeit arbeite ich eher an der Steuerung und Logik. Ziel ist u.a. eine SoC-Berücksichtigung, die Marstek selbst nicht vorsieht. Also z.B.: Akku nur bis XX % laden oder nur bis XX % entladen – frei im Skript einstellbar.

      Außerdem möchte ich eine regelbare Lade-/Entladeleistung integrieren.
      In der App kann man zwar Grenzen setzen (z.B. max. 800 W oder 2.500 W), aber:

      Bei der Entladung halten sich die Speicher an die eingestellte Begrenzung (u.a. wegen Leitungs-/Brandschutz).

      Beim Laden leider nicht – selbst wenn man 800 W vorgibt, ziehen die Speicher problemlos 2.500 W. Für die Leitungen macht es aber keinen Unterschied, ob der Strom „+“ oder „–“ ist (Laden/Entladen) – belastet werden sie gleich stark.

      Da ich selbst nicht programmieren kann, nutze ich KI zur Unterstützung. Allerdings stoße ich dabei immer wieder (trotz bezahlter Tarife) auf Sperren und Timeouts, was das Ganze ziemlich ausbremst.

      Als Nächstes möchte ich eine Mehr-Speicher-Lösung sauber umsetzen. Erste Ansätze habe ich bereits bei mir integriert. Auch der API-Abruf läuft momentan noch in einem separaten Skript – den würde ich dann einarbeiten, sobald die Basis stabil läuft.

      posted in ioBroker Allgemein
      G
      Gismoh
    • RE: Shelly 3PM Pro Emulator

      Moin @steffen-5
      , besten Dank und sehr gerne.
      Bin gerade leider etwas im Streß, und sehe mir später deinen Kommentar noch mal an.
      Aktuell sind meine Scripte eh bereits etwas anders - hatte noch einen "Blöden" Fehler bei mir gehabt.

      posted in ioBroker Allgemein
      G
      Gismoh
    • RE: Shelly 3PM Pro Emulator

      @cwa
      Gerne 😉

      Evtl. bin ich dann auch schon weiter mit dem Regelverhalten.

      posted in ioBroker Allgemein
      G
      Gismoh
    • RE: Shelly 3PM Pro Emulator

      @gismoh

      3-MARSTEK-Monitor-Control:

      // ===================================================
      // MARSTEK-Monitor-Control v2.0 - ÜBERWACHUNG & STEUERUNG
      // ===================================================
      // 
      // 📊 ZWECK: ÜBERWACHUNG, DIAGNOSE & STEUERUNG
      // ✅ Steuerungsmodus-Erkennung (CT vs Manuell)
      // ✅ API-Integration + Smart-Timing
      // ✅ Diagnose-System + Datenpunkte-Verwaltung
      // ✅ Debug-Modi + Override-Funktionen
      // ✅ SOC-Daten für Intelligence-Script v2.0
      // ✅ v2.0: Kompatibel mit korrigierter Regelungslogik
      // 
      // ⚠️ DARF CRASHEN ohne Core/Intelligence zu stoppen!
      // 
      // ===================================================
      
      // KONFIGURATION
      const MONITOR_CONFIG = {
          // API-Zugangsdaten (werden aus Datenpunkten gelesen)
          API_ENABLED: true,
          API_INTERVAL: 300000,
          
          // Steuerungsmodus-Erkennung
          MODE_DETECTION_ENABLED: true,
          
          // Timing
          UPDATE_INTERVAL: 10000,  // Monitor läuft langsamer
          
          // Kommunikation - BITTE ANPASSEN!
          SHARED_PREFIX: '0_userdata.0.marstek_V-E.shared.',        // ← Dein Shared-Prefix hier eintragen
          INTEL_PREFIX: '0_userdata.0.marstek_V-E.intel.',          // ← Dein Intelligence-Prefix hier eintragen
          MONITOR_PREFIX: '0_userdata.0.marstek_V-E.monitor.',      // ← Dein Monitor-Prefix hier eintragen
          
          // Logging
          LOG_MONITOR: true,
          LOG_MODE_DETECTION: true,
          LOG_API: true
      };
      
      // MONITOR STATE
      let monitorState = {
          // Von Core/Intelligence empfangen
          coreActive: false,
          intelActive: false,
          tibberCurrent: 0,
          reportedPower: 0,          // v2.0: Neuer Name für gemeldeten Wert
          limitingActive: false,
          
          // API-Daten
          deviceSoc: 0,
          deviceCharge: 0,
          deviceDischarge: 0,
          lastReportTime: 0,
          
          // Steuerungsmodus-Erkennung
          controlMode: 'unbekannt',
          controlModeConfidence: 'niedrig',
          lastModeDetection: 0,
          modeDetectionHistory: [],
          apiModeData: {},
          behaviorModeData: {
              responseCount: 0,
              noResponseCount: 0,
              avgEfficiency: 0,
              lastBehaviorCheck: 0
          },
          
          // API-Timing
          apiState: {
              lastFetch: 0,
              lastActivity: Date.now(),
              nextFetchTime: 0
          },
          
          // Status & Diagnose
          monitorActive: true,
          lastUpdate: 0,
          systemStatus: 'starting',
          alarms: [],
          
          // Override-Modi
          overrideMode: '',
          manualModeOverride: ''
      };
      
      // ===================================================
      // MONITOR DATENPUNKTE
      // ===================================================
      
      function createMonitorDatapoints() {
          const prefix = MONITOR_CONFIG.MONITOR_PREFIX;
          
          const datapoints = [
              // API-Konfiguration - BITTE AUSFÜLLEN!
              { name: 'mailbox', value: '', desc: 'MARSTEK Mailbox (deine E-Mail)' },
              { name: 'token', value: '', desc: 'MARSTEK API Token (aus App)' },
              { name: 'deviceId', value: 'XXXX', desc: 'MARSTEK Device ID (z.B. 3456)' },      // ← ANPASSEN!
              { name: 'enable_second_storage', value: false, desc: '🔄 Zweiter Speicher (MSTYYYY)' },
              
              // Status
              { name: 'system_status', value: 'starting', desc: '🔧 System-Status (shared)', shared: true },
              { name: 'monitor_active', value: true, desc: '📊 Monitor läuft' },
              { name: 'api_connected', value: false, desc: '📡 API verbunden' },
              
              // Steuerungsmodus-Erkennung
              { name: 'control_mode_detected', value: 'unbekannt', desc: '🎮 Erkannter Steuerungsmodus' },
              { name: 'control_mode_confidence', value: 'niedrig', desc: '🎯 Erkennungs-Sicherheit' },
              { name: 'control_mode_method', value: 'keine', desc: '🔍 Erkennungsmethode' },
              { name: 'manual_mode_active', value: false, desc: '⚠️ Manueller Modus aktiv' },
              { name: 'regulation_possible', value: true, desc: '✅ Regelung möglich' },
              { name: 'check_mode_now', value: false, desc: '🔄 Sofort-Prüfung starten' },
              
              // API-Daten
              { name: 'device_charge', value: 0, desc: 'Speicher lädt [W]' },
              { name: 'device_discharge', value: 0, desc: 'Speicher entlädt [W]' },
              { name: 'device_soc', value: 0, desc: 'Speicher SOC [%]' },
              { name: 'api_delay', value: 0, desc: '📡 API Verzögerung [s]' },
              { name: 'last_api_fetch', value: 0, desc: '🕐 Letzte API-Abfrage' },
              { name: 'next_api_fetch', value: 0, desc: '⏰ Nächste API-Abfrage' },
              
              // Berechnete Werte
              { name: 'net_result', value: 0, desc: 'Resultat am Netz [W]' },
              { name: 'regulation_quality', value: 0, desc: '📈 Regelgüte [%]' },  // v2.0: Neu
              { name: 'current_efficiency', value: 0, desc: '📈 Aktuelle Effizienz [%]' },
              
              // Override & Debug
              { name: 'override_mode', value: '', desc: '🔧 Override-Modus (test/zero/normal)' },
              { name: 'manual_mode_override', value: '', desc: '🎮 Modus-Override (ct/manual/auto)' },
              { name: 'debug_mode', value: false, desc: '🔍 Debug-Modus aktiv' },
              
              // Diagnose
              { name: 'status', value: '', desc: 'Aktueller Status' },
              { name: 'timeline', value: '', desc: 'Timeline' },
              { name: 'alarms', value: '', desc: '🚨 Aktive Alarme' },
              
              // Statistiken (Überblick)
              { name: 'daily_summary', value: '', desc: '📊 Tages-Zusammenfassung' },
              { name: 'system_version', value: 'v2.0', desc: '🔧 System-Version' }  // v2.0: Neu
          ];
          
          datapoints.forEach(dp => {
              try {
                  const base = dp.shared ? MONITOR_CONFIG.SHARED_PREFIX : prefix;
                  createState(base + dp.name, dp.value, {
                      name: dp.desc,
                      type: typeof dp.value,
                      read: true,
                      write: true,
                      desc: `MARSTEK Monitor v2.0 - ${dp.desc}`
                  });
              } catch (error) {
                  // State existiert bereits
              }
          });
      }
      
      // ===================================================
      // KOMMUNIKATION MIT ANDEREN SCRIPTS
      // ===================================================
      
      function readFromOtherScripts() {
          try {
              const sharedPrefix = MONITOR_CONFIG.SHARED_PREFIX;
              const intelPrefix = MONITOR_CONFIG.INTEL_PREFIX;
              
              // Von Core
              monitorState.coreActive = getState(sharedPrefix + 'core_active').val || false;
              monitorState.tibberCurrent = getState(sharedPrefix + 'tibber_current').val || 0;
              
              // v2.0: Neuer Name für gemeldeten Wert
              monitorState.reportedPower = getState(sharedPrefix + 'target_power').val || 0;
              
              // Von Intelligence v2.0
              try {
                  monitorState.intelActive = getState(intelPrefix + 'intel_active').val || false;
                  monitorState.limitingActive = getState(intelPrefix + 'limiting_active').val || false;
              } catch (error) {
                  // Intelligence möglicherweise nicht gestartet
                  monitorState.intelActive = false;
              }
              
              return true;
          } catch (error) {
              console.error('❌ Monitor: Fehler beim Lesen von anderen Scripts:', error.message);
              return false;
          }
      }
      
      // ===================================================
      // ROBUSTER STATE-SETTER
      // ===================================================
      
      function safeSetState(id, value) {
          if (existsState(id)) {
              try {
                  setState(id, value);
              } catch (e) {
                  console.warn(`⚠️ Monitor safeSetState: Fehler beim Setzen von ${id}: ${e.message}`);
              }
          } else {
              console.warn(`⚠️ Monitor safeSetState: Datenpunkt ${id} existiert nicht.`);
          }
      }
      
      // ===================================================
      // KOMMUNIKATION MIT ANDEREN SCRIPTS (ROBUST)
      // ===================================================
      
      function sendToOtherScripts() {
          try {
              const sharedPrefix = MONITOR_CONFIG.SHARED_PREFIX;
              const monitorPrefix = MONITOR_CONFIG.MONITOR_PREFIX;
      
              // Override-Modus an alle
              safeSetState(sharedPrefix + 'override_mode', monitorState.overrideMode);
      
              // System-Status
              safeSetState(sharedPrefix + 'system_status', monitorState.systemStatus);
      
              // Steuerungsmodus-Info
              safeSetState(monitorPrefix + 'control_mode_detected', monitorState.controlMode);
              safeSetState(monitorPrefix + 'control_mode_confidence', monitorState.controlModeConfidence);
              safeSetState(monitorPrefix + 'manual_mode_active', monitorState.controlMode === 'manual');
              safeSetState(monitorPrefix + 'regulation_possible', monitorState.controlMode === 'ct');
      
              return true;
      
          } catch (error) {
              console.error('❌ Monitor: Fehler beim Senden an andere Scripts:', error.message);
              return false;
          }
      }
      
      // ===================================================
      // API-INTEGRATION (MARSTEK CLOUD)
      // ===================================================
      
      function fetchDeviceData() {
          if (!MONITOR_CONFIG.API_ENABLED) return;
          
          try {
              const prefix = MONITOR_CONFIG.MONITOR_PREFIX;
              const mailbox = getState(prefix + 'mailbox').val;
              const token = getState(prefix + 'token').val;
              const deviceId = getState(prefix + 'deviceId').val;
              
              if (!mailbox || !token || !deviceId) {
                  console.error("❌ Monitor: API-Zugangsdaten fehlen!");
                  return;
              }
              
              if (MONITOR_CONFIG.LOG_API) {
                  console.log("🔄 Monitor v2.0: API-Abfrage für Speicher " + deviceId);
              }
              
              const url = `https://eu.hamedata.com/ems/api/v1/getDeviceList?mailbox=${encodeURIComponent(mailbox)}&token=${token}`;
              
              httpGet(url, function(error, response) {
                  if (error) {
                      console.error("❌ Monitor: HTTP Fehler:", error);
                      return;
                  }
                  
                  try {
                      const result = JSON.parse(response.data);
                      
                      if (!result || result.code !== 1) {
                          console.log("❌ Monitor: API Error:", result?.msg || "Unknown");
                          return;
                      }
                      
                      const devices = result.data;
                      const targetDevice = devices.find(d => d.mac && d.mac.endsWith(deviceId));
                      
                      if (targetDevice) {
                          // Standard-Daten speichern
                          monitorState.deviceSoc = parseFloat(targetDevice.soc) || 0;
                          monitorState.deviceCharge = parseFloat(targetDevice.charge) || 0;
                          monitorState.deviceDischarge = parseFloat(targetDevice.discharge) || 0;
                          monitorState.lastReportTime = targetDevice.report_time;
                          
                          const prefix = MONITOR_CONFIG.MONITOR_PREFIX;
                          setState(prefix + 'device_charge', monitorState.deviceCharge);
                          setState(prefix + 'device_discharge', monitorState.deviceDischarge);
                          setState(prefix + 'device_soc', monitorState.deviceSoc);
                          setState(prefix + 'api_connected', true);
                          
                          // Steuerungsmodus-Erkennung
                          detectControlModeFromAPI(targetDevice);
                          
                          if (MONITOR_CONFIG.LOG_API) {
                              console.log(`✅ Monitor v2.0: API OK - SOC: ${monitorState.deviceSoc}%, Charge: ${monitorState.deviceCharge}W, Discharge: ${monitorState.deviceDischarge}W`);
                          }
                      }
                      
                  } catch (e) {
                      console.error("❌ Monitor: Parse Fehler:", e.message);
                  }
              });
              
          } catch (error) {
              console.error('❌ Monitor: API-Fetch Fehler:', error.message);
          }
      }
      
      function fetchDeviceDataSmart() {
          if (!MONITOR_CONFIG.API_ENABLED) return;
          
          const now = Date.now();
          
          if (now < monitorState.apiState.nextFetchTime) {
              return;
          }
          
          const currentPower = Math.abs(monitorState.tibberCurrent);
          const isActive = currentPower > 100;
          
          if (isActive) {
              monitorState.apiState.lastActivity = now;
          }
          
          // Modus-abhängige Intervalle
          let interval;
          if (monitorState.controlMode === 'manual' && monitorState.controlModeConfidence !== 'niedrig') {
              // MANUELLER MODUS: Häufiger prüfen (auf Umstellung warten)
              interval = 60000 + Math.random() * 30000; // 1-1.5 Minuten
          } else if (isActive) {
              // AKTIV: Normale Intervalle
              interval = 240000 + Math.random() * 120000; // 4-6 Minuten
          } else {
              // INAKTIV: Längere Intervalle
              interval = 600000 + Math.random() * 300000; // 10-15 Minuten
          }
          
          monitorState.apiState.nextFetchTime = now + interval;
          monitorState.apiState.lastFetch = now;
          
          // Timing-Info speichern
          const prefix = MONITOR_CONFIG.MONITOR_PREFIX;
          setState(prefix + 'last_api_fetch', monitorState.apiState.lastFetch);
          setState(prefix + 'next_api_fetch', monitorState.apiState.nextFetchTime);
          
          fetchDeviceData();
      }
      
      // ===================================================
      // STEUERUNGSMODUS-ERKENNUNG (UNVERÄNDERT)
      // ===================================================
      
      function detectControlModeFromAPI(deviceData) {
          if (!MONITOR_CONFIG.MODE_DETECTION_ENABLED || !deviceData) return 'unbekannt';
          
          let detectedMode = 'unbekannt';
          let confidence = 'niedrig';
          let reasons = [];
          
          // API-Feld-Analyse
          if (deviceData.status !== undefined) {
              if (deviceData.status === 0) {
                  detectedMode = 'ct';
                  confidence = 'mittel';
                  reasons.push('status=0 → CT-Modus vermutet');
              } else if (deviceData.status === 1) {
                  reasons.push('status=1 → Gerät online, Modus unklar');
              }
          }
          
          // Weitere Modus-Felder suchen
          const modeFields = ['mode', 'control_mode', 'work_mode', 'operation_mode', 'ct_mode', 'manual_mode'];
          for (const field of modeFields) {
              if (deviceData[field] !== undefined) {
                  const value = String(deviceData[field]).toLowerCase();
                  if (value.includes('manual')) {
                      detectedMode = 'manual';
                      confidence = 'hoch';
                      reasons.push(`${field}="${value}" → Manueller Modus`);
                      break;
                  } else if (value.includes('ct') || value.includes('auto') || value.includes('grid')) {
                      detectedMode = 'ct';
                      confidence = 'hoch';
                      reasons.push(`${field}="${value}" → CT-Modus`);
                      break;
                  }
              }
          }
          
          // API-Daten speichern
          monitorState.apiModeData = {
              access: deviceData.access,
              status: deviceData.status,
              detectedMode: detectedMode,
              confidence: confidence,
              reasons: reasons,
              lastUpdate: Date.now(),
              rawData: JSON.stringify(deviceData, null, 2)
          };
          
          return { mode: detectedMode, confidence: confidence, reasons: reasons };
      }
      
      function updateControlModeDetection() {
          if (!MONITOR_CONFIG.MODE_DETECTION_ENABLED) return;
          
          const now = Date.now();
          
          // API-basierte Erkennung
          let apiResult = { mode: 'unbekannt', confidence: 'niedrig', reasons: [] };
          if (monitorState.apiModeData && monitorState.apiModeData.lastUpdate && 
              (now - monitorState.apiModeData.lastUpdate) < 600000) {
              apiResult = {
                  mode: monitorState.apiModeData.detectedMode || 'unbekannt',
                  confidence: monitorState.apiModeData.confidence || 'niedrig',
                  reasons: monitorState.apiModeData.reasons || []
              };
          }
          
          // Intelligente Kombination
          let finalMode = 'unbekannt';
          let finalConfidence = 'niedrig';
          let detectionMethod = 'keine';
          
          if (apiResult.confidence === 'hoch') {
              finalMode = apiResult.mode;
              finalConfidence = 'hoch';
              detectionMethod = 'api';
          } else if (apiResult.confidence === 'mittel') {
              finalMode = apiResult.mode;
              finalConfidence = 'mittel';
              detectionMethod = 'api';
          } else {
              finalMode = 'unbekannt';
              finalConfidence = 'niedrig';
              detectionMethod = 'unclear';
          }
          
          // Update State
          const previousMode = monitorState.controlMode;
          monitorState.controlMode = finalMode;
          monitorState.controlModeConfidence = finalConfidence;
          monitorState.lastModeDetection = now;
          
          // History
          monitorState.modeDetectionHistory.push({
              time: now,
              mode: finalMode,
              confidence: finalConfidence,
              method: detectionMethod
          });
          if (monitorState.modeDetectionHistory.length > 20) {
              monitorState.modeDetectionHistory.shift();
          }
          
          // Logging bei Änderungen
          if (previousMode !== finalMode && MONITOR_CONFIG.LOG_MODE_DETECTION) {
              console.log(`🎮 Monitor v2.0: MODUS-ÄNDERUNG! ${previousMode.toUpperCase()} → ${finalMode.toUpperCase()}`);
              console.log(`   Sicherheit: ${finalConfidence}, Methode: ${detectionMethod}`);
              
              if (finalMode === 'manual' && finalConfidence !== 'niedrig') {
                  console.log(`⚠️ Monitor: KRITISCH - Wechsel zu MANUELLER STEUERUNG!`);
                  console.log(`   ❌ CT-Signale werden ignoriert`);
                  console.log(`   🔧 Scripts v2.0 können nicht mehr regeln`);
                  console.log(`   💡 LÖSUNG: In MARSTEK App auf CT-Modus umstellen`);
                  
                  // Alarm hinzufügen
                  monitorState.alarms.push({
                      time: now,
                      type: 'critical',
                      message: 'Manueller Modus erkannt - CT-Regelung deaktiviert'
                  });
                  
              } else if (finalMode === 'ct' && finalConfidence !== 'niedrig') {
                  console.log(`✅ Monitor: PERFEKT - Wechsel zu CT-STEUERUNG!`);
                  console.log(`   ✅ Scripts v2.0 können wieder regeln`);
                  console.log(`   🎯 Automatische 0W-Regelung aktiviert`);
                  
                  // Alarm entfernen wenn vorhanden
                  monitorState.alarms = monitorState.alarms.filter(a => a.type !== 'critical');
              }
          }
          
          // An andere Scripts senden
          sendToOtherScripts();
      }
      
      // ===================================================
      // v2.0: REGELGÜTE-BERECHNUNG
      // ===================================================
      
      function calculateRegulationQuality() {
          try {
              const netResult = Math.abs(monitorState.tibberCurrent);
              
              // Regelgüte in % (100% = perfekte 0W-Regelung)
              let quality = 100;
              
              if (netResult <= 20) {
                  quality = 100; // Exzellent
              } else if (netResult <= 50) {
                  quality = 95;  // Sehr gut
              } else if (netResult <= 100) {
                  quality = 85;  // Gut
              } else if (netResult <= 200) {
                  quality = 70;  // Befriedigend
              } else {
                  quality = Math.max(0, 50 - ((netResult - 200) / 20)); // Schlechter werdend
              }
              
              // Bei manueller Steuerung = 0%
              if (monitorState.controlMode === 'manual') {
                  quality = 0;
              }
              
              safeSetState(MONITOR_CONFIG.MONITOR_PREFIX + 'regulation_quality', Math.round(quality));
              
              return quality;
              
          } catch (error) {
              console.error('❌ Monitor: Regelgüte-Berechnung Fehler:', error.message);
              return 0;
          }
      }
      
      // ===================================================
      // DIAGNOSE & STATUS-UPDATES (v2.0)
      // ===================================================
      
      function updateDiagnoseAndStatus() {
          try {
              const prefix = MONITOR_CONFIG.MONITOR_PREFIX;
              
              // Netz-Resultat berechnen
              const netResult = monitorState.tibberCurrent - monitorState.deviceDischarge + monitorState.deviceCharge;
              setState(prefix + 'net_result', Math.round(netResult));
              
              // v2.0: Regelgüte berechnen
              const quality = calculateRegulationQuality();
              
              // Status-Text erstellen
              let status = "";
              const absNetResult = Math.abs(netResult);
              const modeInfo = monitorState.controlMode !== 'unbekannt' ? ` (${monitorState.controlMode.toUpperCase()}-Modus)` : '';
              
              if (monitorState.controlMode === 'manual' && monitorState.controlModeConfidence !== 'niedrig') {
                  status = `⚠️ MANUELLER MODUS AKTIV - CT-Regelung DEAKTIVIERT! In App auf CT-Modus umstellen.`;
              } else if (!monitorState.coreActive) {
                  status = `❌ CORE-ENGINE v2.0 NICHT AKTIV - CT-Simulation gestoppt!`;
              } else if (!monitorState.intelActive) {
                  status = `⚠️ Intelligence v2.0 nicht aktiv - nur Basis-CT`;
              } else if (monitorState.limitingActive) {
                  status = `🔒 Begrenzung aktiv${modeInfo} - System v2.0 funktioniert optimal`;
              } else if (absNetResult <= 20) {
                  status = `🏆 EXZELLENTE 0W-REGELUNG v2.0!${modeInfo} (${Math.round(quality)}% Güte)`;
              } else if (absNetResult <= 50) {
                  status = `✅ Gut geregelt v2.0${modeInfo} (${Math.round(quality)}% Güte)`;
              } else {
                  status = `🎯 Normale Regelung v2.0${modeInfo} - ${Math.round(netResult)}W Netz-Resultat (${Math.round(quality)}% Güte)`;
              }
              setState(prefix + 'status', status);
              
              // Timeline erstellen (v2.0)
              const now = new Date().toLocaleTimeString();
              const coreStatus = monitorState.coreActive ? '✅' : '❌';
              const intelStatus = monitorState.intelActive ? '✅' : '⚠️';
              const apiStatus = (Date.now() - monitorState.apiState.lastFetch) < 600000 ? '✅' : '⚠️';
              
              const timeline = [
                  `[${now}] 📊 MARSTEK MONITOR v2.0`,
                  `Core: ${coreStatus} | Intelligence: ${intelStatus} | API: ${apiStatus}`,
                  `Modus: ${monitorState.controlMode.toUpperCase()} (${monitorState.controlModeConfidence})`,
                  `SOC: ${monitorState.deviceSoc}% | Netz: ${Math.round(netResult)}W | Güte: ${Math.round(quality)}%`,
                  `Tibber: ${Math.round(monitorState.tibberCurrent)}W → Gemeldet: ${Math.round(monitorState.reportedPower)}W`,
                  `System: ${monitorState.systemStatus} v2.0`
              ].join(' | ');
              setState(prefix + 'timeline', timeline);
              
              // API-Timing
              if (monitorState.lastReportTime) {
                  const apiDelay = Math.round(Date.now()/1000 - monitorState.lastReportTime);
                  setState(prefix + 'api_delay', apiDelay);
              }
              
              // Alarme
              const alarmText = monitorState.alarms.map(a => `[${new Date(a.time).toLocaleTimeString()}] ${a.message}`).join(' | ');
              setState(prefix + 'alarms', alarmText);
              
              // v2.0: Erweiterte Tages-Zusammenfassung
              let dailySummary = `SOC: ${monitorState.deviceSoc}% | Netz: ${Math.round(netResult)}W | Modus: ${monitorState.controlMode} | Güte: ${Math.round(quality)}%`;
              if (monitorState.intelActive) {
                  // Daten von Intelligence v2.0 holen
                  try {
                      const dailyNetPurchase = getState(MONITOR_CONFIG.INTEL_PREFIX + 'daily_net_purchase_kwh').val || 0;
                      const zeroWattPercent = getState(MONITOR_CONFIG.INTEL_PREFIX + 'zero_watt_time_percent').val || 0;
                      dailySummary += ` | Bezug: ${dailyNetPurchase.toFixed(2)}kWh | 0W: ${zeroWattPercent.toFixed(1)}%`;
                  } catch (error) {
                      // Intelligence-Daten nicht verfügbar
                  }
              }
              setState(prefix + 'daily_summary', dailySummary);
              
          } catch (error) {
              console.error('❌ Monitor: Diagnose-Update Fehler:', error.message);
          }
      }
      
      // ===================================================
      // HAUPT-MONITOR-LOOP (v2.0)
      // ===================================================
      
      function runMonitorLoop() {
          try {
              // 1. Von anderen Scripts lesen
              readFromOtherScripts();
              
              // 2. Steuerungsmodus-Erkennung aktualisieren
              const shouldUpdateMode = !monitorState.lastModeDetection || 
                  (Date.now() - monitorState.lastModeDetection) > 60000 ||
                  (monitorState.controlMode === 'manual' && (Date.now() - monitorState.lastModeDetection) > 30000);
              
              if (shouldUpdateMode) {
                  updateControlModeDetection();
              }
              
              // 3. System-Status bewerten (v2.0)
              if (monitorState.coreActive && monitorState.intelActive) {
                  monitorState.systemStatus = 'optimal-v2.0';
              } else if (monitorState.coreActive) {
                  monitorState.systemStatus = 'basic-ct-v2.0';
              } else {
                  monitorState.systemStatus = 'critical';
              }
              
              // 4. Diagnose & Status aktualisieren
              updateDiagnoseAndStatus();
              
              // 5. An andere Scripts senden
              sendToOtherScripts();
              
              monitorState.lastUpdate = Date.now();
              
          } catch (error) {
              console.error('❌ Monitor: Loop-Fehler:', error.message);
              monitorState.systemStatus = 'error';
          }
      }
      
      // ===================================================
      // OVERRIDE & DEBUG FUNKTIONEN (UNVERÄNDERT)
      // ===================================================
      
      function handleOverrides() {
          try {
              const prefix = MONITOR_CONFIG.MONITOR_PREFIX;
              
              // Override-Modus prüfen
              const overrideMode = getState(prefix + 'override_mode').val || '';
              if (overrideMode !== monitorState.overrideMode) {
                  monitorState.overrideMode = overrideMode;
                  if (MONITOR_CONFIG.LOG_MONITOR) {
                      console.log(`🔧 Monitor v2.0: Override-Modus geändert: "${overrideMode}"`);
                  }
              }
              
              // Manueller Modus-Override
              const manualModeOverride = getState(prefix + 'manual_mode_override').val || '';
              if (manualModeOverride !== monitorState.manualModeOverride) {
                  monitorState.manualModeOverride = manualModeOverride;
                  if (manualModeOverride === 'ct') {
                      monitorState.controlMode = 'ct';
                      monitorState.controlModeConfidence = 'hoch';
                      console.log(`🔧 Monitor v2.0: Manueller Override - Modus auf CT gesetzt`);
                  } else if (manualModeOverride === 'manual') {
                      monitorState.controlMode = 'manual';
                      monitorState.controlModeConfidence = 'hoch';
                      console.log(`🔧 Monitor v2.0: Manueller Override - Modus auf MANUAL gesetzt`);
                  }
              }
              
              // Sofort-Prüfung
              const checkModeNow = getState(prefix + 'check_mode_now').val;
              if (checkModeNow === true) {
                  console.log('🔄 Monitor v2.0: Sofort-Prüfung gestartet...');
                  fetchDeviceData(); // Sofortige API-Abfrage
                  setState(prefix + 'check_mode_now', false); // Reset
              }
              
          } catch (error) {
              console.error('❌ Monitor: Override-Handling Fehler:', error.message);
          }
      }
      
      // ===================================================
      // MONITOR START (v2.0)
      // ===================================================
      
      function startMonitor() {
          console.log('📊 MARSTEK-Monitor-Control v2.0 - ÜBERWACHUNG & STEUERUNG\n');
          console.log('🎯 ZWECK: ÜBERWACHUNG, DIAGNOSE & STEUERUNG');
          console.log('✅ Steuerungsmodus-Erkennung (CT vs Manuell)');
          console.log('✅ API-Integration + Smart-Timing');
          console.log('✅ Diagnose-System + Datenpunkte-Verwaltung');
          console.log('✅ Debug-Modi + Override-Funktionen');
          console.log('✅ SOC-Intelligence (von API für Intelligence v2.0)');
          console.log('✅ v2.0: Regelgüte-Monitoring für korrekte Logik');
          console.log('');
          console.log('🔗 KOMMUNIKATION:');
          console.log('   📥 Überwacht: Core v2.0 + Intelligence v2.0 Status');
          console.log('   📡 API: MARSTEK Cloud-Integration');
          console.log('   🎮 Steuert: Override-Modi und Modus-Erkennung');
          console.log('   📈 Überwacht: Regelgüte der neuen 0W-Regelung');
          console.log('   ⚠️ Darf crashen ohne CT/Intelligence zu stoppen!');
          console.log('=====================================');
          
          // Datenpunkte erstellen
          createMonitorDatapoints();
              
          // Status setzen
          safeSetState(MONITOR_CONFIG.MONITOR_PREFIX + 'monitor_active', true);
          safeSetState(MONITOR_CONFIG.MONITOR_PREFIX + 'system_status', 'starting');
          safeSetState(MONITOR_CONFIG.SHARED_PREFIX + 'system_status', 'starting-v2.0');
          safeSetState(MONITOR_CONFIG.MONITOR_PREFIX + 'system_version', 'v2.0');
      
          
          // Loops starten
          setInterval(runMonitorLoop, MONITOR_CONFIG.UPDATE_INTERVAL);         // 10s Monitor-Loop
          setInterval(fetchDeviceDataSmart, 30000);                           // 30s API-Check
          setInterval(handleOverrides, 5000);                                 // 5s Override-Check
          
          // Sofort starten
          setTimeout(runMonitorLoop, 3000);
          setTimeout(fetchDeviceData, 5000);
          
          console.log('\n📊 MONITOR v2.0 AKTIV!');
          console.log('💡 Überwacht Core v2.0 + Intelligence v2.0');
          console.log('🎮 Modus-Erkennung: ' + (MONITOR_CONFIG.MODE_DETECTION_ENABLED ? 'Aktiv' : 'Deaktiviert'));
          console.log('📡 API-Integration: ' + (MONITOR_CONFIG.API_ENABLED ? 'Aktiv' : 'Deaktiviert'));
          console.log('🔋 SOC-Intelligence: Übertragen an Intelligence-Script v2.0');
          console.log('📈 Regelgüte-Monitoring: Aktiv');
          console.log('=====================================\n');
      }
      
      function stopMonitor() {
          try {
              safeSetState(MONITOR_CONFIG.MONITOR_PREFIX + 'monitor_active', false);
              safeSetState(MONITOR_CONFIG.MONITOR_PREFIX + 'system_status', 'stopped');
              console.log('🛑 MARSTEK-Monitor v2.0 beendet');
          } catch (error) {
              console.error('❌ Monitor Stop-Fehler:', error.message);
          }
      }
      
      // ===================================================
      // SCRIPT START
      // ===================================================
      
      startMonitor();
      
      // Event-Handler für Sofort-Prüfung
      on({id: MONITOR_CONFIG.MONITOR_PREFIX + 'check_mode_now', change: 'ne'}, function (obj) {
          if (obj.state.val === true) {
              fetchDeviceData();
              setTimeout(() => {
                  setState(MONITOR_CONFIG.MONITOR_PREFIX + 'check_mode_now', false);
              }, 1000);
          }
      });
      
      onStop(() => {
          stopMonitor();
      });
      
      // ===================================================
      // KONFIGURATIONSHINWEISE FÜR FORUM-NUTZER:
      // ===================================================
      /*
      WICHTIGE ANPASSUNGEN NÖTIG:
      
      1. PREFIXE ANPASSEN:
         Zeile 20: SHARED_PREFIX: '0_userdata.0.marstek_V-E.shared.'
         Zeile 21: INTEL_PREFIX: '0_userdata.0.marstek_V-E.intel.'
         Zeile 22: MONITOR_PREFIX: '0_userdata.0.marstek_V-E.monitor.'
         → Passe die Prefixe an deine ioBroker-Struktur an
      
      2. API-ZUGANGSDATEN AUSFÜLLEN:
         Nach dem Script-Start in ioBroker:
         - monitor.mailbox: Deine E-Mail-Adresse für MARSTEK-App
         - monitor.token: API-Token aus der MARSTEK-App
         - monitor.deviceId: Deine MARSTEK Speicher-ID (z.B. 3456, 4567, etc.)
         
      3. MARSTEK API-TOKEN ERHALTEN:
         - MARSTEK-App öffnen
         - Einstellungen → API oder Entwickler-Bereich
         - Token generieren und kopieren
         
      4. DEVICE-ID FINDEN:
         - In der MARSTEK-App: Geräteinfo
         - Oder MAC-Adresse → letzte 4 Stellen (z.B. ABCD3456 → 4567)
         
      5. ZWEITER SPEICHER (OPTIONAL):
         - enable_second_storage auf true setzen falls vorhanden
         - Zweite Device-ID entsprechend anpassen
      
      FEATURES DEAKTIVIEREN (OPTIONAL):
      - API_ENABLED: false → Deaktiviert Cloud-Integration
      - MODE_DETECTION_ENABLED: false → Deaktiviert Modus-Erkennung
      - Logging reduzieren: LOG_MONITOR/LOG_API: false setzen
      
      HINWEIS: 
      Das Monitor-Script ist optional! Core + Intelligence funktionieren auch ohne Monitor.
      Monitor bietet aber wertvolle Diagnose- und Überwachungsfunktionen.
      */
      

      Grundsätzlich läuft es mir, bin aber mit der Regelung noch nicht zufrieden.

      Edit:
      Ne, die Regelung ist noch nicht richtig, es Oszilliert zu viel hin und her bei der Entladung.

      posted in ioBroker Allgemein
      G
      Gismoh
    • RE: Shelly 3PM Pro Emulator

      @gismoh
      2-MARSTEK-Intelligence:

      // ===================================================
      // MARSTEK-Intelligence v2.0 - KORREKTE REGELUNGSLOGIK
      // ===================================================
      // 
      // 🧠 ZWECK: INTELLIGENTE 0W-REGELUNG (KORREKT IMPLEMENTIERT!)
      // ✅ GRUNDLEGENDE ÄNDERUNG: Sendet gemessenen Netzbezug statt Sollwerte
      // ✅ SOC-Intelligence (Werte-Manipulation statt falsche Berechnungen)
      // ✅ Intelligente Begrenzung (min/max des gemessenen Werts)
      // ✅ Anti-Oszillations-Glättung (des Tibber-Werts)
      // ✅ Tasmota Reality-Check für Effizienz-Monitoring
      // ✅ 0W-Metriken & Tagesstatistiken
      // 
      // 🔧 KERNPRINZIP: MARSTEK regelt selbst auf 0W - wir senden nur IST-Werte!
      // 
      // ===================================================
      
      // KONFIGURATION
      const INTEL_CONFIG = {
          // Hardware für Reality-Check - BITTE ANPASSEN!
          tasmotaXXXX: 'sonoff.0.TasmotaXXX-MSTXXXX.ENERGY_Power',  // ← Deine Tasmota-Geräte hier eintragen
          tasmotayyyy: 'sonoff.0.TasmotaXXX-MSTXXXX.ENERGY_Power',  // ← Optional: zweites Gerät
          
          // Intelligente Begrenzung (des gemessenen Werts)
          POWER_LIMIT: 750,              // Max Netzbezug/Einspeisung die gemeldet wird
          HYSTERESIS_ENABLED: true,      
          HYSTERESIS_OFFSET: 75,         // 750W→825W Grenze, 675W zurück
          
          // ⭐ ANTI-OSZILLATIONS-GLÄTTUNG (des Tibber-Werts) ⭐
          SMOOTHING_ENABLED: true,       // Glättung aktivieren
          SMOOTH_FACTOR: 0.3,            // Glättungsfaktor für Tibber-Wert
          THROTTLE_INTERVAL: 4000,       // 4s Sendepause (MARSTEK-Rhythmus)
          DEAD_ZONE_WATTS: 50,           // Kleine Änderungen ignorieren
          MAX_CHANGE_PER_CYCLE: 200,     // Max Änderung pro Zyklus
          
          // ⭐ SOC-INTELLIGENCE (Werte-Manipulation) ⭐
          SOC_PROTECTION_ENABLED: true,  // SOC-Schutz aktiv
          SOC_HIGH_THRESHOLD: 95,        // Ab 95% SOC Ladung verhindern
          SOC_LOW_THRESHOLD: 10,         // Ab 10% SOC Entladung reduzieren
          
          // Features
          CLOUD_SMOOTHING: true,         // Wolken-Glättung
          TASMOTA_REALITY_CHECK: true,   // Effizienz-Monitoring
          ZERO_WATT_FOCUS: true,         // 0W-Metriken
          
          // Timing
          UPDATE_INTERVAL: 3000,
          
          // Kommunikation - BITTE ANPASSEN!
          SHARED_PREFIX: '0_userdata.0.marstek_V-E.shared.',        // ← Dein Shared-Prefix hier eintragen
          INTEL_PREFIX: '0_userdata.0.marstek_V-E.intel.',          // ← Dein Intelligence-Prefix hier eintragen
          
          // Logging
          LOG_INTELLIGENCE: true,
          LOG_SOC_ACTIONS: true,
          LOG_SMOOTHING: false           // Reduziertes Logging
      };
      
      // INTELLIGENCE STATE
      let intelState = {
          // Von Core empfangen
          tibberCurrent: 0,
          tibberRaw: 0,
          shellyL1: 0,
          shellyL2: 0,
          shellyL3: 0,
          coreActive: false,
          
          // ⭐ NEUE LOGIK: Gemessener Wert (geglättet/manipuliert) ⭐
          reportedPower: 0,              // Was an MARSTEK gesendet wird
          lastReportedPower: 0,          // Letzter gesendeter Wert
          limitingActive: false,         // Begrenzung aktiv
          
          // Glättung
          smoothingHistory: [],          // Historie für Glättung
          smoothedTibber: 0,            // Geglätteter Tibber-Wert
          lastSendTime: 0,              // Letztes Senden (Throttling)
          
          // SOC-Intelligence
          deviceSoc: 0,                 // SOC vom Monitor
          socActionActive: false,       // SOC-Aktion aktiv
          lastSocReason: "",           // Grund der SOC-Aktion
          
          // Wolken-Erkennung
          cloudModeActive: false,
          pvHistory: [],
          lastCloudLog: 0,
          
          // Tasmota Reality-Check (nur Monitoring)
          tasmotaXXXXPower: 0,
          tasmotayyyyPower: 0,
          storageEfficiency: 0,
          lastEfficiencyLog: 0,
          
          // Metriken
          zeroWattTime: 0,
          dailyNetPurchase: 0,
          dailyAdjustments: 0,
          lastMetricReset: Date.now(),
          bestDailyResult: 999,
          lastMetricUpdate: 0,
          limitingActivations: 0,
          totalLimitingTime: 0,
          
          // Status
          intelActive: true,
          lastUpdate: 0
      };
      
      // ===================================================
      // ROBUSTER STATE-SETTER
      // ===================================================
      
      function safeSetState(id, value) {
          if (existsState(id)) {
              try {
                  setState(id, value);
              } catch (e) {
                  console.warn(`⚠️ Intelligence safeSetState: Fehler beim Setzen von ${id}: ${e.message}`);
              }
          }
      }
      
      // ===================================================
      // INTELLIGENCE DATENPUNKTE
      // ===================================================
      
      function createIntelligenceDatapoints() {
          const prefix = INTEL_CONFIG.INTEL_PREFIX;
          
          const datapoints = [
              // Konfiguration
              { name: 'power_limit', value: INTEL_CONFIG.POWER_LIMIT, desc: '⚡ Leistungsgrenze [W]' },
              { name: 'smoothing_enabled', value: INTEL_CONFIG.SMOOTHING_ENABLED, desc: '🌊 Glättung aktiv' },
              { name: 'soc_protection_enabled', value: INTEL_CONFIG.SOC_PROTECTION_ENABLED, desc: '🔋 SOC-Schutz aktiv' },
              
              // Status
              { name: 'intel_active', value: true, desc: '🧠 Intelligence läuft' },
              { name: 'limiting_active', value: false, desc: '🔒 Begrenzung aktiv' },
              { name: 'cloud_mode_active', value: false, desc: '☁️ Wolken-Modus aktiv' },
              { name: 'soc_action_active', value: false, desc: '🔋 SOC-Aktion aktiv' },
              
              // Berechnungen
              { name: 'tibber_raw', value: 0, desc: '📊 Roher Tibber-Wert [W]' },
              { name: 'tibber_smoothed', value: 0, desc: '🌊 Geglätteter Tibber [W]' },
              { name: 'reported_power', value: 0, desc: '📤 An MARSTEK gesendeter Wert [W]' },
              { name: 'power_change', value: 0, desc: '📈 Leistungsänderung [W]' },
              
              // SOC-Intelligence
              { name: 'device_soc', value: 0, desc: '🔋 Speicher SOC [%]' },
              { name: 'soc_reason', value: '', desc: '📝 SOC-Aktion Grund' },
              
              // Tasmota Reality-Check (Monitoring)
              { name: 'tasmota_power', value: 0, desc: '📊 Tasmota Messung [W]' },
              { name: 'storage_efficiency', value: 0, desc: '📈 Speicher-Effizienz [%]' },
              { name: 'storage_responds', value: false, desc: '✅ Speicher reagiert' },
              
              // Metriken
              { name: 'zero_watt_time_percent', value: 0, desc: '🎯 0W-Zeit [%]' },
              { name: 'daily_net_purchase_kwh', value: 0, desc: '⚡ Netzbezug [kWh]' },
              { name: 'daily_adjustments', value: 0, desc: '🔄 Regelzyklen' },
              { name: 'limiting_activations_today', value: 0, desc: '🔒 Begrenzungs-Aktivierungen' },
              { name: 'limiting_time_percent', value: 0, desc: '⏱️ Zeit mit Begrenzung [%]' },
              { name: 'best_daily_result', value: 999, desc: '🏆 Bester Tageswert [kWh]' }
          ];
          
          datapoints.forEach(dp => {
              try {
                  createState(prefix + dp.name, dp.value, {
                      name: dp.desc,
                      type: typeof dp.value,
                      read: true,
                      write: true,
                      desc: `MARSTEK Intelligence v2.0 - ${dp.desc}`
                  });
              } catch (error) {
                  // State existiert bereits
              }
          });
      }
      
      // ===================================================
      // KOMMUNIKATION MIT CORE
      // ===================================================
      
      function readFromCore() {
          try {
              const prefix = INTEL_CONFIG.SHARED_PREFIX;
              
              intelState.tibberCurrent = getState(prefix + 'tibber_current').val || 0;
              intelState.tibberRaw = getState(prefix + 'tibber_raw').val || 0;
              intelState.shellyL1 = getState(prefix + 'shelly_l1').val || 0;
              intelState.shellyL2 = getState(prefix + 'shelly_l2').val || 0;
              intelState.shellyL3 = getState(prefix + 'shelly_l3').val || 0;
              intelState.coreActive = getState(prefix + 'core_active').val || false;
              
              // SOC-Werte vom Monitor lesen
              try {
                  const monitorPrefix = '0_userdata.0.marstek_V-E.monitor.';  // ← ANPASSBAR
                  intelState.deviceSoc = getState(monitorPrefix + 'device_soc').val || 0;
                  safeSetState(INTEL_CONFIG.INTEL_PREFIX + 'device_soc', intelState.deviceSoc);
              } catch (error) {
                  intelState.deviceSoc = 0;
              }
              
              return true;
          } catch (error) {
              console.error('❌ Intelligence: Fehler beim Lesen von Core:', error.message);
              return false;
          }
      }
      
      function sendToCore() {
          try {
              const prefix = INTEL_CONFIG.SHARED_PREFIX;
              
              // ⭐ KERNÄNDERUNG: Sende gemessenen/manipulierten Wert (nicht Sollwert!) ⭐
              safeSetState(prefix + 'target_power', intelState.reportedPower);
              safeSetState(prefix + 'correction_factor', 1); // Nicht mehr verwendet
              safeSetState(prefix + 'limiting_active', intelState.limitingActive);
              
              return true;
          } catch (error) {
              console.error('❌ Intelligence: Fehler beim Senden an Core:', error.message);
              return false;
          }
      }
      
      // ===================================================
      // ⭐ TIBBER-WERT GLÄTTUNG (ANTI-OSZILLATION) ⭐
      // ===================================================
      
      function smoothTibberValue(currentValue) {
          if (!INTEL_CONFIG.SMOOTHING_ENABLED) return currentValue;
          
          try {
              const now = Date.now();
              
              // History aktualisieren (letzte 30s)
              intelState.smoothingHistory.push({ time: now, value: currentValue });
              const thirtySecondsAgo = now - 30000;
              intelState.smoothingHistory = intelState.smoothingHistory.filter(h => h.time > thirtySecondsAgo);
              
              if (intelState.smoothingHistory.length < 2) {
                  intelState.smoothedTibber = currentValue;
                  return currentValue;
              }
              
              // Glättung anwenden
              const lastSmoothed = intelState.smoothedTibber || currentValue;
              const smoothed = lastSmoothed * (1 - INTEL_CONFIG.SMOOTH_FACTOR) + currentValue * INTEL_CONFIG.SMOOTH_FACTOR;
              
              intelState.smoothedTibber = smoothed;
              
              // Logging bei größeren Änderungen
              const change = Math.abs(currentValue - smoothed);
              if (INTEL_CONFIG.LOG_SMOOTHING && change > 100) {
                  console.log(`🌊 Intelligence: Tibber-Glättung ${Math.round(currentValue)}W → ${Math.round(smoothed)}W`);
              }
              
              return smoothed;
              
          } catch (error) {
              console.error('❌ Intelligence: Glättung Fehler:', error.message);
              return currentValue;
          }
      }
      
      // ===================================================
      // ⭐ SOC-INTELLIGENCE (WERTE-MANIPULATION) ⭐
      // ===================================================
      
      function applySocProtection(measuredValue) {
          if (!INTEL_CONFIG.SOC_PROTECTION_ENABLED) {
              intelState.socActionActive = false;
              return measuredValue;
          }
          
          try {
              const soc = intelState.deviceSoc;
              let manipulatedValue = measuredValue;
              let socReason = "";
              let socActive = false;
              
              // ⭐ SOC >95%: LADUNG VERHINDERN durch Werte-Manipulation ⭐
              if (soc >= INTEL_CONFIG.SOC_HIGH_THRESHOLD && measuredValue < 0) {
                  // Negative Werte (Einspeisung) → auf 0 setzen = keine Ladung
                  manipulatedValue = 0;
                  socReason = `SOC ${soc}% ≥${INTEL_CONFIG.SOC_HIGH_THRESHOLD}% - Einspeisung unterdrückt (Ladeschutz)`;
                  socActive = true;
                  
              } 
              // ⭐ SOC <10%: ENTLADUNG REDUZIEREN ⭐ 
              else if (soc <= INTEL_CONFIG.SOC_LOW_THRESHOLD && measuredValue > 0) {
                  // Positive Werte (Netzbezug) → reduzieren = weniger Entladung
                  manipulatedValue = measuredValue * 0.5; // 50% reduzieren
                  socReason = `SOC ${soc}% ≤${INTEL_CONFIG.SOC_LOW_THRESHOLD}% - Netzbezug reduziert (Entladeschutz)`;
                  socActive = true;
                  
              } else {
                  socReason = `SOC ${soc}% - normale Regelung`;
                  socActive = false;
              }
              
              // Status aktualisieren
              intelState.socActionActive = socActive;
              
              // Logging bei Zustandsänderung
              if (socReason !== intelState.lastSocReason) {
                  if (INTEL_CONFIG.LOG_SOC_ACTIONS && socActive) {
                      console.log(`🔋 Intelligence: ${socReason} - ${Math.round(measuredValue)}W → ${Math.round(manipulatedValue)}W`);
                  }
                  intelState.lastSocReason = socReason;
              }
              
              // Datenpunkte aktualisieren
              safeSetState(INTEL_CONFIG.INTEL_PREFIX + 'soc_action_active', socActive);
              safeSetState(INTEL_CONFIG.INTEL_PREFIX + 'soc_reason', socReason);
              
              return manipulatedValue;
              
          } catch (error) {
              console.error('❌ Intelligence: SOC-Schutz Fehler:', error.message);
              return measuredValue;
          }
      }
      
      // ===================================================
      // INTELLIGENTE BEGRENZUNG (des gemessenen Werts)
      // ===================================================
      
      function applyIntelligentLimiting(measuredValue) {
          try {
              const limit = INTEL_CONFIG.POWER_LIMIT;
              const hysteresis = INTEL_CONFIG.HYSTERESIS_OFFSET;
              const now = Date.now();
              
              // Begrenzung prüfen
              let needsLimiting = false;
              
              if (INTEL_CONFIG.HYSTERESIS_ENABLED) {
                  if (!intelState.limitingActive) {
                      // Begrenzung einschalten: bei 750W
                      needsLimiting = Math.abs(measuredValue) > limit;
                  } else {
                      // Begrenzung ausschalten: erst bei 675W (75W Hysterese)
                      needsLimiting = Math.abs(measuredValue) >= (limit - hysteresis);
                  }
              } else {
                  needsLimiting = Math.abs(measuredValue) > limit;
              }
              
              // Status aktualisieren
              if (needsLimiting && !intelState.limitingActive) {
                  if (INTEL_CONFIG.LOG_INTELLIGENCE) {
                      console.log(`🔒 Intelligence: Begrenzung aktiviert bei ${Math.round(measuredValue)}W (Limit: ${limit}W)`);
                  }
                  intelState.limitingActive = true;
                  intelState.limitingActivations++;
              } else if (!needsLimiting && intelState.limitingActive) {
                  if (INTEL_CONFIG.LOG_INTELLIGENCE) {
                      console.log(`🔓 Intelligence: Begrenzung deaktiviert bei ${Math.round(measuredValue)}W`);
                  }
                  intelState.limitingActive = false;
              }
              
              if (intelState.limitingActive) {
                  intelState.totalLimitingTime += INTEL_CONFIG.UPDATE_INTERVAL;
              }
              
              // Wert begrenzen
              let limitedValue = measuredValue;
              if (needsLimiting) {
                  limitedValue = Math.max(-limit, Math.min(limit, measuredValue));
              }
              
              return limitedValue;
              
          } catch (error) {
              console.error('❌ Intelligence: Begrenzung Fehler:', error.message);
              return measuredValue;
          }
      }
      
      // ===================================================
      // THROTTLING (Sendepausen)
      // ===================================================
      
      function shouldThrottle() {
          const now = Date.now();
          const timeSinceLastSend = now - intelState.lastSendTime;
          
          if (timeSinceLastSend < INTEL_CONFIG.THROTTLE_INTERVAL) {
              return true; // Noch in Throttling-Phase
          }
          
          intelState.lastSendTime = now;
          return false;
      }
      
      function applyDeadZoneAndRateLimit(newValue) {
          const deadZone = INTEL_CONFIG.DEAD_ZONE_WATTS;
          const maxChange = INTEL_CONFIG.MAX_CHANGE_PER_CYCLE;
          
          // Dead-Zone prüfen
          const change = Math.abs(newValue - intelState.lastReportedPower);
          if (change < deadZone) {
              return intelState.lastReportedPower; // Keine Änderung
          }
          
          // Rate-Limiting
          const currentChange = newValue - intelState.lastReportedPower;
          if (Math.abs(currentChange) > maxChange) {
              const limitedChange = currentChange > 0 ? maxChange : -maxChange;
              return intelState.lastReportedPower + limitedChange;
          }
          
          return newValue;
      }
      
      // ===================================================
      // WOLKEN-ERKENNUNG (vereinfacht)
      // ===================================================
      
      function detectCloudMode() {
          if (!INTEL_CONFIG.CLOUD_SMOOTHING) return false;
          
          try {
              const now = Date.now();
              intelState.pvHistory.push({ time: now, value: intelState.tibberCurrent });
              
              // Nur letzte 60s behalten
              const sixtySecondsAgo = now - 60000;
              intelState.pvHistory = intelState.pvHistory.filter(h => h.time > sixtySecondsAgo);
              
              if (intelState.pvHistory.length < 6) return false;
              
              const recent = intelState.pvHistory.slice(-6);
              let rapidChanges = 0;
              
              for (let i = 1; i < recent.length; i++) {
                  const diff = Math.abs(recent[i].value - recent[i-1].value);
                  if (diff > 400) rapidChanges++;
              }
              
              const newCloudMode = rapidChanges >= 3;
              
              if (newCloudMode !== intelState.cloudModeActive) {
                  if (INTEL_CONFIG.LOG_INTELLIGENCE) {
                      console.log(`${newCloudMode ? '☁️ WOLKEN-MODUS' : '☀️ STABILER MODUS'} erkannt`);
                  }
                  intelState.cloudModeActive = newCloudMode;
                  safeSetState(INTEL_CONFIG.INTEL_PREFIX + 'cloud_mode_active', newCloudMode);
              }
              
              return newCloudMode;
              
          } catch (error) {
              console.error('❌ Intelligence: Wolken-Erkennung Fehler:', error.message);
              return false;
          }
      }
      
      // ===================================================
      // TASMOTA REALITY-CHECK (nur Monitoring)
      // ===================================================
      
      function readTasmotaValues() {
          if (!INTEL_CONFIG.TASMOTA_REALITY_CHECK) return;
          
          try {
              intelState.tasmotaXXXXPower = getState(INTEL_CONFIG.tasmotaXXXX).val || 0;
              intelState.tasmotayyyyPower = getState(INTEL_CONFIG.tasmotayyyy).val || 0;
              
              safeSetState(INTEL_CONFIG.INTEL_PREFIX + 'tasmota_power', Math.round(intelState.tasmotaXXXXPower));
              
              // Effizienz berechnen (nur Monitoring)
              const expectedPower = Math.abs(intelState.reportedPower);
              const actualPower = Math.abs(intelState.tasmotaXXXXPower);
              
              if (expectedPower > 50) {
                  const efficiency = Math.min(100, (actualPower / expectedPower) * 100);
                  const responds = efficiency > 40;
                  
                  intelState.storageEfficiency = efficiency;
                  
                  safeSetState(INTEL_CONFIG.INTEL_PREFIX + 'storage_efficiency', Math.round(efficiency));
                  safeSetState(INTEL_CONFIG.INTEL_PREFIX + 'storage_responds', responds);
                  
                  // Logging alle 60s
                  const now = Date.now();
                  if ((!intelState.lastEfficiencyLog || now - intelState.lastEfficiencyLog > 60000)) {
                      if (INTEL_CONFIG.LOG_INTELLIGENCE) {
                          const direction = intelState.reportedPower < 0 ? "Laden" : "Entladen";
                          const status = responds ? "✅" : "❌";
                          console.log(`📊 Intelligence: ${direction} Effizienz ${Math.round(expectedPower)}W → ${Math.round(actualPower)}W = ${Math.round(efficiency)}% ${status}`);
                      }
                      intelState.lastEfficiencyLog = now;
                  }
              }
              
          } catch (error) {
              console.error('❌ Intelligence: Tasmota-Lesefehler:', error.message);
          }
      }
      
      // ===================================================
      // 0W-METRIKEN & STATISTIKEN
      // ===================================================
      
      function updateZeroWattMetrics() {
          if (!INTEL_CONFIG.ZERO_WATT_FOCUS) return;
          
          try {
              const now = Date.now();
              const prefix = INTEL_CONFIG.INTEL_PREFIX;
              
              // Tagesreset prüfen
              const today = new Date().toDateString();
              const lastResetDay = new Date(intelState.lastMetricReset).toDateString();
              
              if (today !== lastResetDay) {
                  if (INTEL_CONFIG.LOG_INTELLIGENCE) {
                      console.log(`📊 Intelligence: Tages-Reset - Netzbezug gestern: ${intelState.dailyNetPurchase.toFixed(3)} kWh`);
                  }
                  
                  if (intelState.dailyNetPurchase < intelState.bestDailyResult) {
                      intelState.bestDailyResult = intelState.dailyNetPurchase;
                      safeSetState(prefix + 'best_daily_result', Math.round(intelState.bestDailyResult * 1000) / 1000);
                  }
                  
                  // Reset
                  intelState.dailyNetPurchase = 0;
                  intelState.dailyAdjustments = 0;
                  intelState.zeroWattTime = 0;
                  intelState.limitingActivations = 0;
                  intelState.totalLimitingTime = 0;
                  intelState.lastMetricReset = now;
              }
              
              // 0W-Zeit messen
              if (Math.abs(intelState.tibberCurrent) <= 30) {
                  intelState.zeroWattTime += INTEL_CONFIG.UPDATE_INTERVAL;
              }
              
              // Netzbezug summieren
              if (intelState.tibberCurrent > 0) {
                  const kWhIncrement = (intelState.tibberCurrent * INTEL_CONFIG.UPDATE_INTERVAL) / (1000 * 3600);
                  intelState.dailyNetPurchase += kWhIncrement;
              }
              
              intelState.dailyAdjustments++;
              
              // Datenpunkte aktualisieren (alle 60s)
              if (!intelState.lastMetricUpdate || now - intelState.lastMetricUpdate > 60000) {
                  const runtimeMs = now - intelState.lastMetricReset;
                  const zeroWattPercent = (intelState.zeroWattTime / runtimeMs) * 100;
                  const limitPercent = (intelState.totalLimitingTime / runtimeMs) * 100;
                  
                  safeSetState(prefix + 'zero_watt_time_percent', Math.round(zeroWattPercent * 10) / 10);
                  safeSetState(prefix + 'daily_net_purchase_kwh', Math.round(intelState.dailyNetPurchase * 1000) / 1000);
                  safeSetState(prefix + 'daily_adjustments', intelState.dailyAdjustments);
                  safeSetState(prefix + 'limiting_activations_today', intelState.limitingActivations);
                  safeSetState(prefix + 'limiting_time_percent', Math.round(limitPercent * 10) / 10);
                  
                  intelState.lastMetricUpdate = now;
              }
              
          } catch (error) {
              console.error('❌ Intelligence: Metriken-Update Fehler:', error.message);
          }
      }
      
      // ===================================================
      // ⭐ HAUPT-INTELLIGENCE-LOOP (NEUE LOGIK!) ⭐
      // ===================================================
      
      function runIntelligenceLoop() {
          try {
              // 1. Von Core lesen
              if (!readFromCore()) {
                  return;
              }
              
              if (!intelState.coreActive) {
                  if (INTEL_CONFIG.LOG_INTELLIGENCE) {
                      console.log('⚠️ Intelligence: Core nicht aktiv - warte...');
                  }
                  return;
              }
              
              // 2. Tasmota-Werte lesen (nur Monitoring)
              readTasmotaValues();
              
              // 3. Wolken-Erkennung
              detectCloudMode();
              
              // 4. ⭐ NEUE KERNLOGIK: Gemessenen Wert verarbeiten ⭐
              
              // 4a. Tibber-Wert glätten (Anti-Oszillation)
              const smoothedTibber = smoothTibberValue(intelState.tibberCurrent);
              
              // 4b. ⭐ KRITISCH: Intelligente Begrenzung anwenden (ZUERST!) ⭐
              const limitedValue = applyIntelligentLimiting(smoothedTibber);
              
              // 4c. SOC-Schutz anwenden (Werte-Manipulation)
              const socProtectedValue = applySocProtection(limitedValue);
              
              // 4d. Throttling & Dead-Zone
              if (shouldThrottle()) {
                  // Zu früh - nicht senden
                  return;
              }
              
              const finalValue = applyDeadZoneAndRateLimit(socProtectedValue);
              
              // 5. ⭐ KERNPRINZIP: Sende gemessenen/manipulierten Wert (NICHT Sollwert!) ⭐
              intelState.reportedPower = finalValue;
              intelState.lastReportedPower = finalValue;
              
              // 6. An Core senden
              sendToCore();
              
              // 7. Metriken aktualisieren
              updateZeroWattMetrics();
              
              // 8. Status-Updates
              const powerChange = finalValue - intelState.lastReportedPower;
              safeSetState(INTEL_CONFIG.INTEL_PREFIX + 'tibber_raw', Math.round(intelState.tibberCurrent));
              safeSetState(INTEL_CONFIG.INTEL_PREFIX + 'tibber_smoothed', Math.round(smoothedTibber));
              safeSetState(INTEL_CONFIG.INTEL_PREFIX + 'reported_power', Math.round(finalValue));
              safeSetState(INTEL_CONFIG.INTEL_PREFIX + 'power_change', Math.round(powerChange));
              safeSetState(INTEL_CONFIG.INTEL_PREFIX + 'limiting_active', intelState.limitingActive);
              
              intelState.lastUpdate = Date.now();
              
              // Debug-Logging mit korrekter Reihenfolge
              if (INTEL_CONFIG.LOG_INTELLIGENCE && Math.abs(powerChange) > 50) {
                  console.log(`📤 Intelligence v2.0 FIX: Tibber ${Math.round(intelState.tibberCurrent)}W → Begrenzt ${Math.round(limitedValue)}W → SOC-Schutz ${Math.round(socProtectedValue)}W → Gesendet ${Math.round(finalValue)}W`);
              }
              
          } catch (error) {
              console.error('❌ Intelligence: Loop-Fehler:', error.message);
              // Fallback: Tibber-Wert direkt senden
              try {
                  intelState.reportedPower = intelState.tibberCurrent || 0;
                  sendToCore();
              } catch (fallbackError) {
                  console.error('❌ Intelligence: Auch Fallback fehlgeschlagen:', fallbackError.message);
              }
          }
      }
      
      // ===================================================
      // INTELLIGENCE START
      // ===================================================
      
      function startIntelligence() {
          console.log('🧠 MARSTEK-Intelligence v2.0 - KORREKTE REGELUNGSLOGIK\n');
          console.log('🎯 ZWECK: INTELLIGENTE 0W-REGELUNG');
          console.log('✅ KERNPRINZIP: Sendet gemessenen Netzbezug (nicht Sollwerte!)');
          console.log('✅ SOC-Intelligence (Werte-Manipulation für Schutz)');
          console.log('✅ Intelligente Begrenzung (des gemessenen Werts)');
          console.log('✅ Anti-Oszillations-Glättung (Tibber-Wert)');
          console.log('✅ Tasmota Reality-Check (Effizienz-Monitoring)');
          console.log('✅ 0W-Metriken & Tagesstatistiken');
          console.log('');
          console.log('🔗 KOMMUNIKATION:');
          console.log('   📥 Empfängt von Core: tibber_current (gemessener Netzbezug)');
          console.log('   📤 Sendet an Core: reported_power (manipulierter Messwert)');
          console.log('   🎯 MARSTEK regelt automatisch auf 0W');
          console.log('   ⚠️ Darf crashen ohne CT-Simulation zu stoppen!');
          console.log('=====================================');
          
          // Datenpunkte erstellen
          createIntelligenceDatapoints();
          
          // Status setzen
          safeSetState(INTEL_CONFIG.INTEL_PREFIX + 'intel_active', true);
          
          // Haupt-Loop starten
          setInterval(runIntelligenceLoop, INTEL_CONFIG.UPDATE_INTERVAL);
          
          // Sofort starten
          setTimeout(runIntelligenceLoop, 2000);
          
          console.log('\n🧠 INTELLIGENCE v2.0 AKTIV!');
          console.log('💡 Neue Logik: Sendet Messwerte statt Sollwerte');
          console.log('🔧 Begrenzung: ±' + INTEL_CONFIG.POWER_LIMIT + 'W');
          console.log('🌊 Glättung: ' + (INTEL_CONFIG.SMOOTHING_ENABLED ? 'Aktiv (' + Math.round(INTEL_CONFIG.SMOOTH_FACTOR*100) + '%)' : 'Deaktiviert'));
          console.log('🔋 SOC-Schutz: ' + (INTEL_CONFIG.SOC_PROTECTION_ENABLED ? 'Aktiv' : 'Deaktiviert'));
          console.log('☁️ Wolken-Erkennung: ' + (INTEL_CONFIG.CLOUD_SMOOTHING ? 'Aktiv' : 'Deaktiviert'));
          console.log('📊 Reality-Check: ' + (INTEL_CONFIG.TASMOTA_REALITY_CHECK ? 'Aktiv' : 'Deaktiviert'));
          console.log('🎯 0W-Focus: ' + (INTEL_CONFIG.ZERO_WATT_FOCUS ? 'Aktiv' : 'Deaktiviert'));
          console.log('=====================================\n');
      }
      
      function stopIntelligence() {
          try {
              safeSetState(INTEL_CONFIG.INTEL_PREFIX + 'intel_active', false);
              console.log('🛑 MARSTEK-Intelligence v2.0 beendet');
          } catch (error) {
              console.error('❌ Intelligence Stop-Fehler:', error.message);
          }
      }
      
      // ===================================================
      // SCRIPT START
      // ===================================================
      
      startIntelligence();
      
      onStop(() => {
          stopIntelligence();
      });
      
      // ===================================================
      // KONFIGURATIONSHINWEISE FÜR FORUM-NUTZER:
      // ===================================================
      /*
      WICHTIGE ANPASSUNGEN NÖTIG:
      
      1. TASMOTA-GERÄTE-PFADE ANPASSEN:
         Zeile 13: tasmotaXXXX: 'sonoff.0.TasmotaXXX-MSTXXXX.ENERGY_Power'
         Zeile 14: tasmotayyyy: 'sonoff.0.TasmotaXXX-MSTXXXX.ENERGY_Power'
         → Ersetze TasmotaXXX-MSTXXXX durch deine echten Tasmota-Geräte-Namen
         → Zweites Gerät ist optional - kann auch weggelassen werden
         
      2. PREFIXE ANPASSEN:
         Zeile 32: SHARED_PREFIX: '0_userdata.0.marstek_V-E.shared.'
         Zeile 33: INTEL_PREFIX: '0_userdata.0.marstek_V-E.intel.'
         → Passe die Prefixe an deine ioBroker-Struktur an
         
      3. MONITOR-PREFIX PRÜFEN:
         Zeile 189: const monitorPrefix = '0_userdata.0.marstek_V-E.monitor.';
         → Falls du ein Monitor-Script verwendest, passe den Pfad an
         
      4. TASMOTA-GERÄTE FINDEN:
         - In ioBroker: Objekte → sonoff.0 → TasmotaXXX-MSTXXXX → ENERGY_Power
         - Oder unter deinem Tasmota/MQTT-Adapter
         
      5. POWER_LIMIT ANPASSEN:
         Zeile 16: POWER_LIMIT: 750
         → Passe die Leistungsgrenze an deine Anlage an (in Watt)
         
      6. SOC-SCHWELLWERTE ANPASSEN:
         Zeile 26: SOC_HIGH_THRESHOLD: 95  (Ladeschutz ab X% SOC)
         Zeile 27: SOC_LOW_THRESHOLD: 10   (Entladeschutz ab X% SOC)
         → Passe an deine Batterie-Strategie an
      
      OPTIONAL FEATURES:
      - Wenn kein Tasmota vorhanden: TASMOTA_REALITY_CHECK: false setzen
      - Wenn kein SOC verfügbar: SOC_PROTECTION_ENABLED: false setzen
      - Logging reduzieren: LOG_INTELLIGENCE: false setzen
      */
      
      posted in ioBroker Allgemein
      G
      Gismoh
    • RE: Shelly 3PM Pro Emulator

      @cwa
      Erstmal das erste Skript (An Ende sind Konfigurationshinweise)
      Aber, wie bereits geschrieben, es ist noch in Arbeit 😉
      1-MARSTEK-Core-Engine:

      // ===================================================
      // MARSTEK-Core-Engine v2.0 - KORREKTE CT-SIMULATION
      // ===================================================
      // 
      // 🎯 ZWECK: ROCK-SOLID CT-SIMULATION - DARF NIEMALS CRASHEN!
      // ✅ HTTP/UDP Server (Ports 1010, 2220, 1011) 
      // ✅ Shelly Pro 3EM Emulation
      // ✅ Basis Power-Reading (Tibber + Shelly)
      // ✅ ECHTE PHASENVERTEILUNG (Shelly-basiert + 66%/20%/14% Fallback)
      // ✅ v2.0: Kompatibel mit korrigierter Intelligence-Logik
      // ❌ KEINE komplexen Berechnungen (Crash-Risiko!)
      // 
      // ===================================================
      
      // KONFIGURATION (MINIMAL)
      const CORE_CONFIG = {
          // Hardware-Pfade - BITTE ANPASSEN!
          shellyInstance: 'shelly.0.SHEM-3#XXXXXXXXXXXX#1',  // ← Deine Shelly-Geräte-ID hier eintragen
          tibberPowerPath: 'tibberlink.0.LocalPulse.0.Power', // ← Dein Tibber-Pfad hier eintragen
          
          // Netzwerk
          httpPort: 1010,
          udpPort1: 1010,
          udpPort2: 2220,
          udpPort3: 1011,
          
          // Timing
          UPDATE_INTERVAL: 3000,
          
          // Geteilte Datenpunkte - BITTE ANPASSEN!
          SHARED_PREFIX: '0_userdata.0.marstek_V-E.shared.',  // ← Dein Prefix hier eintragen
          
          // Logging
          LOG_BASIC: true,
          LOG_PHASE_DISTRIBUTION: false    // v2.0: Reduziertes Logging
      };
      
      // MINIMALER STATE (NUR BASICS)
      let coreState = {
          // Basis-Werte
          tibberRaw: 0,
          tibberCurrent: 0,
          shellyL1: 0,
          shellyL2: 0,
          shellyL3: 0,
          
          // ⭐ v2.0: Von Intelligence empfangen (neuer Name) ⭐
          reportedPower: 0,             // Intelligence → Core (gemessener/manipulierter Wert)
          correctionFactor: 1,          // Nicht mehr verwendet, aber kompatibel
          limitingActive: false,        // Status-Info von Intelligence
          
          // Server-Objekte
          httpServer: null,
          udpSocket1: null,
          udpSocket2: null,
          udpSocket3: null,
          
          // Status
          lastUpdate: 0,
          lastUdpLog: 0,
          lastPhaseLog: 0,
          coreActive: true
      };
      
      // ===================================================
      // GETEILTE DATENPUNKTE ERSTELLEN
      // ===================================================
      
      function createSharedDatapoints() {
          const prefix = CORE_CONFIG.SHARED_PREFIX;
          
          const datapoints = [
              // Core → Intelligence
              { name: 'tibber_current', value: 0, desc: 'Aktuelle Tibber-Leistung [W]' },
              { name: 'tibber_raw', value: 0, desc: 'Rohe Tibber-Leistung [W]' },
              { name: 'shelly_l1', value: 0, desc: 'Shelly Phase L1 [W]' },
              { name: 'shelly_l2', value: 0, desc: 'Shelly Phase L2 [W]' },
              { name: 'shelly_l3', value: 0, desc: 'Shelly Phase L3 [W]' },
              { name: 'core_active', value: true, desc: 'Core-Engine läuft' },
              
              // Intelligence → Core (v2.0: Neue Namen)
              { name: 'target_power', value: 0, desc: 'Gemeldeter Netzbezug von Intelligence [W]' },
              { name: 'correction_factor', value: 1, desc: 'Korrekturfaktor (legacy)' },
              { name: 'limiting_active', value: false, desc: 'Begrenzung aktiv' },
              
              // Monitor → Alle
              { name: 'override_mode', value: '', desc: 'Manueller Override (test/normal)' },
              { name: 'core_status', value: 'starting', desc: 'Core-Engine Status' }
          ];
          
          datapoints.forEach(dp => {
              try {
                  createState(prefix + dp.name, dp.value, {
                      name: dp.desc,
                      type: typeof dp.value,
                      read: true,
                      write: true,
                      desc: `MARSTEK Core-Engine v2.0 - ${dp.desc}`
                  });
              } catch (error) {
                  // State existiert bereits - OK
              }
          });
      }
      
      // ===================================================
      // POWER-READING (EINFACH & SICHER)
      // ===================================================
      
      function readBasicPowerValues() {
          try {
              // Tibber lesen (mit Error-Handling)
              const tibberValue = getState(CORE_CONFIG.tibberPowerPath).val;
              if (tibberValue !== null && !isNaN(tibberValue)) {
                  coreState.tibberRaw = tibberValue;
                  coreState.tibberCurrent = tibberValue; // Basis-Version ohne Glättung
                  
                  // An Intelligence weiterleiten
                  setState(CORE_CONFIG.SHARED_PREFIX + 'tibber_current', tibberValue);
                  setState(CORE_CONFIG.SHARED_PREFIX + 'tibber_raw', tibberValue);
              }
              
              // Shelly Phasen lesen (mit Error-Handling)
              try {
                  coreState.shellyL1 = getState(CORE_CONFIG.shellyInstance + '.Emeter0.Power').val || 0;
                  coreState.shellyL2 = getState(CORE_CONFIG.shellyInstance + '.Emeter1.Power').val || 0;
                  coreState.shellyL3 = getState(CORE_CONFIG.shellyInstance + '.Emeter2.Power').val || 0;
                  
                  setState(CORE_CONFIG.SHARED_PREFIX + 'shelly_l1', coreState.shellyL1);
                  setState(CORE_CONFIG.SHARED_PREFIX + 'shelly_l2', coreState.shellyL2);
                  setState(CORE_CONFIG.SHARED_PREFIX + 'shelly_l3', coreState.shellyL3);
              } catch (error) {
                  // Shelly-Fehler nicht kritisch - weiter mit 0-Werten
                  if (CORE_CONFIG.LOG_BASIC) {
                      console.log('⚠️ Core: Shelly-Lesefehler (nicht kritisch)');
                  }
              }
              
              // ⭐ v2.0: Von Intelligence empfangen (neuer Name) ⭐
              try {
                  // Intelligence sendet jetzt "gemessenen/manipulierten Wert" statt Sollwert
                  // ⭐ KRITISCHER FIX: 0W ist ein gültiger Wert! Nur bei null/undefined Fallback ⭐
                  const targetPowerVal = getState(CORE_CONFIG.SHARED_PREFIX + 'target_power').val;
                  coreState.reportedPower = (targetPowerVal !== null && targetPowerVal !== undefined) 
                                           ? targetPowerVal 
                                           : coreState.tibberCurrent;
                  
                  const correctionVal = getState(CORE_CONFIG.SHARED_PREFIX + 'correction_factor').val;
                  coreState.correctionFactor = (correctionVal !== null && correctionVal !== undefined) 
                                              ? correctionVal 
                                              : 1;
                  
                  const limitingVal = getState(CORE_CONFIG.SHARED_PREFIX + 'limiting_active').val;
                  coreState.limitingActive = (limitingVal !== null && limitingVal !== undefined) 
                                            ? limitingVal 
                                            : false;
              } catch (error) {
                  // Fallback: Tibber-Werte verwenden
                  coreState.reportedPower = coreState.tibberCurrent;
              }
              
              coreState.lastUpdate = Date.now();
              
          } catch (error) {
              // KRITISCHER FEHLER - aber Core darf nicht crashen!
              console.error('❌ Core: Kritischer Power-Reading Fehler:', error.message);
              // Fallback-Werte verwenden
              coreState.reportedPower = 0;
          }
      }
      
      // ===================================================
      // ECHTE PHASENVERTEILUNG (BEREITS KORREKT - BEIBEHALTEN)
      // ===================================================
      
      function createRealPhaseDistribution(totalPower) {
          try {
              // Echte Shelly-Phasen-Verhältnisse berechnen
              const shellyTotal = Math.abs(coreState.shellyL1) + Math.abs(coreState.shellyL2) + Math.abs(coreState.shellyL3);
              
              // Fallback bei Shelly-Total = 0 oder sehr klein
              if (shellyTotal < 10) {
                  // FALLBACK: Echte Haushalts-Verteilung (66%/20%/14% - typisch DE)
                  return {
                      l1: totalPower * 0.66,
                      l2: totalPower * 0.20,
                      l3: totalPower * 0.14
                  };
              }
              
              // Echte prozentuale Verteilung basierend auf Shelly-Verhältnissen
              const l1Ratio = Math.abs(coreState.shellyL1) / shellyTotal;
              const l2Ratio = Math.abs(coreState.shellyL2) / shellyTotal;
              const l3Ratio = Math.abs(coreState.shellyL3) / shellyTotal;
              
              // v2.0: DEBUG-LOG für Phasenverteilung (reduziert)
              const now = Date.now();
              const shouldLog = CORE_CONFIG.LOG_PHASE_DISTRIBUTION && 
                               (!coreState.lastPhaseLog || (now - coreState.lastPhaseLog) > 120000) && 
                               Math.abs(totalPower) > 100;
              
              if (shouldLog) {
                  console.log(`📊 Core: Phasenverteilung - Shelly: L1=${Math.round(coreState.shellyL1)}W(${Math.round(l1Ratio*100)}%) L2=${Math.round(coreState.shellyL2)}W(${Math.round(l2Ratio*100)}%) L3=${Math.round(coreState.shellyL3)}W(${Math.round(l3Ratio*100)}%)`);
                  console.log(`📊 Core: Reported ${Math.round(totalPower)}W → L1=${Math.round(totalPower * l1Ratio)}W L2=${Math.round(totalPower * l2Ratio)}W L3=${Math.round(totalPower * l3Ratio)}W`);
                  coreState.lastPhaseLog = now;
              }
              
              return {
                  l1: totalPower * l1Ratio,
                  l2: totalPower * l2Ratio,
                  l3: totalPower * l3Ratio
              };
              
          } catch (error) {
              // NOTFALL-FALLBACK: Echte Haushalts-Verteilung (66%/20%/14%)
              console.error('❌ Core: Phasenverteilung Fehler - verwende Fallback');
              return {
                  l1: totalPower * 0.66,
                  l2: totalPower * 0.20,
                  l3: totalPower * 0.14
              };
          }
      }
      
      // ===================================================
      // VENUS RESPONSE (STABIL)
      // ===================================================
      
      function createBasicVenusResponse() {
          try {
              // ⭐ v2.0: Intelligence sendet "gemessenen/manipulierten Wert" ⭐
              let reportedTotal = coreState.reportedPower;
              
              // Override-Modus prüfen (von Monitor)
              const overrideMode = getState(CORE_CONFIG.SHARED_PREFIX + 'override_mode').val;
              if (overrideMode === 'test') {
                  reportedTotal = 100; // Test-Wert
              } else if (overrideMode === 'zero') {
                  reportedTotal = 0;   // Force Zero
              }
              
              // Echte Phasenverteilung basierend auf Shelly-Verhältnissen
              const phases = createRealPhaseDistribution(reportedTotal);
              const l1 = phases.l1;
              const l2 = phases.l2;
              const l3 = phases.l3;
              
              // Shelly Pro 3EM Response (Standard-Werte)
              const voltage = 230.0;
              const frequency = 50.0;
              const power_factor = 1.0;
              
              const a_current = Math.abs(l1) / voltage;
              const b_current = Math.abs(l2) / voltage;
              const c_current = Math.abs(l3) / voltage;
              const total_current = Math.abs(reportedTotal) / voltage;
              
              const a_aprt_power = Math.abs(l1);
              const b_aprt_power = Math.abs(l2);
              const c_aprt_power = Math.abs(l3);
              const total_aprt_power = Math.abs(reportedTotal);
              
              return {
                  id: 0,
                  a_current: Math.round(a_current * 1000) / 1000,
                  a_voltage: voltage,
                  a_act_power: Math.round(l1 * 10) / 10,
                  a_aprt_power: Math.round(a_aprt_power * 10) / 10,
                  a_pf: power_factor,
                  a_freq: frequency,
                  b_current: Math.round(b_current * 1000) / 1000,
                  b_voltage: voltage,
                  b_act_power: Math.round(l2 * 10) / 10,
                  b_aprt_power: Math.round(b_aprt_power * 10) / 10,
                  b_pf: power_factor,
                  b_freq: frequency,
                  c_current: Math.round(c_current * 1000) / 1000,
                  c_voltage: voltage,
                  c_act_power: Math.round(l3 * 10) / 10,
                  c_aprt_power: Math.round(c_aprt_power * 10) / 10,
                  c_pf: power_factor,
                  c_freq: frequency,
                  n_current: null,
                  total_current: Math.round(total_current * 1000) / 1000,
                  total_act_power: Math.round(reportedTotal * 10) / 10,
                  total_aprt_power: Math.round(total_aprt_power * 10) / 10,
                  user_calibrated_phase: []
              };
              
          } catch (error) {
              // NOTFALL-RESPONSE - Core darf nicht crashen!
              console.error('❌ Core: Venus Response Fehler - verwende Notfall-Response');
              return {
                  id: 0,
                  total_act_power: 0,
                  total_aprt_power: 0,
                  total_current: 0,
                  a_act_power: 0, a_aprt_power: 0, a_current: 0, a_voltage: 230, a_pf: 1, a_freq: 50,
                  b_act_power: 0, b_aprt_power: 0, b_current: 0, b_voltage: 230, b_pf: 1, b_freq: 50,
                  c_act_power: 0, c_aprt_power: 0, c_current: 0, c_voltage: 230, c_pf: 1, c_freq: 50,
                  n_current: null,
                  user_calibrated_phase: []
              };
          }
      }
      
      // ===================================================
      // HTTP SERVER (STABIL)
      // ===================================================
      
      function createStableHTTPServer() {
          const http = require('http');
          const url = require('url');
          
          return http.createServer(function(req, res) {
              try {
                  res.setHeader('Access-Control-Allow-Origin', '*');
                  res.setHeader('Content-Type', 'application/json');
                  const parsedUrl = url.parse(req.url, true);
                  
                  if (parsedUrl.pathname === '/rpc/EM.GetStatus') {
                      const response = createBasicVenusResponse();
                      
                      // v2.0: Verbessertes Logging mit 0W-Fix-Info
                      if (CORE_CONFIG.LOG_BASIC) {
                          const status = coreState.limitingActive ? " (BEGRENZT)" : "";
                          const socInfo = response.total_act_power === 0 ? " (SOC-SCHUTZ)" : "";
                          console.log(`📤 Core v2.0 FIX: ${response.total_act_power}W (L1=${Math.round(response.a_act_power)}W L2=${Math.round(response.b_act_power)}W L3=${Math.round(response.c_act_power)}W)${status}${socInfo}`);
                      }
                      
                      res.writeHead(200);
                      res.end(JSON.stringify(response));
                      return;
                  }
                  
                  if (parsedUrl.pathname === '/rpc/Shelly.GetInfo') {
                      const info = {
                          "id": "marstek-core-engine-v20-fix",
                          "mac": "MARSTEKCOREXX",  // Anonymisiert
                          "model": "SPRO-3EM",
                          "gen": 2,
                          "fw_id": "20250810-000000/2.0.1-core-engine-fixed",
                          "ver": "2.0.1",
                          "app": "Pro3EM"
                      };
                      res.writeHead(200);
                      res.end(JSON.stringify(info));
                      return;
                  }
                  
                  res.writeHead(404);
                  res.end(JSON.stringify({error: "Not Found"}));
                  
              } catch (error) {
                  // HTTP-Fehler nicht kritisch - Standard-Response senden
                  console.error('❌ Core: HTTP Error:', error.message);
                  try {
                      res.writeHead(500);
                      res.end(JSON.stringify({error: "Core Engine Error"}));
                  } catch (e) {
                      // Auch das könnte fehlschlagen - ignorieren
                  }
              }
          });
      }
      
      // ===================================================
      // UDP HANDLER (STABIL)
      // ===================================================
      
      function handleStableUDPMessage(msg, rinfo, socket) {
          try {
              const message = msg.toString();
              
              // UDP-Logging (begrenzt)
              const now = Date.now();
              if (!coreState.lastUdpLog || (now - coreState.lastUdpLog) > 60000) {
                  if (CORE_CONFIG.LOG_BASIC) {
                      console.log(`📡 Core: UDP aktiv (Port ${socket.address().port})`);
                  }
                  coreState.lastUdpLog = now;
              }
              
              if (message.includes('EM.GetStatus')) {
                  const response = createBasicVenusResponse();
                  socket.send(Buffer.from(JSON.stringify(response)), rinfo.port, rinfo.address);
              }
              
          } catch (error) {
              // UDP-Fehler nicht kritisch
              if (CORE_CONFIG.LOG_BASIC) {
                  console.log('⚠️ Core: UDP Fehler (nicht kritisch)');
              }
          }
      }
      
      // ===================================================
      // CORE-ENGINE START
      // ===================================================
      
      function startCoreEngine() {
          console.log('🚀 MARSTEK-Core-Engine v2.0.1 - KORREKTE CT-SIMULATION (0W-FIX)\n');
          console.log('🎯 ZWECK: ROCK-SOLID CT-SIMULATION');
          console.log('✅ HTTP/UDP Server für Shelly Pro 3EM Emulation');
          console.log('✅ Basis Power-Reading (Tibber + Shelly)');
          console.log('✅ ECHTE PHASENVERTEILUNG (Shelly-basiert + 66%/20%/14% Fallback)');
          console.log('✅ v2.0: Kompatibel mit korrigierter Intelligence-Logik');
          console.log('✅ v2.0.1: KRITISCHER 0W-BUG REPARIERT!');
          console.log('❌ KEINE komplexen Berechnungen (Crash-Schutz!)');
          console.log('');
          console.log('🔗 KOMMUNIKATION:');
          console.log('   📤 Sendet: tibber_current, shelly_l1/l2/l3');
          console.log('   📥 Empfängt: target_power (jetzt gemessener/manipulierter Wert)');
          console.log('   🤝 Intelligence-Script v2.0 sendet korrekte Messwerte');
          console.log('   📊 Monitor-Script überwacht und steuert');
          console.log('');
          console.log('🔄 v2.0.1 KRITISCHER FIX:');
          console.log('   🚨 0W-Bug repariert: 0W ist ein gültiger Wert!');
          console.log('   📊 Fallback nur noch bei null/undefined');
          console.log('   🔋 SOC-Schutz funktioniert jetzt korrekt');
          console.log('   📝 Verbessertes Logging mit SOC-Info');
          console.log('=====================================');
          
          // Geteilte Datenpunkte erstellen
          createSharedDatapoints();
          
          // HTTP Server starten
          try {
              coreState.httpServer = createStableHTTPServer();
              coreState.httpServer.listen(CORE_CONFIG.httpPort, () => {
                  console.log('✅ HTTP Server auf Port ' + CORE_CONFIG.httpPort);
              });
          } catch (error) {
              console.error('❌ HTTP Server Start-Fehler:', error.message);
              // Trotzdem weitermachen - UDP könnte funktionieren
          }
          
          // Multi-UDP Server starten
          const dgram = require('dgram');
          
          try {
              coreState.udpSocket1 = dgram.createSocket('udp4');
              coreState.udpSocket1.bind(CORE_CONFIG.udpPort1, () => {
                  console.log('✅ UDP Port ' + CORE_CONFIG.udpPort1 + ' (FW <= v224)');
              });
              coreState.udpSocket1.on('message', (msg, rinfo) => {
                  handleStableUDPMessage(msg, rinfo, coreState.udpSocket1);
              });
          } catch (error) {
              console.error('❌ UDP1 Fehler:', error.message);
          }
          
          try {
              coreState.udpSocket2 = dgram.createSocket('udp4');
              coreState.udpSocket2.bind(CORE_CONFIG.udpPort2, () => {
                  console.log('✅ UDP Port ' + CORE_CONFIG.udpPort2 + ' (FW >= v226)');
              });
              coreState.udpSocket2.on('message', (msg, rinfo) => {
                  handleStableUDPMessage(msg, rinfo, coreState.udpSocket2);
              });
          } catch (error) {
              console.error('❌ UDP2 Fehler:', error.message);
          }
          
          try {
              coreState.udpSocket3 = dgram.createSocket('udp4');
              coreState.udpSocket3.bind(CORE_CONFIG.udpPort3, () => {
                  console.log('✅ UDP Port ' + CORE_CONFIG.udpPort3 + ' (Fallback)');
              });
              coreState.udpSocket3.on('message', (msg, rinfo) => {
                  handleStableUDPMessage(msg, rinfo, coreState.udpSocket3);
              });
          } catch (error) {
              console.error('❌ UDP3 Fehler:', error.message);
          }
          
          // Status setzen
          setState(CORE_CONFIG.SHARED_PREFIX + 'core_status', 'running');
          setState(CORE_CONFIG.SHARED_PREFIX + 'core_active', true);
          
          // Haupt-Loop starten
          setInterval(readBasicPowerValues, CORE_CONFIG.UPDATE_INTERVAL);
          
          // Sofort ersten Wert lesen
          setTimeout(readBasicPowerValues, 1000);
          
          console.log('\n🎯 CORE-ENGINE v2.0.1 AKTIV - 0W-BUG REPARIERT!');
          console.log('💡 Basis-CT funktioniert - Intelligence-Script v2.0 sendet korrekte Messwerte');
          console.log('🛡️ CRASH-SICHER: Basis-CT läuft auch wenn andere Scripts crashen');
          console.log('📊 Phasenverteilung: Shelly-basiert oder 66%/20%/14% Fallback');
          console.log('🔧 Kompatibel mit neuer Intelligence-Regelungslogik');
          console.log('🚨 KRITISCHER 0W-BUG REPARIERT: SOC-Schutz funktioniert jetzt!');
          console.log('=====================================\n');
      }
      
      function stopCoreEngine() {
          try {
              if (coreState.httpServer) coreState.httpServer.close();
              if (coreState.udpSocket1) coreState.udpSocket1.close();
              if (coreState.udpSocket2) coreState.udpSocket2.close();
              if (coreState.udpSocket3) coreState.udpSocket3.close();
              
              setState(CORE_CONFIG.SHARED_PREFIX + 'core_status', 'stopped');
              setState(CORE_CONFIG.SHARED_PREFIX + 'core_active', false);
              
              console.log('🛑 MARSTEK-Core-Engine v2.0 beendet');
          } catch (error) {
              console.error('❌ Stop-Fehler:', error.message);
          }
      }
      
      // ===================================================
      // SCRIPT START
      // ===================================================
      
      startCoreEngine();
      
      onStop(() => {
          stopCoreEngine();
      });
      
      // ===================================================
      // KONFIGURATIONSHINWEISE FÜR FORUM-NUTZER:
      // ===================================================
      /*
      WICHTIGE ANPASSUNGEN NÖTIG:
      
      1. SHELLY-GERÄTE-ID ANPASSEN:
         Zeile 14: shellyInstance: 'shelly.0.SHEM-3#XXXXXXXXXXXX#1'
         → Ersetze XXXXXXXXXXXX durch deine echte Shelly-Geräte-ID
         
      2. TIBBER-PFAD PRÜFEN:
         Zeile 15: tibberPowerPath: 'tibberlink.0.LocalPulse.0.Power'
         → Prüfe ob dein Tibber-Adapter denselben Pfad verwendet
         
      3. SHARED-PREFIX ANPASSEN:
         Zeile 25: SHARED_PREFIX: '0_userdata.0.marstek_V-E.shared.'
         → Passe den Prefix an deine ioBroker-Struktur an
         
      4. SHELLY-GERÄTE-ID FINDEN:
         - In ioBroker: Objekte → shelly.0 → SHEM-3#[DEINE-ID]#1
         - Oder im Shelly-Adapter unter "Instanzen"
         
      5. TIBBER-PFAD FINDEN:
         - In ioBroker: Objekte → tibberlink.0 → LocalPulse.0 → Power
         - Oder deinen entsprechenden Stromzähler-Pfad
      */
      
      posted in ioBroker Allgemein
      G
      Gismoh
    • RE: Shelly 3PM Pro Emulator

      @cwa
      Sorry, habe dein Anfrage erst nun gesehen.
      Brauchst du es noch?

      100% ist es eh noch nicht fertig, habe gerade noch dran gebastelt und bin sicherlich noch nicht durch.
      Es sind mittlerweile 3 Skripte die zusammenarbeiten, aber es reicht, diese als JS im IObroker laufen zu lassen.

      posted in ioBroker Allgemein
      G
      Gismoh
    • RE: Shelly 3PM Pro Emulator

      LoL, habe nun mal den anderen Marstek Venus e auf Eigenverbrauch gesetzt, dort regelt der Marstek nun selbstständig 😉
      Muss nur noch beobachten wie, aber denke darauf habe ich mit echten Werten eh keinen Einfluss,
      aber der JS scheint auszureichen.

      Edit:
      Musste das JS noch anpassen, denn der Marstek hatte nicht gut geregelt, nun scheint die Regelung vom Marstek ganz gut zu laufen 😉

      posted in ioBroker Allgemein
      G
      Gismoh
    • RE: Shelly 3PM Pro Emulator

      Nun kommen alle drei Phasen in der App an.
      Weis aber noch nicht wie sich der Venus-E verhält, aktuelle sind die Speicher voll, und haben PV-Überschuss.

      posted in ioBroker Allgemein
      G
      Gismoh
    • RE: Shelly 3PM Pro Emulator

      Komme auch nicht wirklich weiter, habe ein JS-Skript für den IOBroker mit Hilfe von einer KI gebaut,
      Der Marstek Venus E kann sich mit dem Simulierten Shelly 3emPro verbinden, dort werden auch Realistische Werte angezeigt, wenn über 100Watt.
      Hatte vorher alle drei Phasen ausprobiert, habe ich allerdings nicht hinbekommen.

      Bin wieder bei meinem Problem, was ich auch bei anderen Versuchen hatte:
      Wenn der Marstek sieht, er soll entladen, macht er das nicht zum reelen Wert, also z.B. 500 Watt, sondern fährt auf die Begrenzung in der App (800 Watt).
      Laut Anzeige 797 Watt, und speist so 300 Watt in das Netz ein, was ich natürlich nicht so erwünsche.
      (5 Tage investiert und das Ding macht immer noch was es will 🆘😭)

      Hat dort jemand eine Lösung dafür?
      (Firmware v152)

      posted in ioBroker Allgemein
      G
      Gismoh
    Community
    Impressum | Datenschutz-Bestimmungen | Nutzungsbedingungen
    The ioBroker Community 2014-2023
    logo