Navigation

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

    NEWS

    • Neuer Blog: Fotos und Eindrücke aus Solingen

    • ioBroker@Smart Living Forum Solingen, 14.06. - Agenda added

    • ioBroker goes Matter ... Matter Adapter in Stable

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

    Gismoh

    @Gismoh

    6
    Reputation
    28
    Profile views
    349
    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

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

      // ===================================================
      // Shelly EM3 Pro Emulation für Marstek Venus E (MIT ECHTEN SHELLY-WERTEN!)
      // by Gismoh
      // ===================================================
      
      // MARSTEK KONFIGURATION - HIER ANPASSEN!
      var MARSTEK_CONFIG = {
          useAbsoluteValues: false,       // Für CT001 Protokoll-Kompatibilität
          invertSignForTest: false,       // ✅ RICHTIGE VORZEICHEN (wie echter Shelly!) - TESTWEISE AUF TRUE SETZEN!
          clampNegativeToZero: false,     // Alternative: negative Werte auf 0
          separateInputOutput: true,      // Separate Input/Output Behandlung
          debugMode: false,               // ❌ Debug-Modus DEAKTIVIERT (reduziert Spam)
          logInterval: 30000,             // Nur alle 30 Sekunden loggen
          logOnlyChanges: true,           // Nur bei Wert-Änderungen loggen
          minChangeThreshold: 50          // Mindestens 50W Änderung für Log
      };
      
      // Konfiguration - Anpassen an deine Shelly EM3 Instanz
      var SHELLY_EM3_INSTANCE = 'shelly.0.SHEM-3#XXXXXXXXXX'; // Deine Shelly EM3 Instanz ID
      var EMULATION_PORT = 1010; // Port für die Emulation
      var UPDATE_INTERVAL = 5000; // Update-Intervall in ms
      
      // Module laden
      var http = require('http');
      var url = require('url');
      var dgram = require('dgram');
      var os = require('os');
      
      // Globale Variablen für die Messwerte
      var currentPowerTotal = 0;
      var currentPowerL1 = 0;
      var currentPowerL2 = 0;
      var currentPowerL3 = 0;
      
      // ✅ ECHTE MESSWERTE FÜR ALLE PHASEN (AUS SHELLY DATEN!)
      var currentCurrentL1 = 0;
      var currentCurrentL2 = 0;
      var currentCurrentL3 = 0;
      var currentVoltageL1 = 230.0;
      var currentVoltageL2 = 230.0;
      var currentVoltageL3 = 230.0;
      var currentPowerFactorL1 = 1.0;
      var currentPowerFactorL2 = 1.0;
      var currentPowerFactorL3 = 1.0;
      
      var energyTotal = 0;
      var energyL1 = 0;
      var energyL2 = 0;
      var energyL3 = 0;
      var energyReturnedL1 = 0;
      var energyReturnedL2 = 0;
      var energyReturnedL3 = 0;
      
      // Performance-Variablen
      var lastLogTime = 0;
      var requestCounter = 0;
      var lastLoggedPower = 0;     // ✅ Für Change-Detection
      var lastDetailedLog = 0;     // ✅ Für seltene Detail-Logs
      
      // Server-Instanzen
      var serverInstance = null;
      var udpSocket = null;
      
      function calculateMarstekPower(originalPower) {
          var result = {
              originalPower: originalPower,
              marstekPower: originalPower,   // Für CT-Anzeige (immer Echtwert)
              limitedPower: originalPower,   // Wird unten korrigiert
              powerInput: 0,
              powerOutput: 0,
              direction: originalPower >= 0 ? 'Bezug' : 'Einspeisung',
              explanation: ''
          };
      
          // ✅ VORZEICHEN UMKEHREN FÜR TEST
          if (MARSTEK_CONFIG.invertSignForTest) {
              result.marstekPower = -originalPower;
              result.direction = result.marstekPower >= 0 ? 'Bezug (umgekehrt)' : 'Einspeisung (umgekehrt)';
              result.explanation = 'Vorzeichen umgekehrt für Venus E Test';
          }
      
          // Begrenzung für Akkuverhalten (auf umgekehrten Wert anwenden)
          var powerForLimit = result.marstekPower;
          if (powerForLimit < 0) {
              // Einspeisung → Akku darf max. mit 1000 W laden
              result.limitedPower = Math.max(powerForLimit, -1000);
              result.explanation += ' - Ladebegrenzung auf max 1000W';
          } else if (powerForLimit > 0) {
              // Netzbezug → Akku darf max. mit 1000 W entladen
              result.limitedPower = Math.min(powerForLimit, 1000);
              result.explanation += ' - Entladebegrenzung auf max 1000W';
          } else {
              result.limitedPower = 0;
              result.explanation += ' - Kein Netzfluss – keine Aktion';
          }
      
          if (MARSTEK_CONFIG.separateInputOutput) {
              if (result.marstekPower >= 0) {
                  result.powerInput = result.marstekPower;
                  result.powerOutput = 0;
              } else {
                  result.powerInput = 0;
                  result.powerOutput = Math.abs(result.marstekPower);
              }
          }
      
          // ✅ NUR LOGGEN BEI GROßEN ÄNDERUNGEN
          var powerChange = Math.abs(result.marstekPower - lastLoggedPower);
          if (MARSTEK_CONFIG.debugMode && powerChange > MARSTEK_CONFIG.minChangeThreshold) {
              console.log('POWER CHANGE: ' + lastLoggedPower + 'W → ' + result.marstekPower + 'W (' + result.direction + ')');
              lastLoggedPower = result.marstekPower;
          }
      
          return result;
      }
      
      // ✅ KORRIGIERTE Funktion zum Lesen der ECHTEN Shelly EM3 Pro Werte
      function readShellyValues() {
          try {
              // ✅ HAUPTQUELLE: Tibber Pulse (direkt am Stromzähler)
              var tibberPower = getState('tibberlink.0.LocalPulse.0.Power').val;
              
              // ✅ ECHTE SHELLY EM3 PRO WERTE AUSLESEN
              var shellyL1Power = getState(SHELLY_EM3_INSTANCE + '.Emeter0.Power').val || 0;
              var shellyL2Power = getState(SHELLY_EM3_INSTANCE + '.Emeter1.Power').val || 0;
              var shellyL3Power = getState(SHELLY_EM3_INSTANCE + '.Emeter2.Power').val || 0;
              
              // ✅ ECHTE CURRENT-WERTE (Ampere)
              var shellyL1Current = getState(SHELLY_EM3_INSTANCE + '.Emeter0.Current').val || 0;
              var shellyL2Current = getState(SHELLY_EM3_INSTANCE + '.Emeter1.Current').val || 0;
              var shellyL3Current = getState(SHELLY_EM3_INSTANCE + '.Emeter2.Current').val || 0;
              
              // ✅ ECHTE POWER FACTOR WERTE
              var shellyL1PF = getState(SHELLY_EM3_INSTANCE + '.Emeter0.PowerFactor').val || 1.0;
              var shellyL2PF = getState(SHELLY_EM3_INSTANCE + '.Emeter1.PowerFactor').val || 1.0;
              var shellyL3PF = getState(SHELLY_EM3_INSTANCE + '.Emeter2.PowerFactor').val || 1.0;
              
              // ✅ ECHTE ENERGIE-WERTE (Wh)
              var shellyL1Energy = getState(SHELLY_EM3_INSTANCE + '.Emeter0.Total').val || 0;
              var shellyL2Energy = getState(SHELLY_EM3_INSTANCE + '.Emeter1.Total').val || 0;
              var shellyL3Energy = getState(SHELLY_EM3_INSTANCE + '.Emeter2.Total').val || 0;
              
              // ✅ ECHTE RÜCKSPEISUNG-WERTE (Wh)
              var shellyL1Returned = getState(SHELLY_EM3_INSTANCE + '.Emeter0.Total_Returned').val || 0;
              var shellyL2Returned = getState(SHELLY_EM3_INSTANCE + '.Emeter1.Total_Returned').val || 0;
              var shellyL3Returned = getState(SHELLY_EM3_INSTANCE + '.Emeter2.Total_Returned').val || 0;
              
              var shellyTotal = shellyL1Power + shellyL2Power + shellyL3Power;
              
              // ✅ PLAUSIBILITÄTSPRÜFUNG für Shelly-Werte
              if (Math.abs(shellyL1Power) > 5000 || Math.abs(shellyL2Power) > 5000 || Math.abs(shellyL3Power) > 5000) {
                  console.log('⚠️ Unplausible Shelly-Werte detected: L1=' + shellyL1Power + ', L2=' + shellyL2Power + ', L3=' + shellyL3Power);
                  return; // Keine Update bei unrealistischen Werten
              }
              
              // ✅ SICHERHEITSPRÜFUNG: Plausible Werte verwenden
              if (tibberPower !== null && tibberPower !== undefined && Math.abs(tibberPower) < 15000) {
                  currentPowerTotal = tibberPower;
                  if (MARSTEK_CONFIG.debugMode) {
                      console.log('Verwende Tibber Pulse: ' + tibberPower + 'W');
                  }
              } else {
                  currentPowerTotal = shellyTotal;
                  console.log('FALLBACK zu Shelly: ' + shellyTotal + 'W (Tibber: ' + tibberPower + ')');
              }
              
              // ✅ ALLE ECHTEN WERTE ÜBERNEHMEN
              currentPowerL1 = shellyL1Power;
              currentPowerL2 = shellyL2Power;
              currentPowerL3 = shellyL3Power;
              
              currentCurrentL1 = shellyL1Current;
              currentCurrentL2 = shellyL2Current;
              currentCurrentL3 = shellyL3Current;
              
              currentPowerFactorL1 = shellyL1PF;
              currentPowerFactorL2 = shellyL2PF;
              currentPowerFactorL3 = shellyL3PF;
              
              // ✅ VOLTAGE BERECHNUNG (falls verfügbar, sonst aus Power/Current)
              if (shellyL1Current > 0.1) {
                  currentVoltageL1 = Math.abs(shellyL1Power / shellyL1Current / shellyL1PF);
              } else {
                  currentVoltageL1 = 230.0; // Fallback
              }
              
              if (shellyL2Current > 0.1) {
                  currentVoltageL2 = Math.abs(shellyL2Power / shellyL2Current / shellyL2PF);
              } else {
                  currentVoltageL2 = 230.0; // Fallback
              }
              
              if (shellyL3Current > 0.1) {
                  currentVoltageL3 = Math.abs(shellyL3Power / shellyL3Current / shellyL3PF);
              } else {
                  currentVoltageL3 = 230.0; // Fallback
              }
              
              // ✅ ENERGIE-WERTE
              energyL1 = shellyL1Energy;
              energyL2 = shellyL2Energy;
              energyL3 = shellyL3Energy;
              energyTotal = energyL1 + energyL2 + energyL3;
              
              energyReturnedL1 = shellyL1Returned;
              energyReturnedL2 = shellyL2Returned;
              energyReturnedL3 = shellyL3Returned;
              
              if (MARSTEK_CONFIG.debugMode) {
                  console.log('📊 ECHTE SHELLY WERTE:');
                  console.log('  Power: L1=' + currentPowerL1.toFixed(1) + 'W, L2=' + currentPowerL2.toFixed(1) + 'W, L3=' + currentPowerL3.toFixed(1) + 'W, Total=' + currentPowerTotal.toFixed(1) + 'W');
                  console.log('  Current: L1=' + currentCurrentL1.toFixed(2) + 'A, L2=' + currentCurrentL2.toFixed(2) + 'A, L3=' + currentCurrentL3.toFixed(2) + 'A');
                  console.log('  Voltage: L1=' + currentVoltageL1.toFixed(1) + 'V, L2=' + currentVoltageL2.toFixed(1) + 'V, L3=' + currentVoltageL3.toFixed(1) + 'V');
                  console.log('  PowerFactor: L1=' + currentPowerFactorL1.toFixed(2) + ', L2=' + currentPowerFactorL2.toFixed(2) + ', L3=' + currentPowerFactorL3.toFixed(2));
              }
              
          } catch (error) {
              console.error('❌ Fehler beim Lesen der Shelly Werte:');
              console.error(error);
          }
      }
      
      // Lokale IP-Adresse ermitteln
      function getLocalIP() {
          try {
              var interfaces = os.networkInterfaces();
              for (var interfaceName in interfaces) {
                  var addresses = interfaces[interfaceName];
                  for (var i = 0; i < addresses.length; i++) {
                      var address = addresses[i];
                      if (address.family === 'IPv4' && !address.internal) {
                          return address.address;
                      }
                  }
              }
          } catch (e) {
              console.log('Fehler beim Ermitteln der IP-Adresse');
          }
          return '192.168.1.100';
      }
      
      // ✅ MARSTEK VENUS E FORMAT - EXAKT 4 FELDER AUF ROOT-EBENE!
      function createMarstekVenusEResponse() {
          var powerCalc = calculateMarstekPower(currentPowerTotal);
          
          // ✅ GENAU DAS WAS MARSTEK ERWARTET - NUR 4 FELDER!
          var response = {
              "a_act_power": currentPowerL1,
              "b_act_power": currentPowerL2,
              "c_act_power": currentPowerL3,
              "total_act_power": powerCalc.marstekPower
          };
          
          // ✅ SICHERSTELLEN DASS NUR 4 FELDER GESENDET WERDEN
          if (Object.keys(response).length !== 4) {
              console.log('⚠️ WARNUNG: Response hat ' + Object.keys(response).length + ' Felder statt 4!');
          }
          
          return response;
      }
      
      // ✅ ECHTE Shelly.GetStatus Response (VERSCHACHTELTE Struktur)
      function createShellyGetStatusResponse() {
          var demand = currentPowerTotal;
          var correctedPower = demand;
          var powerCalc = calculateMarstekPower(correctedPower);
          
          // ✅ NUR LOGGEN BEI DEBUG MODE UND GROßEN ÄNDERUNGEN
          if (MARSTEK_CONFIG.debugMode && Math.abs(correctedPower - lastLoggedPower) > MARSTEK_CONFIG.minChangeThreshold) {
              console.log('📊 Shelly.GetStatus ECHTE WERTE: A=' + currentPowerL1.toFixed(1) + 'W/' + currentCurrentL1.toFixed(2) + 'A, B=' + currentPowerL2.toFixed(1) + 'W/' + currentCurrentL2.toFixed(2) + 'A, C=' + currentPowerL3.toFixed(1) + 'W/' + currentCurrentL3.toFixed(2) + 'A');
          }
      
          // ✅ ECHTE SHELLY.GETSTATUS STRUKTUR - MIT 100% ECHTEN 3-PHASEN WERTEN!
          return {
              "ble": {},
              "cloud": {"connected": true},
              "em:0": {
                  "id": 0,
                  "a_current": currentCurrentL1,          // ✅ ECHTER Current-Wert Phase A!
                  "a_voltage": currentVoltageL1,          // ✅ ECHTER Voltage-Wert Phase A!
                  "a_act_power": currentPowerL1,          // ✅ ECHTER Power-Wert Phase A!
                  "a_aprt_power": Math.abs(currentPowerL1),
                  "a_pf": currentPowerFactorL1,           // ✅ ECHTER PowerFactor Phase A!
                  "a_freq": 50.0,
                  
                  "b_current": currentCurrentL2,          // ✅ ECHTER Current-Wert Phase B!
                  "b_voltage": currentVoltageL2,          // ✅ ECHTER Voltage-Wert Phase B!
                  "b_act_power": currentPowerL2,          // ✅ ECHTER Power-Wert Phase B!
                  "b_aprt_power": Math.abs(currentPowerL2),
                  "b_pf": currentPowerFactorL2,           // ✅ ECHTER PowerFactor Phase B!
                  "b_freq": 50.0,
                  
                  "c_current": currentCurrentL3,          // ✅ ECHTER Current-Wert Phase C!
                  "c_voltage": currentVoltageL3,          // ✅ ECHTER Voltage-Wert Phase C!
                  "c_act_power": currentPowerL3,          // ✅ ECHTER Power-Wert Phase C!
                  "c_aprt_power": Math.abs(currentPowerL3),
                  "c_pf": currentPowerFactorL3,           // ✅ ECHTER PowerFactor Phase C!
                  "c_freq": 50.0,
                  
                  "n_current": null,                      // ✅ Wie echter Shelly (oft null)
                  "total_current": currentCurrentL1 + currentCurrentL2 + currentCurrentL3, // ✅ Summe der echten Ströme
                  "total_act_power": powerCalc.marstekPower,
                  "total_aprt_power": Math.abs(powerCalc.marstekPower),
                  "user_calibrated_phase": []
              },
              "emdata:0": {
                  "id": 0,
                  "a_total_act_energy": energyL1,         // ✅ ECHTE Energie Phase A
                  "a_total_act_ret_energy": energyReturnedL1, // ✅ ECHTE Rückspeisung Phase A
                  "b_total_act_energy": energyL2,         // ✅ ECHTE Energie Phase B
                  "b_total_act_ret_energy": energyReturnedL2, // ✅ ECHTE Rückspeisung Phase B
                  "c_total_act_energy": energyL3,         // ✅ ECHTE Energie Phase C
                  "c_total_act_ret_energy": energyReturnedL3, // ✅ ECHTE Rückspeisung Phase C
                  "total_act": energyTotal,               // ✅ ECHTE Gesamt-Energie
                  "total_act_ret": energyReturnedL1 + energyReturnedL2 + energyReturnedL3 // ✅ ECHTE Gesamt-Rückspeisung
              },
              "eth": {"ip": getLocalIP()},
              "modbus": {},
              "mqtt": {"connected": false},
              "sys": {
                  "mac": "XXXXXXXXXXXX",
                  "restart_required": false,
                  "time": new Date().toTimeString().split(' ')[0],
                  "unixtime": Math.floor(Date.now() / 1000),
                  "uptime": 12345,
                  "ram_size": 255736,
                  "ram_free": 76476,
                  "fs_size": 524288,
                  "fs_free": 188416,
                  "cfg_rev": 12,
                  "kvs_rev": 1
              }
          };
      }
      
      // HTTP Server erstellen
      var server = http.createServer(function(req, res) {
          var parsedUrl = url.parse(req.url, true);
          
          // CORS Headers setzen
          res.setHeader('Access-Control-Allow-Origin', '*');
          res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
          res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
          
          if (req.method === 'OPTIONS') {
              res.writeHead(200);
              res.end();
              return;
          }
          
          // ✅ NUR WICHTIGE HTTP REQUESTS LOGGEN
          var isImportantRequest = parsedUrl.pathname.includes('/rpc/EM.GetStatus') || 
                                  parsedUrl.pathname.includes('/rpc/Shelly.GetStatus') ||
                                  parsedUrl.pathname.includes('/settings');
          
          if (isImportantRequest) {
              console.log('🌐 HTTP: ' + parsedUrl.pathname + ' von ' + req.connection.remoteAddress);
          }
          
          // ✅ KRITISCH: VERSCHIEDENE ENDPOINTS = VERSCHIEDENE STRUKTUREN!
          
          // Venus E erwartet: /rpc/EM.GetStatus → MARSTEK FORMAT (4 Felder)
          if (parsedUrl.pathname === '/rpc/EM.GetStatus') {
              res.writeHead(200, { 'Content-Type': 'application/json' });
              var response = createMarstekVenusEResponse();
              
              console.log('🎯 VENUS E EM.GetStatus! Sende MARSTEK FORMAT: total_act_power=' + response.total_act_power + 'W (ROOT-LEVEL)');
              
              res.end(JSON.stringify(response));
              return;
          }
          
          // Legacy Support: /rpc/Shelly.GetStatus → VERSCHACHTELTE Struktur
          if (parsedUrl.pathname === '/rpc/Shelly.GetStatus' || parsedUrl.pathname === '/status') {
              res.writeHead(200, { 'Content-Type': 'application/json' });
              var response = createShellyGetStatusResponse();
              
              // ✅ NUR BEI GROßEN ÄNDERUNGEN LOGGEN
              var powerChange = Math.abs(response['em:0'].total_act_power - lastLoggedPower);
              if (powerChange > MARSTEK_CONFIG.minChangeThreshold) {
                  console.log('📊 Legacy Shelly.GetStatus! em:0.total_act_power: ' + response['em:0'].total_act_power + 'W');
              }
              
              res.end(JSON.stringify(response));
              return;
          }
          
          // Shelly Info API emulieren
          if (parsedUrl.pathname === '/rpc/Shelly.GetInfo' || parsedUrl.pathname === '/settings') {
              res.writeHead(200, { 'Content-Type': 'application/json' });
              var infoResponse = {
                  "id": 0,
                  "src": "shellypro3em-XXXXXXXXXXXX",
                  "result": {
                      "name": "Shelly Pro 3EM Emulation (ECHTE SHELLY-DATEN)",
                      "id": "shellypro3em-XXXXXXXXXXXX",
                      "mac": "XXXXXXXXXXXX",
                      "slot": 1,
                      "model": "SPRO-3EM",
                      "gen": 2,
                      "fw_id": "20230913-XXXXXX/v1.14.0-XXXXXXXX",
                      "ver": "1.14.0",
                      "app": "Pro3EM",
                      "auth_en": false,
                      "auth_domain": null
                  }
              };
              res.end(JSON.stringify(infoResponse));
              return;
          }
          
          // Zusätzliche Shelly-Endpunkte
          if (parsedUrl.pathname === '/shelly') {
              res.writeHead(200, { 'Content-Type': 'application/json' });
              res.end(JSON.stringify({
                  "type": "SPRO-3EM",
                  "mac": "XXXXXXXXXXXX",
                  "auth": false,
                  "fw": "1.14.0",
                  "discoverable": true,
                  "longid": 1,
                  "num_outputs": 1,
                  "num_meters": 3
              }));
              return;
          }
          
          // RPC über HTTP (fallback) - AUCH MARSTEK FORMAT
          if (parsedUrl.pathname === '/rpc') {
              res.writeHead(200, { 'Content-Type': 'application/json' });
              res.end(JSON.stringify(createMarstekVenusEResponse()));
              return;
          }
          
          // Device Description
          if (parsedUrl.pathname === '/description.xml') {
              res.writeHead(200, { 'Content-Type': 'text/xml' });
              var xml = '<?xml version="1.0"?>\n' +
                        '<root xmlns="urn:schemas-upnp-org:device-1-0">\n' +
                        '<device>\n' +
                        '<deviceType>urn:shelly:device:pro3em:1</deviceType>\n' +
                        '<friendlyName>Shelly Pro 3EM Emulation (ECHTE SHELLY-DATEN)</friendlyName>\n' +
                        '<manufacturer>Allterco</manufacturer>\n' +
                        '<modelName>Shelly Pro 3EM</modelName>\n' +
                        '<UDN>uuid:shelly-pro3em-emulation</UDN>\n' +
                        '</device>\n' +
                        '</root>';
              res.end(xml);
              return;
          }
          
          // Fallback für unbekannte Anfragen
          // ✅ NUR UNBEKANNTE REQUESTS LOGGEN (reduziert Spam)
          res.writeHead(404, { 'Content-Type': 'text/plain' });
          res.end('Not Found: ' + parsedUrl.pathname);
      });
      
      // ✅ KORRIGIERTE RPC Request Handler für UDP
      function handleRPCRequest(msg, rinfo, socket) {
          var message = msg.toString();
          
          try {
              // RPC-Anfrage parsen
              if (message.includes('EM.GetStatus') || message.includes('Shelly.GetStatus')) {
                  
                  var correctedPower = currentPowerTotal;
                  var powerCalc = calculateMarstekPower(correctedPower);
                  
                  requestCounter++;
                  var now = Date.now();
                  
                  // Reduziertes Logging
                  if (now - lastLogTime > MARSTEK_CONFIG.logInterval || requestCounter % 20 === 0) {
                      console.log('📡 UDP REQUEST #' + requestCounter + ' von ' + rinfo.address + ' - Method: ' + (message.includes('EM.GetStatus') ? 'EM.GetStatus' : 'Shelly.GetStatus'));
                      lastLogTime = now;
                  }
                  
                  // Parse die Anfrage um die ID zu bekommen
                  var requestData;
                  try {
                      requestData = JSON.parse(message);
                  } catch (e) {
                      requestData = { id: 1, method: 'EM.GetStatus', params: { id: 0 } };
                  }
                  
                  // ✅ VERSCHIEDENE RPC RESPONSES je nach Method!
                  var rpcResponse;
                  
                  if (message.includes('EM.GetStatus')) {
                      // ✅ MARSTEK VENUS E UDP REQUEST → NUR 4 FELDER AUF ROOT-EBENE!
                      rpcResponse = createMarstekVenusEResponse();
                      console.log('🎯 UDP EM.GetStatus! Sende MARSTEK FORMAT: total_act_power=' + rpcResponse.total_act_power + 'W (ROOT-LEVEL)');
                  } else {
                      // Legacy UDP Request → VERSCHACHTELTE Struktur
                      rpcResponse = {
                          "id": requestData.id || 1,
                          "src": "shellypro3em-XXXXXXXXXXXX",
                          "result": createShellyGetStatusResponse()
                      };
                      console.log('📊 UDP Shelly.GetStatus! Sende em:0.total_act_power: ' + powerCalc.marstekPower + 'W (VERSCHACHTELT)');
                  }
                  
                  var responseStr = JSON.stringify(rpcResponse);
                  
                  socket.send(responseStr, rinfo.port, rinfo.address, function(err) {
                      if (err && MARSTEK_CONFIG.debugMode) {
                          console.log('Fehler beim Senden: ' + err);
                      }
                  });
              }
              // Fallback: JSON RPC versuchen zu parsen
              else {
                  try {
                      var rpcRequest = JSON.parse(message);
                      if (rpcRequest.method) {
                          console.log('🔧 RPC Method: ' + rpcRequest.method);
                          
                          var rpcResponse = createMarstekVenusEResponse();
                          
                          var responseStr = JSON.stringify(rpcResponse);
                          socket.send(responseStr, rinfo.port, rinfo.address);
                          console.log('📤 JSON RPC Response gesendet');
                      }
                  } catch (e) {
                      if (MARSTEK_CONFIG.debugMode) {
                          console.log('❓ Unbekannte UDP-Nachricht: ' + message.substring(0, 100));
                      }
                  }
              }
          } catch (error) {
              console.log('❌ Fehler bei UDP-Verarbeitung: ' + error);
          }
      }
      
      // UDP Socket für Discovery erstellen
      function createUDPSocket() {
          udpSocket = dgram.createSocket('udp4');
          
          udpSocket.on('message', function(msg, rinfo) {
              var message = msg.toString();
              
              // Discovery Response
              if (message.includes('M-SEARCH') || message.includes('shelly') || message.includes('SSDP')) {
                  var localIP = getLocalIP();
                  var response = 'HTTP/1.1 200 OK\r\n' +
                                'ST: urn:shelly:device\r\n' +
                                'USN: uuid:shelly-pro3em-emulation\r\n' +
                                'LOCATION: http://' + localIP + ':' + EMULATION_PORT + '/settings\r\n' +
                                'SERVER: Shelly/1.14.0\r\n' +
                                'CACHE-CONTROL: max-age=1800\r\n' +
                                '\r\n';
                  
                  udpSocket.send(response, rinfo.port, rinfo.address, function(err) {
                      // Kein Success-Logging für Discovery
                  });
              }
              // Falls doch RPC über Port 1900 kommt
              else {
                  handleRPCRequest(msg, rinfo, udpSocket);
              }
          });
          
          udpSocket.on('error', function(err) {
              console.log('UDP Discovery Fehler: ' + err);
          });
      }
      
      // Emulation starten
      function startEmulation() {
          if (serverInstance) {
              console.log('Emulation läuft bereits');
              return;
          }
          
          try {
              // HTTP Server starten
              serverInstance = server.listen(EMULATION_PORT, function() {
                  console.log('=== ECHTE SHELLY PRO 3EM EMULATION GESTARTET ===');
                  console.log('Port: ' + EMULATION_PORT);
                  console.log('Erreichbar unter: http://localhost:' + EMULATION_PORT);
                  console.log('');
                  console.log('✅ ECHTE API STRUKTUR:');
                  console.log('  /rpc/EM.GetStatus      → DIREKTE Struktur (für Venus E)');
                  console.log('  /rpc/Shelly.GetStatus  → VERSCHACHTELTE Struktur (Legacy)');
                  console.log('');
                  console.log('🎯 Venus E sollte /rpc/EM.GetStatus verwenden!');
                  console.log('📊 Verwendet echte Shelly EM3 Pro Messwerte!');
                  console.log('========================================================');
              });
              
              // UDP Discovery starten (Port 1900)
              createUDPSocket();
              udpSocket.bind(1900, function() {
                  console.log('UDP Discovery läuft auf Port 1900');
              });
              
              // UDP Socket für Port 1010 (RPC)
              var rpcSocket = dgram.createSocket('udp4');
              rpcSocket.bind(1010, function() {
                  console.log('UDP RPC Socket läuft auf Port 1010');
              });
              
              rpcSocket.on('message', function(msg, rinfo) {
                  handleRPCRequest(msg, rinfo, rpcSocket);
              });
              
              rpcSocket.on('error', function(err) {
                  console.log('UDP RPC Port 1010 Fehler: ' + err);
              });
              
              // Regelmäßige Updates der Messwerte
              setInterval(readShellyValues, UPDATE_INTERVAL);
              
              // Initiale Werte laden
              readShellyValues();
              
          } catch (error) {
              console.error('Fehler beim Starten der Emulation:');
              console.error(error);
          }
      }
      
      function stopEmulation() {
          if (serverInstance) {
              serverInstance.close(function() {
                  console.log('Shelly EM3 Pro Emulation gestoppt');
                  serverInstance = null;
              });
          }
          if (udpSocket) {
              udpSocket.close(function() {
                  console.log('UDP Discovery gestoppt');
              });
          }
      }
      
      // ZUSÄTZLICHE Überwachungsfunktion für Marstek-Verhalten
      function monitorMarstekBehavior() {
          console.log('\n=== MARSTEK STATUS UPDATE ===');
          console.log('⚡ Aktueller Verbrauch: ' + currentPowerTotal + 'W');
          console.log('📊 Phase Details: L1=' + currentPowerL1.toFixed(1) + 'W, L2=' + currentPowerL2.toFixed(1) + 'W, L3=' + currentPowerL3.toFixed(1) + 'W');
          console.log('🔌 Current Details: L1=' + currentCurrentL1.toFixed(2) + 'A, L2=' + currentCurrentL2.toFixed(2) + 'A, L3=' + currentCurrentL3.toFixed(2) + 'A');
          console.log('⚡ Voltage Details: L1=' + currentVoltageL1.toFixed(1) + 'V, L2=' + currentVoltageL2.toFixed(1) + 'V, L3=' + currentVoltageL3.toFixed(1) + 'V');
          console.log('📡 UDP Requests: ' + requestCounter + ' (seit Start)');
          console.log('🔧 API-Struktur: EM.GetStatus (DIREKT) + Shelly.GetStatus (VERSCHACHTELT)');
          
          if (currentPowerTotal !== 0) {
              var calc = calculateMarstekPower(currentPowerTotal);
              console.log('🎯 Venus E erhält: total_act_power=' + calc.marstekPower + 'W (' + calc.direction + ')');
          }
          
          // Reset Counter für bessere Übersicht
          if (requestCounter > 1000) {
              requestCounter = 0;
              console.log('🔄 Request Counter zurückgesetzt');
          }
          
          console.log('=============================\n');
      }
      
      // Monitoring alle 2 Minuten
      setInterval(monitorMarstekBehavior, 120000);
      
      // Emulation starten
      startEmulation();
      
      // Cleanup bei Script-Stop
      onStop(function() {
          console.log('Script wird gestoppt...');
          stopEmulation();
      });
      
      // Test-Funktion für Venus E Kompatibilität
      function testVenusECompatibility() {
          console.log('\n=== VENUS E KOMPATIBILITÄTS-TEST ===');
          
          console.log('🎯 Teste EM.GetStatus (Venus E Endpoint):');
          var emResponse = createMarstekVenusEResponse();
          console.log('- total_act_power: ' + (emResponse.total_act_power !== undefined ? '✅ ' + emResponse.total_act_power + 'W' : '❌ FEHLT'));
          console.log('- id: ' + (emResponse.id !== undefined ? '✅ ' + emResponse.id : '❌ FEHLT'));
          console.log('- a_act_power: ' + (emResponse.a_act_power !== undefined ? '✅ ' + emResponse.a_act_power + 'W' : '❌ FEHLT'));
          console.log('- Struktur: ' + (emResponse.result === undefined ? '✅ ROOT-LEVEL (korrekt für Venus E)' : '❌ VERSCHACHTELT'));
          console.log('- Felder-Anzahl: ' + (Object.keys(emResponse).length === 4 ? '✅ 4 Felder (korrekt)' : '❌ ' + Object.keys(emResponse).length + ' Felder'));
          console.log('- Felder: ' + JSON.stringify(Object.keys(emResponse)));
          
          console.log('\n📊 Teste Shelly.GetStatus (Legacy Endpoint):');
          var shellyResponse = createShellyGetStatusResponse();
          console.log('- em:0.total_act_power: ' + (shellyResponse['em:0'] && shellyResponse['em:0'].total_act_power !== undefined ? '✅ ' + shellyResponse['em:0'].total_act_power + 'W' : '❌ FEHLT'));
          console.log('- Struktur: ' + (shellyResponse['em:0'] !== undefined ? '✅ VERSCHACHTELT (korrekt für Legacy)' : '❌ NICHT VERSCHACHTELT'));
          
          console.log('\n📊 ECHTE SHELLY-DATEN INTEGRATION:');
          console.log('- Power Werte: ' + (currentPowerL1 !== 0 || currentPowerL2 !== 0 || currentPowerL3 !== 0 ? '✅ Echte Werte geladen' : '❌ Keine echten Werte'));
          console.log('- Current Werte: ' + (currentCurrentL1 !== 0 || currentCurrentL2 !== 0 || currentCurrentL3 !== 0 ? '✅ Echte Ampere-Werte' : '❌ Standard-Werte'));
          console.log('- PowerFactor: ' + (currentPowerFactorL1 !== 1.0 || currentPowerFactorL2 !== 1.0 || currentPowerFactorL3 !== 1.0 ? '✅ Echte PF-Werte' : '❌ Standard-Werte'));
          console.log('- Energie-Werte: ' + (energyL1 > 0 || energyL2 > 0 || energyL3 > 0 ? '✅ Echte Energie-Daten' : '❌ Keine Energie-Daten'));
          
          console.log('\n💡 LOGGING KONFIGURATION:');
          console.log('- Debug Mode: ' + (MARSTEK_CONFIG.debugMode ? '🔍 AN (ausführlich)' : '🔇 AUS (reduziert)'));
          console.log('- Change Threshold: ' + MARSTEK_CONFIG.minChangeThreshold + 'W (nur bei größeren Änderungen loggen)');
          console.log('- Log Interval: ' + (MARSTEK_CONFIG.logInterval / 1000) + 's (reduziert Spam)');
          console.log('====================================\n');
      }
      
      // Test bei Start ausführen
      setTimeout(testVenusECompatibility, 3000);
      
      // DEBUGGING: Zeige alle HTTP Requests im Detail
      function enableDetailedLogging() {
          MARSTEK_CONFIG.debugMode = true;
          MARSTEK_CONFIG.minChangeThreshold = 0;  // Jede Änderung loggen
          MARSTEK_CONFIG.logInterval = 5000;      // Alle 5 Sekunden
          console.log('🔍 DETAILED LOGGING AKTIVIERT');
          console.log('- Jede Wert-Änderung wird geloggt');
          console.log('- Alle HTTP/UDP Requests werden geloggt'); 
          console.log('- Echte Shelly-Werte werden im Detail angezeigt');
          console.log('- Aufruf: disableDetailedLogging() zum Deaktivieren');
      }
      
      function disableDetailedLogging() {
          MARSTEK_CONFIG.debugMode = false;
          MARSTEK_CONFIG.minChangeThreshold = 50; // Nur große Änderungen
          MARSTEK_CONFIG.logInterval = 30000;     // Alle 30 Sekunden
          console.log('🔇 DETAILED LOGGING DEAKTIVIERT');
          console.log('- Nur noch wichtige Änderungen werden geloggt');
          console.log('- Reduzierte Log-Ausgabe für bessere Übersicht');
      }
      
      // Spezielle Test-Funktion für echte Shelly-Werte
      function testRealShellyValues() {
          console.log('\n=== ECHTE SHELLY-WERTE TEST ===');
          console.log('📊 Teste Shelly EM3 Instance: ' + SHELLY_EM3_INSTANCE);
          
          try {
              // Test alle Shelly-Werte
              var shellyL1Power = getState(SHELLY_EM3_INSTANCE + '.Emeter0.Power').val;
              var shellyL1Current = getState(SHELLY_EM3_INSTANCE + '.Emeter0.Current').val;
              var shellyL1PF = getState(SHELLY_EM3_INSTANCE + '.Emeter0.PowerFactor').val;
              var shellyL1Energy = getState(SHELLY_EM3_INSTANCE + '.Emeter0.Total').val;
              var shellyL1Returned = getState(SHELLY_EM3_INSTANCE + '.Emeter0.Total_Returned').val;
              
              console.log('Phase A (L1):');
              console.log('  Power: ' + (shellyL1Power !== null ? shellyL1Power + 'W ✅' : 'FEHLT ❌'));
              console.log('  Current: ' + (shellyL1Current !== null ? shellyL1Current + 'A ✅' : 'FEHLT ❌'));
              console.log('  PowerFactor: ' + (shellyL1PF !== null ? shellyL1PF + ' ✅' : 'FEHLT ❌'));
              console.log('  Energy: ' + (shellyL1Energy !== null ? shellyL1Energy + 'Wh ✅' : 'FEHLT ❌'));
              console.log('  Returned: ' + (shellyL1Returned !== null ? shellyL1Returned + 'Wh ✅' : 'FEHLT ❌'));
              
              // Tibber Test
              var tibberPower = getState('tibberlink.0.LocalPulse.0.Power').val;
              console.log('\nTibber Pulse:');
              console.log('  Power: ' + (tibberPower !== null ? tibberPower + 'W ✅' : 'FEHLT ❌'));
              
          } catch (error) {
              console.log('❌ Fehler beim Testen der Shelly-Werte: ' + error);
          }
          
          console.log('====================================\n');
      }
      
      // Test der echten Werte bei Start
      setTimeout(testRealShellyValues, 5000);
      
      // Aufruf: enableDetailedLogging() zum Aktivieren des Detailed Logging
      // Aufruf: testRealShellyValues() zum Testen der Shelly-Verbindung
      
      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
    • RE: Probleme getHistory im History Adapter v3.0.1- Invalid call

      @paul53, @Homoran

      Kurzes Update:

      async function erstelleUndLoggeTestDatenpunkt() {
        const testDpId = '0_userdata.0.testVerlaufDp';
      
        // Erstelle den Datenpunkt, falls er noch nicht existiert
        const existiert = await existsStateAsync(testDpId);
        if (!existiert) {
          await createStateAsync(testDpId, 0, {
            name: 'Test Verlauf Datenpunkt',
            type: 'number',
            role: 'state',
            read: true,
            write: true,
          });
          log(`Datenpunkt ${testDpId} wurde erstellt.`);
        }
      
        // Protokolliere einige Werte mit Zeitstempeln
        const jetzt = Date.now();
        await setStateAsync(testDpId, { val: 10, ack: false, ts: jetzt - 60000 }); // Protokolliere Wert 10 vor einer Minute
        await setStateAsync(testDpId, { val: 20, ack: false, ts: jetzt - 30000 }); // Protokolliere Wert 20 vor dreißig Sekunden
        await setStateAsync(testDpId, { val: 30, ack: true, ts: jetzt });        // Protokolliere Wert 30 jetzt
      
        log('Rufe leseTestVerlauf auf...');
      
        // Versuchen wir nun, den Verlauf dieses Datenpunkts abzurufen
        await leseTestVerlauf(testDpId);
      }
      
      async function leseTestVerlauf(dpId) {
        log('Testausgabe leseTestVerlauf');
        const historyInstanz = 'history.0';
        const jetzt = Date.now();
        const startZeit = jetzt - 120000; // Lese Verlauf der letzten zwei Minuten
        const endZeit = jetzt;
      
        const optionen = {
          start: startZeit,
          end: endZeit,
          aggregate: 'none',
          addId: true,
          returnNewestEntries: false,
        };
      
        log(`Versuche Verlauf für ${dpId} abzurufen...`);
        log(`Sende an ${historyInstanz} 'getHistory' mit Optionen (verschachtelt): ${JSON.stringify({ id: dpId, options: optionen })}`);
        try {
          const verlaufsDaten = await sendToAsync(historyInstanz, 'getHistory', { id: dpId, options: { ...optionen } });
          log(`Verlaufsdaten (raw) für ${dpId}: ${JSON.stringify(verlaufsDaten)}`);
          if (verlaufsDaten && verlaufsDaten.result) {
            log(`Verlaufsdaten für ${dpId}: ${JSON.stringify(verlaufsDaten.result)}`);
          } else {
            log(`Fehler beim Abrufen der Verlaufsdaten für ${dpId}: ${JSON.stringify(verlaufsDaten)}`, 'warn');
          }
        } catch (e) {
          log(`Fehler beim Aufruf von getHistory für ${dpId}: ${e}`, 'error');
        }
      }
      
      erstelleUndLoggeTestDatenpunkt();
      

      Das Problem mit "Invalid call. No options for getHistory provided" ist gelöst! Der Schlüssel war, die Optionen für getHistory im sendToAsync-Aufruf unter der Eigenschaft options zu verschachteln. Dies funktioniert sowohl mit der direkten Übergabe des optionen-Objekts als auch mit dem Spread-Operator:

      await sendToAsync(historyInstanz, 'getHistory', { id: dpId, options: optionen });
      

      oder

      await sendToAsync(historyInstanz, 'getHistory', { id: dpId, options: { ...optionen } });
      

      Der History Adapter erwartet die Optionen in dieser Struktur. Die Abfrage funktioniert nun wie erwartet.
      Als Nächstes werde ich nun mein ursprüngliches Skript anpassen, um die getHistory-Aufrufe entsprechend zu gestalten.
      Danke für die Unterstützung!

      posted in JavaScript
      G
      Gismoh
    • RE: Probleme getHistory im History Adapter v3.0.1- Invalid call

      @homoran
      merci, werde nun testen 😉

      posted in JavaScript
      G
      Gismoh
    • RE: Probleme getHistory im History Adapter v3.0.1- Invalid call

      @paul53
      Hallo nochmal!

      Vielen Dank für deine Hinweise und die Erklärung zur Zeitstempelübergabe.

      Leider hat auch das explizite Übergeben der einzelnen Optionen ohne den Spread-Operator nicht zu einer Änderung des Ergebnisses geführt.

      Fehler beim Aufruf von getHistory für <Datenpunkt-ID>: Invalid call. No options for getHistory provided
      

      Aktuell:
      Node.js: v20.19.1
      NPM: 10.8.2
      js-controller: 7.0.6

      async function erstelleUndLoggeTestDatenpunkt() {
        const testDpId = '0_userdata.0.testVerlaufDp';
      
        // Erstelle den Datenpunkt, falls er noch nicht existiert
        const existiert = await existsStateAsync(testDpId);
        if (!existiert) {
          await createStateAsync(testDpId, 0, {
            name: 'Test Verlauf Datenpunkt',
            type: 'number',
            role: 'state',
            read: true,
            write: true,
          });
          log(`Datenpunkt ${testDpId} wurde erstellt.`);
        }
      
        // Protokolliere einige Werte mit Zeitstempeln
        const jetzt = Date.now();
        await setStateAsync(testDpId, { val: 10, ack: false, ts: jetzt - 60000 }); // Protokolliere Wert 10 vor einer Minute
        await setStateAsync(testDpId, { val: 20, ack: false, ts: jetzt - 30000 }); // Protokolliere Wert 20 vor dreißig Sekunden
        await setStateAsync(testDpId, { val: 30, ack: true, ts: jetzt });        // Protokolliere Wert 30 jetzt
      
        log('Rufe leseTestVerlauf auf...');
      
        // Versuchen wir nun, den Verlauf dieses Datenpunkts abzurufen
        await leseTestVerlauf(testDpId);
      }
      
      async function leseTestVerlauf(dpId) {
        log('Testausgabe leseTestVerlauf');
        const historyInstanz = 'history.0';
        const jetzt = Date.now();
        const startZeit = jetzt - 120000; // Lese Verlauf der letzten zwei Minuten
        const endZeit = jetzt;
      
        const optionen = {
          start: startZeit,
          end: endZeit,
          aggregate: 'none',
          addId: true,
          returnNewestEntries: false,
        };
      
        log(`Versuche Verlauf für ${dpId} abzurufen...`);
        log(`Sende an ${historyInstanz} 'getHistory' mit Optionen: ${JSON.stringify({ id: dpId, start: optionen.start, end: optionen.end, aggregate: optionen.aggregate, addId: optionen.addId, returnNewestEntries: optionen.returnNewestEntries })}`);
        try {
          const verlaufsDaten = await sendToAsync(historyInstanz, 'getHistory', { id: dpId, start: optionen.start, end: optionen.end, aggregate: optionen.aggregate, addId: optionen.addId, returnNewestEntries: optionen.returnNewestEntries });
          log(`Verlaufsdaten (raw) für ${dpId}: ${JSON.stringify(verlaufsDaten)}`);
          if (verlaufsDaten && verlaufsDaten.result) {
            log(`Verlaufsdaten für ${dpId}: ${JSON.stringify(verlaufsDaten.result)}`);
          } else {
            log(`Fehler beim Abrufen der Verlaufsdaten für ${dpId}: ${JSON.stringify(verlaufsDaten)}`, 'warn');
          }
        } catch (e) {
          log(`Fehler beim Aufruf von getHistory für ${dpId}: ${e}`, 'error');
        }
      }
      
      erstelleUndLoggeTestDatenpunkt();
      

      Ergebnisse:

      Die Übergabe der Optionen an getHistory erfolgte sowohl mit dem Spread-Operator (...optionen - im vorherigen Test) als auch durch die explizite Angabe der einzelnen Eigenschaften ({ id: dpId, start: optionen.start, ... } - im letzten Test). Beide Methoden führten zum gleichen Ergebnis.
      Die Schreibweise für setStateAsync mit dem Objekt { val: ..., ts: ... } wurde verwendet.

      Die grundlegende Protokollierung von Datenpunkten durch den History Adapter funktioniert.
      Trotz dieser Versuche erhalte ich weiterhin den Fehler "Invalid call. No options for getHistory provided". Es scheint, als ob die übergebenen Optionen vom history-Adapter in meiner Umgebung aus irgendeinem Grund nicht akzeptiert werden.

      Hat vielleicht noch jemand eine Idee oder einen Ansatzpunkt, den ich bisher übersehen habe? Ich bin für jede Hilfe dankbar!

      posted in JavaScript
      G
      Gismoh
    • Probleme getHistory im History Adapter v3.0.1- Invalid call

      Hallo zusammen,

      ich habe Probleme, die getHistory-Funktion des History Adapters in meinen Javascript-Skripten zu verwenden. Egal welche Optionen ich übergebe, ich erhalte immer den Fehler:

      Fehler beim Aufruf von getHistory für <Datenpunkt-ID>: Invalid call. No options for getHistory provided
      

      Meine Systeminformationen:

      Betriebssystem: Linux
      Plattform: linux
      Architektur: x64
      Node.js: v20.18.3
      NPM: 10.8.2
      js-controller: 7.0.6
      History Adapter Versionen getestet: 3.0.1 (aktuell), 3.0.0, 2.2.6

      Meine history.0-Adapter Konfiguration:
      Ich verwende die Standardkonfiguration, hier wurden keine Anpassungen vorgenommen.

      Mein Testskript:

      async function erstelleUndLoggeTestDatenpunkt() {
        const testDpId = '0_userdata.0.testVerlaufDp';
      
        // Erstelle den Datenpunkt, falls er noch nicht existiert
        const existiert = await existsStateAsync(testDpId);
        if (!existiert) {
          await createStateAsync(testDpId, 0, {
            name: 'Test Verlauf Datenpunkt',
            type: 'number',
            role: 'state',
            read: true,
            write: true,
          });
          log(`Datenpunkt ${testDpId} wurde erstellt.`);
        }
      
        // Protokolliere einige Werte mit Zeitstempeln
        const jetzt = Date.now();
        await setStateAsync(testDpId, 10, false, jetzt - 60000);
        await setStateAsync(testDpId, 20, false, jetzt - 30000);
        await setStateAsync(testDpId, 30, true, jetzt);
      
        log('Rufe leseTestVerlauf auf...');
      
        // Versuchen wir nun, den Verlauf dieses Datenpunkts abzurufen
        await leseTestVerlauf(testDpId);
      }
      
      async function leseTestVerlauf(dpId) {
        log('Testausgabe leseTestVerlauf');
        const historyInstanz = 'history.0';
        const jetzt = Date.now();
        const startZeit = jetzt - 120000; // Lese Verlauf der letzten zwei Minuten
        const endZeit = jetzt;
      
        const optionen = {
          start: startZeit,
          end: endZeit,
          aggregate: 'none',
          addId: true,
          returnNewestEntries: false,
        };
      
        log(`Versuche Verlauf für ${dpId} abzurufen...`);
        log(`Sende an ${historyInstanz} 'getHistory' mit Optionen: ${JSON.stringify({ id: dpId, ...optionen })}`);
        try {
          const verlaufsDaten = await sendToAsync(historyInstanz, 'getHistory', { id: dpId, ...optionen });
          log(`Verlaufsdaten (raw) für ${dpId}: ${JSON.stringify(verlaufsDaten)}`);
          if (verlaufsDaten && verlaufsDaten.result) {
            log(`Verlaufsdaten für ${dpId}: ${JSON.stringify(verlaufsDaten.result)}`);
          } else {
            log(`Fehler beim Abrufen der Verlaufsdaten für ${dpId}: ${JSON.stringify(verlaufsDaten)}`, 'warn');
          }
        } catch (e) {
          log(`Fehler beim Aufruf von getHistory für ${dpId}: ${e}`, 'error');
        }
      }
      
      erstelleUndLoggeTestDatenpunkt();
      

      Log-Ausschnitte (Beispielhaft mit Version 3.0.1 - ähnliche Ausgabe mit anderen Versionen):

      javascript.0  info Start JavaScript script.js.Test.Akku-Skript-11_Test_001 (Javascript/js)
      javascript.0  info script.js.Test.Akku-Skript-11_Test_001: Rufe leseTestVerlauf auf...
      javascript.0  info script.js.Test.Akku-Skript-11_Test_001: Testausgabe leseTestVerlauf
      javascript.0  info script.js.Test.Akku-Skript-11_Test_001: Versuche Verlauf für 0_userdata.0.testVerlaufDp abzurufen...
      javascript.0  info script.js.Test.Akku-Skript-11_Test_001: Sende an history.0 'getHistory' mit Optionen: {"id":"0_userdata.0.testVerlaufDp","start":1746363867548,"end":1746363987548,"aggregate":"none","addId":true,"returnNewestEntries":false}
      javascript.0 error script.js.Test.Akku-Skript-11_Test_001: Fehler beim Aufruf von getHistory für 0_userdata.0.testVerlaufDp: Invalid call. No options for getHistory provided
      history.0     debug Value logged 0_userdata.0.testVerlaufDp, value=30, ts=1746363987545
      history.0     debug new value received for 0_userdata.0.testVerlaufDp, new-value=30, ts=1746363987545, relog=false
      history.0     debug Value logged 0_userdata.0.testVerlaufDp, value=20, ts=1746363987542
      history.0     debug new value received for 0_userdata.0.testVerlaufDp, new-value=20, ts=1746363987542, relog=false
      history.0     debug Value logged 0_userdata.0.testVerlaufDp, value=10, ts=1746363987454
      history.0     debug new value received for 0_userdata.0.testVerlaufDp, new-value=10, ts=1746363987454, relog=false
      

      Wie man sieht, werden die Optionen im Log korrekt ausgegeben, aber der history-Adapter meldet trotzdem einen "Invalid call". Die grundlegende Protokollierung von Werten durch den Adapter funktioniert jedoch.

      Ich habe bereits verschiedene Versionen des Adapters (3.0.1, 3.0.0, 2.2.6) getestet, aber das Problem bleibt bestehen.

      Hat jemand eine Idee, woran das liegen könnte oder wie ich das Problem weiter eingrenzen kann? Gibt es möglicherweise eine spezielle Konfiguration oder eine andere Interaktion, die hier eine Rolle spielt?

      Vielen Dank für eure Hilfe!

      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: Error admin0 (783) socket.io [init] No user found in cookies

      @mcm1957
      Merci, wird gerade installiert 😉

      Edit:
      Installation scheint sich "Tot zu laufen".
      Habe in dem Log zur Installation aber auch Probleme gesehen:

      gyp info it worked if it ends with ok
      
      gyp info using node-gyp@7.1.2
      
      gyp info using node@20.18.3 | linux | x64
      
      gyp info find Python using Python version 3.11.2 found at "/usr/bin/python3"
      
      gyp ERR! UNCAUGHT EXCEPTION
      
      gyp ERR! System Linux 6.1.0-31-amd64gyp ERR! command "/usr/bin/node" "/opt/iobroker/node_modules/usocket/node_modules/.bin/node-gyp" "rebuild"gyp ERR! cwd /opt/iobroker/node_modules/usocketgyp ERR! node -v v20.18.3gyp ERR! node-gyp -v v7.1.2gyp ERR! Node-gyp failed to build your package.gyp ERR! Try to update npm and/or node-gyp and if it does not help file an issue with the package author.
      
      npm error Cannot destructure property 'package' of 'node.target' as it is null.
      
      npm error A complete log of this run can be found in: /home/iobroker/.npm/_logs/2025-04-07T06_39_08_885Z-debug-0.log
      

      Der Vollständige Log:

      
      admin
      v7.6.3
      
      Nach Namen filtern
      admin
      Ausgewählte Adapter
      1
      Adapter insgesamt:
      563
      Installierte Adapter:
      71
      Adapter im letzten Monat aktualisiert:
      19
      
      admin
      Admin
      
      Installierte Instanzen:
      1
      Verfügbare Version:
      7.6.3
      Installierte Version:
      7.6.3
      Befehl ausführen
      weniger
      mehr
      $ iobroker url https://github.com/ioBroker/ioBroker.admin/archive/refs/tags/v7.6.4.zip --host ioBrokerVM --debug
      
      install ioBroker/ioBroker.admin#refs/tags/v7.6.4
      
      NPM version: 10.8.2
      
      Installing ioBroker/ioBroker.admin#refs/tags/v7.6.4... (System call)
      
      > usocket@0.3.0 install> node-gyp rebuild
      
      gyp info it worked if it ends with ok
      
      gyp info using node-gyp@7.1.2
      
      gyp info using node@20.18.3 | linux | x64
      
      gyp info find Python using Python version 3.11.2 found at "/usr/bin/python3"
      
      gyp ERR! UNCAUGHT EXCEPTION
      
      gyp ERR! System Linux 6.1.0-31-amd64gyp ERR! command "/usr/bin/node" "/opt/iobroker/node_modules/usocket/node_modules/.bin/node-gyp" "rebuild"gyp ERR! cwd /opt/iobroker/node_modules/usocketgyp ERR! node -v v20.18.3gyp ERR! node-gyp -v v7.1.2gyp ERR! Node-gyp failed to build your package.gyp ERR! Try to update npm and/or node-gyp and if it does not help file an issue with the package author.
      
      npm error Cannot destructure property 'package' of 'node.target' as it is null.
      
      npm error A complete log of this run can be found in: /home/iobroker/.npm/_logs/2025-04-07T06_39_08_885Z-debug-0.log
      
      upload [149] admin.admin /opt/iobroker/node_modules/iobroker.admin/admin/admin.png admin.png image/png
      
      upload [100] admin.admin /opt/iobroker/node_modules/iobroker.admin/admin/custom/assets/emotion-react.browser.esm-BuFM-c4M.js custom/assets/emotion-react.browser.esm-BuFM-c4M.js application/javascript
      
      upload [90] admin.admin /opt/iobroker/node_modules/iobroker.admin/admin/custom/assets/index-6YAx3tu6.js custom/assets/index-6YAx3tu6.js application/javascript
      
      upload [80] admin.admin /opt/iobroker/node_modules/iobroker.admin/admin/custom/assets/index-Bwqdok6R.js custom/assets/index-Bwqdok6R.js application/javascript
      
      upload [70] admin.admin /opt/iobroker/node_modules/iobroker.admin/admin/custom/assets/index-CcWdaO-A.js custom/assets/index-CcWdaO-A.js application/javascript
      
      upload [60] admin.admin /opt/iobroker/node_modules/iobroker.admin/admin/custom/assets/index-Di7Jseht.js custom/assets/index-Di7Jseht.js application/javascript
      
      upload [50] admin.admin /opt/iobroker/node_modules/iobroker.admin/admin/custom/assets/index-VCQPKkxj.js custom/assets/index-VCQPKkxj.js application/javascript
      
      upload [40] admin.admin /opt/iobroker/node_modules/iobroker.admin/admin/custom/assets/jsx-runtime-CTWygAA3.js custom/assets/jsx-runtime-CTWygAA3.js application/javascript
      
      upload [30] admin.admin /opt/iobroker/node_modules/iobroker.admin/admin/custom/assets/useTheme-Vt4pBeH8.js custom/assets/useTheme-Vt4pBeH8.js application/javascript
      
      upload [20] admin.admin /opt/iobroker/node_modules/iobroker.admin/admin/custom/i18n/es.json custom/i18n/es.json application/json
      
      upload [19] admin.admin /opt/iobroker/node_modules/iobroker.admin/admin/custom/i18n/fr.json custom/i18n/fr.json application/json
      
      upload [18] admin.admin /opt/iobroker/node_modules/iobroker.admin/admin/custom/i18n/it.json custom/i18n/it.json application/json
      
      upload [17] admin.admin /opt/iobroker/node_modules/iobroker.admin/admin/custom/i18n/nl.json custom/i18n/nl.json application/json
      
      upload [16] admin.admin /opt/iobroker/node_modules/iobroker.admin/admin/custom/i18n/pl.json custom/i18n/pl.json application/json
      
      upload [15] admin.admin /opt/iobroker/node_modules/iobroker.admin/admin/custom/i18n/pt.json custom/i18n/pt.json application/json
      
      upload [14] admin.admin /opt/iobroker/node_modules/iobroker.admin/admin/custom/i18n/ru.json custom/i18n/ru.json application/json
      
      upload [13] admin.admin /opt/iobroker/node_modules/iobroker.admin/admin/custom/i18n/zh-cn.json custom/i18n/zh-cn.json application/json
      
      upload [12] admin.admin /opt/iobroker/node_modules/iobroker.admin/admin/favicon.ico favicon.ico image/vnd.microsoft.icon
      
      upload [11] admin.admin /opt/iobroker/node_modules/iobroker.admin/admin/i18n/de/translations.json i18n/de/translations.json application/json
      
      upload [10] admin.admin /opt/iobroker/node_modules/iobroker.admin/admin/i18n/en/translations.json i18n/en/translations.json application/json
      
      upload [9] admin.admin /opt/iobroker/node_modules/iobroker.admin/admin/i18n/es/translations.json i18n/es/translations.json application/json
      
      upload [8] admin.admin /opt/iobroker/node_modules/iobroker.admin/admin/i18n/fr/translations.json i18n/fr/translations.json application/json
      
      upload [7] admin.admin /opt/iobroker/node_modules/iobroker.admin/admin/i18n/it/translations.json i18n/it/translations.json application/json
      
      upload [6] admin.admin /opt/iobroker/node_modules/iobroker.admin/admin/i18n/nl/translations.json i18n/nl/translations.json application/json
      
      upload [5] admin.admin /opt/iobroker/node_modules/iobroker.admin/admin/i18n/pl/translations.json i18n/pl/translations.json application/json
      
      upload [4] admin.admin /opt/iobroker/node_modules/iobroker.admin/admin/i18n/pt/translations.json i18n/pt/translations.json application/json
      
      upload [3] admin.admin /opt/iobroker/node_modules/iobroker.admin/admin/i18n/ru/translations.json i18n/ru/translations.json application/json
      
      upload [2] admin.admin /opt/iobroker/node_modules/iobroker.admin/admin/i18n/uk/translations.json i18n/uk/translations.json application/json
      
      upload [1] admin.admin /opt/iobroker/node_modules/iobroker.admin/admin/i18n/zh-cn/translations.json i18n/zh-cn/translations.json application/json
      
      upload [0] admin.admin /opt/iobroker/node_modules/iobroker.admin/admin/jsonConfig.json5 jsonConfig.json5 application/json5
      
      Updating objects from io-package.json for adapter "admin" with version "7.6.3"
      
      Update "system.adapter.admin.0"
      
      

      Weis jemand, was bei mir schief läuft und wie ich es ggf. beheben kann?

      Edit2:
      Okay, scheint doch geklappt zu haben.

      Server is running on http://localhost:8081
      Successfully upgraded admin to version 7.6.4
      
      ---------------------------------------------------
      admin wurde erfolgreich auf 7.6.4 aktualisiert
      

      Habe es nun direkt im Adpater aktualisiert und nicht als "manueller" Download aus "Link".
      Also Adapter -> Bestimmte Version installieren.

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

      @fenriswolf said in Error admin0 (783) socket.io [init] No user found in cookies:

      Bei mir tritt ebenfalls dieser Fehler auf.
      Nach Update von admin.0 von 7.4.x auf 7.6.3

      Auch bei mir. Hatte heute Nacht "geupdatet".

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