Skip to content
  • Home
  • Aktuell
  • Tags
  • 0 Ungelesen 0
  • Kategorien
  • Unreplied
  • Beliebt
  • GitHub
  • Docu
  • Hilfe
Skins
  • Light
  • Brite
  • Cerulean
  • Cosmo
  • Flatly
  • Journal
  • Litera
  • Lumen
  • Lux
  • Materia
  • Minty
  • Morph
  • Pulse
  • Sandstone
  • Simplex
  • Sketchy
  • Spacelab
  • United
  • Yeti
  • Zephyr
  • Dark
  • Cyborg
  • Darkly
  • Quartz
  • Slate
  • Solar
  • Superhero
  • Vapor

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

Community Forum

donate donate
  1. ioBroker Community Home
  2. Deutsch
  3. Skripten / Logik
  4. Verbesserung erfahren Blockly/Javascript/KI/AI

NEWS

  • Jahresrückblick 2025 – unser neuer Blogbeitrag ist online! ✨
    BluefoxB
    Bluefox
    17
    1
    3.0k

  • Neuer Blogbeitrag: Monatsrückblick - Dezember 2025 🎄
    BluefoxB
    Bluefox
    13
    1
    1.1k

  • Weihnachtsangebot 2025! 🎄
    BluefoxB
    Bluefox
    25
    1
    2.4k

Verbesserung erfahren Blockly/Javascript/KI/AI

Geplant Angeheftet Gesperrt Verschoben Skripten / Logik
48 Beiträge 11 Kommentatoren 1.3k Aufrufe 10 Watching
  • Älteste zuerst
  • Neuste zuerst
  • Meiste Stimmen
Antworten
  • In einem neuen Thema antworten
Anmelden zum Antworten
Dieses Thema wurde gelöscht. Nur Nutzer mit entsprechenden Rechten können es sehen.
  • liv-in-skyL liv-in-sky

    ich finde es gut, sich mit ki und iobroker zu beschäftigen - iobroker ist eine hausautomatisierung und es sollte für user einfach sein, alles zu realisieren, was man sich vorstellt - ohne sich lange mit programmierung zu beschäftigen. man sollte ki momentan noch nicht als allwissend verstehen, aber auch nicht in den boden reden. wenn das projekt iobroker weiterhin erfolgreich sein möchte, wird man um ki nicht "rumkommen".

    stattdessen wäre es wohl besser eine empfehlungsliste oder sowas zu machen: z.b.

    • beginne nicht großen (langen) code zu generieren, sondern benutze es anfangs für funktionen
    • evtl kann man eine liste erstellen wie oben erwähnt (achte darauf, das du den heartbeat nicht zu hoch setzt, .....
    • was sind empfohlende ki's (claude code, gemini, ..) für die programmierung

    usw

    wenn ich mir ansehe, was openclaw kann, wird es nicht lange dauern, bis man ein system zur hausautomatisierung sehen wird - unsere adapter codes sind offenzugänglich - warum sollte ein neues "erfundenes" system das nicht nutzen können und iobroker einfach ersetzen ?

    Jey CeeJ Online
    Jey CeeJ Online
    Jey Cee
    Developer
    schrieb am zuletzt editiert von
    #21

    @liv-in-sky sagte in Verbesserung erfahren Blockly/Javascript/KI/AI:

    wenn ich mir ansehe, was openclaw kann, wird es nicht lange dauern, bis man ein system zur hausautomatisierung sehen wird - unsere adapter codes sind offenzugänglich - warum sollte ein neues "erfundenes" system das nicht nutzen können und iobroker einfach ersetzen ?

    Sicher das geht jetzt schon. Nur weil es geht ist es nicht gleich Sinnvoll.
    Was für mich nach wie vor dagegen Spricht:

    • Ressourcen Verschwendung
    • Wiederholungsgenauigkeit nicht gegeben: man kann 5 mal das selbe rein werfen und 5 verschiedene Ergebnisse bekommen.

    @asgothian hat hier schon ein Punkt das was raus kommt kann gut aussehen ohne es zu sein.
    Ich denke hier muss man Unterscheiden zwischen Einzelfall und breiter Masse. Es gibt Fehler die treten bei einem von 100 Benutzern auf, wenn das 1000 nutzen ist das schlecht. Wenn es einer nutzt merkt er das vielleicht nie.
    Und das passiert auch bei code der von Menschen geschrieben wird.

    Ich Denke wir sollten damit Pragmatisch umgehen und erstmal bewerten (lernen) wann ist das vertretbar und wann nicht.

    Persönlicher Support
    Spenden -> paypal.me/J3YC33

    1 Antwort Letzte Antwort
    0
    • OliverIOO Offline
      OliverIOO Offline
      OliverIO
      schrieb am zuletzt editiert von OliverIO
      #22

      Ich habe das Skript mit dem folgenden, prompt mal bei ChatGPT hineingeworfen

      Bitte analysiere zunächst das Folgende Skript
      Dann liefere mir bitte das Skript ergänzt mit Kommentaren, so dass es ein Anfänger verstehen kann. In einem weiteren Schritt liefere mir bitte ein verbessertes Skript mit der Analyse, warum du welche Punkte verbessert hast
      

      Als Ergebnis kam folgendes

      1. Analyse: Warum „das Folgende“ so vermutlich nicht klappt

      Ich gehe davon aus, dass das ein ioBroker-JavaScript (JS-Adapter) Script ist (wegen getState(), setState(), on(), schedule(), formatDate()).

      A) Harte Syntaxfehler (Script startet dann gar nicht)
      1. formatTime() gibt einen Template-String ohne Backticks zurück

      return ${h}:${m < 10 ? '0' + m : m};

      Das ist ungültiges JavaScript. Template-Literals brauchen Backticks:

      return ${h}:${m < 10 ? '0' + m : m};

      2.	In deinem Code sind fremde Textzeilen mitten im JS:
      
      •	Copy to Clipboard
      •	Bullet-Lines wie •  // "Fertig um" Uhrzeit berechnen
      

      Das sind keine Kommentare, sondern normaler Text → Syntax Error.
      Wenn du das behalten willst, muss es als Kommentar geschrieben werden (// ... oder /* ... */).

      B) Logik-/Robustheitsprobleme (läuft ggf., aber liefert falsche/instabile Werte)
      3. formatDate() ist nicht Standard-JS
      Im ioBroker-JS-Adapter existiert formatDate() typischerweise, aber nur dort. Wenn du das Script woanders testest, knallt es. (In ioBroker ist es ok.)
      4. Trigger reagieren nicht auf SOC/Speichergröße
      Du berechnest Restladezeit mit soc und speicherMax, aber dein on() triggert nur auf:

      •	PV, Smartmeter, Batterie-Leistung
      

      Wenn sich SOC ändert (häufig), kann die Anzeige „hinterherhinken“.

      5.	Akkumulator „Tagesverbrauch“: fest 10s gerechnet, aber ohne Drift-Korrektur
      

      setInterval(..., 10000) ist nicht garantiert exakt. Wenn das System hängt, stimmt der Tageswert langsam nicht mehr. Besser: echte Zeitdifferenz messen.
      6. getState(...).val || 0 kann echte 0 überdecken?
      Hier ist es okay (0 bleibt 0), aber bei NaN/Strings wird es stillschweigend zu 0. Besser: sauber parsen und validieren.
      7. Einheiten & Vorzeichen sind nur korrekt, wenn deine Quellen wirklich so liefern

      •	netPower: positiv Bezug, negativ Einspeisung (du schreibst das so)
      •	batPower: positiv Laden, negativ Entladen (du schreibst das so)
      

      Wenn ein Gerät das anders liefert, sind alle Flüsse „gespiegelt“.

      ⸻

      1. Dein Skript – ergänzt und so kommentiert, dass Anfänger es verstehen

      Ich habe nur die offensichtlichen Syntaxprobleme repariert und Kommentare ergänzt, ohne die Struktur stark zu verändern.

      /*
      Name: PV-Energiemaster Pro
      Zweck: Echtzeit-Berechnung von Hausverbrauch, Autarkie & Speicher-Logik
      inkl. täglichem Reset für saubere Grafana-Balken
      */

      // --- KONFIGURATION: IDs ---
      const ID_BATTERIE_W = 'modbus.0.inputRegisters.100.842_Battery_Power_(System)';
      const ID_PV_DACH_W = 'solax.0.data.acpower';
      const ID_SMARTMETER_W = 'smartmeter.0.1-0:16_7_0__255.value';
      const ID_SOC = 'modbus.0.inputRegisters.100.843_Battery_State_of_Charge_(System)';
      const ID_SPEICHER_KWH = '0_userdata.0.Energie.PV.Speichergroesse';

      const PATH = '0_userdata.0.Energie.PV.';

      // --- HILFSFUNKTIONEN ---

      /**

      • Wandelt Minuten (z.B. 85.3) in "1:25" um.
      • Wenn Minuten nicht sinnvoll sind, geben wir "n. n." zurück.
        */
        function formatTime(minutes) {
        if (isNaN(minutes) || minutes <= 0 || !isFinite(minutes)) return "n. n.";

      const h = Math.floor(minutes / 60);
      const m = Math.floor(minutes % 60);

      // WICHTIG: Template-String braucht Backticks: ...
      return ${h}:${m < 10 ? '0' + m : m};
      }

      /**

      • Kern-Logik: Berechnet alle Energieflüsse
        */
        function calculateEnergyMaster() {
        // 1) Rohdaten abrufen (Watt bzw. % / kWh)
        const pvPower = getState(ID_PV_DACH_W).val || 0;

      // Smartmeter: + = Netzbezug, - = Einspeisung (laut Kommentar)
      const netPower = getState(ID_SMARTMETER_W).val || 0;

      // Batterie: + = Laden, - = Entladen (laut Kommentar)
      const batPower = getState(ID_BATTERIE_W).val || 0;

      const soc = getState(ID_SOC).val || 0; // Prozent
      const speicherMax = getState(ID_SPEICHER_KWH).val || 0; // kWh

      // 2) Zerlegung der Flüsse in „nur positive“ Teilgrößen
      const netzBezug = netPower > 0 ? netPower : 0;
      const einspeisung = netPower < 0 ? Math.abs(netPower) : 0;

      const batterieLaden = batPower > 0 ? batPower : 0;
      const batterieEntladen = batPower < 0 ? Math.abs(batPower) : 0;

      // 3) Hausverbrauch berechnen (physikalisch konsistent)
      // Idee: Alles was ins Haus "rein kommt" minus alles was "raus geht"
      // Formel: PV + Netzbezug + Batterie-Entladung - Batterie-Ladung - Einspeisung
      let hausverbrauch = pvPower + netzBezug + batterieEntladen - batterieLaden - einspeisung;
      if (hausverbrauch < 0) hausverbrauch = 0;

      // 4) Autarkie & Eigenverbrauch
      // Autarkie: wie viel % des Hausverbrauchs NICHT aus dem Netz kommt
      let autarkie = 0;
      if (hausverbrauch > 0) {
      autarkie = Math.min(100, (1 - (netzBezug / hausverbrauch)) * 100);
      }

      // Eigenverbrauch: PV-Leistung, die nicht eingespeist wird (vereinfacht)
      const eigenverbrauch = Math.max(0, pvPower - einspeisung);

      // 5) Speicher-Restlaufzeit (wie lange bis Akku voll, wenn er lädt)
      const ladestandKWh = (speicherMax * soc) / 100;
      const leerspeicherKWh = Math.max(0, speicherMax - ladestandKWh);

      let restladezeitMin = 0;
      let restladezeitSec = 0;
      let fertigUm = "n. n.";

      // Nur berechnen, wenn wirklich geladen wird (und nicht nur „Rauschen“)
      if (batterieLaden > 50) {
      // batterieLaden ist Watt, daher /1000 => kW
      // Zeit in Stunden = kWh / kW
      // dann *60 für Minuten, *3600 für Sekunden
      restladezeitMin = (leerspeicherKWh / (batterieLaden / 1000)) * 60;
      restladezeitSec = (leerspeicherKWh / (batterieLaden / 1000)) * 3600;

      // "Fertig um" Uhrzeit berechnen
      // (formatDate ist im ioBroker-JS-Adapter verfügbar)
      let now = new Date();
      now.setSeconds(now.getSeconds() + restladezeitSec);
      fertigUm = formatDate(now, "hh:mm");
      

      }

      // 6) Datenpunkte schreiben (Echtzeit)
      setState(PATH + 'Batterieleistung', batPower, true);
      setState(PATH + 'PV_Dach', pvPower, true);
      setState(PATH + 'L1_L3', netPower, true);
      setState(PATH + 'Netzbezug', netzBezug, true);
      setState(PATH + 'Einspeisung', einspeisung, true);
      setState(PATH + 'Hausverbrauch', hausverbrauch, true);
      setState(PATH + 'Autarkie', Math.round(autarkie), true);
      setState(PATH + 'Eigenverbrauch', eigenverbrauch, true);

      setState(PATH + 'lade_kwh', parseFloat(ladestandKWh.toFixed(1)), true);
      setState(PATH + 'Restladezeit', formatTime(restladezeitMin), true);
      setState(PATH + 'Restladezeit_sec', Math.round(restladezeitSec), true);
      setState(PATH + 'Restladezeit_final', fertigUm, true);
      }

      // --- ZEITPLANUNG & TRIGGER ---

      // Trigger: Sofort rechnen bei jeder Änderung der Hardware-Werte
      on(
      { id: [ID_PV_DACH_W, ID_SMARTMETER_W, ID_BATTERIE_W], change: "any" },
      calculateEnergyMaster
      );

      // Zähler: Alle 10 Sekunden den Energie-Akkumulator füllen (Wh-Schritte)
      setInterval(() => {
      const haus = getState(PATH + 'Hausverbrauch').val || 0;
      const oldVal = getState(PATH + 'Tagesverbrauch').val || 0;

      // Watt / 360 = Wh-Anteil für 10 Sekunden (weil 10s = 1/360 h)
      setState(PATH + 'Tagesverbrauch', oldVal + (haus / 360), true);
      }, 10000);

      // Reset: Jeden Tag um 00:00 Uhr den Tagesverbrauch für Grafana nullen
      schedule("0 0 * * *", () => {
      setState(PATH + 'Tagesverbrauch', 0, true);
      });

      // Initialer Start beim Skript-Speichern
      calculateEnergyMaster();

      ⸻

      1. Verbessertes Skript + Analyse, warum diese Punkte besser sind

      Was ich verbessert habe (kurz & konkret)
      1. Sauberes Einlesen von Zahlen: readNumber() verhindert NaN, Strings, null.
      2. Trigger erweitert: SOC und Speichergröße triggern ebenfalls → Anzeigen bleiben aktuell.
      3. Debounce: Wenn PV/Smartmeter/Batterie sehr häufig ändern, wird nicht 50×/Sek gerechnet.
      4. Tagesverbrauch korrekt nach echter Zeitdifferenz statt „fix 10 Sekunden“ → weniger Drift.
      5. State-Initialisierung (setObjectNotExists) optional, aber extrem hilfreich, damit Grafana/Objekte sauber sind.
      6. Schutz gegen Division durch 0 / unrealistische Werte (z.B. Speichergröße 0).

      ⸻

      Verbessertes Skript (robuster & „produktionsnäher“)

      /*
      PV-Energiemaster Pro (improved)

      • Robustere Zahlenverarbeitung
      • Aktuellere Trigger
      • Debounce gegen Rechen-Spam
      • Tagesverbrauch über echte Zeitdifferenz
        */

      const ID_BATTERIE_W = 'modbus.0.inputRegisters.100.842_Battery_Power_(System)';
      const ID_PV_DACH_W = 'solax.0.data.acpower';
      const ID_SMARTMETER_W = 'smartmeter.0.1-0:16_7_0__255.value';
      const ID_SOC = 'modbus.0.inputRegisters.100.843_Battery_State_of_Charge_(System)';
      const ID_SPEICHER_KWH = '0_userdata.0.Energie.PV.Speichergroesse';

      const PATH = '0_userdata.0.Energie.PV.';

      // -------------------- Hilfsfunktionen --------------------

      /** Liest einen Datenpunkt als Zahl und liefert bei Problemen defaultValue zurück. */
      function readNumber(id, defaultValue = 0) {
      const st = getState(id);
      const v = st ? st.val : defaultValue;
      const n = Number(v);
      return Number.isFinite(n) ? n : defaultValue;
      }

      /** Wie zuvor: Minuten -> "h:mm" */
      function formatTime(minutes) {
      if (!Number.isFinite(minutes) || minutes <= 0) return "n. n.";
      const h = Math.floor(minutes / 60);
      const m = Math.floor(minutes % 60);
      return ${h}:${m < 10 ? '0' + m : m};
      }

      /** Kleiner Debounce: fasst viele Änderungen zusammen und rechnet max. alle X ms. */
      function debounce(fn, waitMs) {
      let t = null;
      return function () {
      if (t) clearTimeout(t);
      t = setTimeout(() => fn(), waitMs);
      };
      }

      // -------------------- (Optional) State-Objekte anlegen --------------------
      // Vorteil: Du siehst die States sauber im Objektbaum, inkl. Typ/Unit/Role.
      // Wenn du das nicht willst, kannst du diesen Block weglassen.
      function ensureStates() {
      setObjectNotExists(PATH + 'Batterieleistung', { type: 'state', common: { name: 'Batterieleistung', type: 'number', role: 'value.power', unit: 'W', read: true, write: true }, native: {} });
      setObjectNotExists(PATH + 'PV_Dach', { type: 'state', common: { name: 'PV Dach', type: 'number', role: 'value.power', unit: 'W', read: true, write: true }, native: {} });
      setObjectNotExists(PATH + 'L1_L3', { type: 'state', common: { name: 'Netzleistung', type: 'number', role: 'value.power', unit: 'W', read: true, write: true }, native: {} });
      setObjectNotExists(PATH + 'Netzbezug', { type: 'state', common: { name: 'Netzbezug', type: 'number', role: 'value.power', unit: 'W', read: true, write: true }, native: {} });
      setObjectNotExists(PATH + 'Einspeisung', { type: 'state', common: { name: 'Einspeisung', type: 'number', role: 'value.power', unit: 'W', read: true, write: true }, native: {} });
      setObjectNotExists(PATH + 'Hausverbrauch', { type: 'state', common: { name: 'Hausverbrauch', type: 'number', role: 'value.power', unit: 'W', read: true, write: true }, native: {} });
      setObjectNotExists(PATH + 'Autarkie', { type: 'state', common: { name: 'Autarkie', type: 'number', role: 'value', unit: '%', read: true, write: true }, native: {} });
      setObjectNotExists(PATH + 'Eigenverbrauch', { type: 'state', common: { name: 'Eigenverbrauch', type: 'number', role: 'value.power', unit: 'W', read: true, write: true }, native: {} });
      setObjectNotExists(PATH + 'lade_kwh', { type: 'state', common: { name: 'Ladestand', type: 'number', role: 'value.energy', unit: 'kWh', read: true, write: true }, native: {} });
      setObjectNotExists(PATH + 'Restladezeit', { type: 'state', common: { name: 'Restladezeit', type: 'string', role: 'text', read: true, write: true }, native: {} });
      setObjectNotExists(PATH + 'Restladezeit_sec', { type: 'state', common: { name: 'Restladezeit (s)', type: 'number', role: 'value', unit: 's', read: true, write: true }, native: {} });
      setObjectNotExists(PATH + 'Restladezeit_final',{ type: 'state', common:{ name:'Fertig um', type:'string', role:'text', read:true, write:true }, native:{} });
      setObjectNotExists(PATH + 'Tagesverbrauch', { type: 'state', common: { name: 'Tagesverbrauch', type: 'number', role: 'value.energy', unit: 'Wh', read: true, write: true }, native: {} });
      }

      ensureStates();

      // -------------------- Kernlogik --------------------

      function calculateEnergyMaster() {
      // 1) Rohdaten (robust eingelesen)
      const pvPower = readNumber(ID_PV_DACH_W, 0);
      const netPower = readNumber(ID_SMARTMETER_W, 0); // + Bezug, - Einspeisung
      const batPower = readNumber(ID_BATTERIE_W, 0); // + Laden, - Entladen
      const soc = readNumber(ID_SOC, 0); // %
      const speicherMax = readNumber(ID_SPEICHER_KWH, 0); // kWh

      // 2) Fluss-Zerlegung
      const netzBezug = netPower > 0 ? netPower : 0;
      const einspeisung = netPower < 0 ? -netPower : 0;

      const batterieLaden = batPower > 0 ? batPower : 0;
      const batterieEntladen = batPower < 0 ? -batPower : 0;

      // 3) Hausverbrauch
      let hausverbrauch = pvPower + netzBezug + batterieEntladen - batterieLaden - einspeisung;
      if (!Number.isFinite(hausverbrauch) || hausverbrauch < 0) hausverbrauch = 0;

      // 4) Autarkie & Eigenverbrauch
      const autarkie = hausverbrauch > 0
      ? Math.max(0, Math.min(100, (1 - (netzBezug / hausverbrauch)) * 100))
      : 0;

      const eigenverbrauch = Math.max(0, pvPower - einspeisung);

      // 5) Speicher-Restladezeit
      const speicherOk = speicherMax > 0 && soc >= 0 && soc <= 100;

      const ladestandKWh = speicherOk ? (speicherMax * soc) / 100 : 0;
      const leerspeicherKWh = speicherOk ? Math.max(0, speicherMax - ladestandKWh) : 0;

      let restladezeitMin = 0;
      let restladezeitSec = 0;
      let fertigUm = "n. n.";

      if (batterieLaden > 50 && speicherOk) {
      const ladeLeistungKW = batterieLaden / 1000;
      // Verhindert Division durch 0
      if (ladeLeistungKW > 0) {
      const hours = leerspeicherKWh / ladeLeistungKW;
      restladezeitMin = hours * 60;
      restladezeitSec = hours * 3600;

        const now = new Date();
        now.setSeconds(now.getSeconds() + restladezeitSec);
        fertigUm = formatDate(now, "hh:mm");
      }
      

      }

      // 6) Schreiben
      setState(PATH + 'Batterieleistung', batPower, true);
      setState(PATH + 'PV_Dach', pvPower, true);
      setState(PATH + 'L1_L3', netPower, true);
      setState(PATH + 'Netzbezug', netzBezug, true);
      setState(PATH + 'Einspeisung', einspeisung, true);
      setState(PATH + 'Hausverbrauch', hausverbrauch, true);
      setState(PATH + 'Autarkie', Math.round(autarkie), true);
      setState(PATH + 'Eigenverbrauch', eigenverbrauch, true);

      setState(PATH + 'lade_kwh', Number(ladestandKWh.toFixed(1)), true);
      setState(PATH + 'Restladezeit', formatTime(restladezeitMin), true);
      setState(PATH + 'Restladezeit_sec', Math.round(restladezeitSec), true);
      setState(PATH + 'Restladezeit_final', fertigUm, true);
      }

      // Debounced Version (z.B. 300 ms): verhindert „Rechenspam“ bei schnell flackernden Werten
      const calculateEnergyMasterDebounced = debounce(calculateEnergyMaster, 300);

      // -------------------- Trigger & Tagesverbrauch --------------------

      // Trigger erweitert: auch SOC und Speichergröße
      on(
      { id: [ID_PV_DACH_W, ID_SMARTMETER_W, ID_BATTERIE_W, ID_SOC, ID_SPEICHER_KWH], change: "any" },
      calculateEnergyMasterDebounced
      );

      // Tagesverbrauch: Wh über echte Zeitdifferenz integrieren
      let lastTs = Date.now();

      setInterval(() => {
      const now = Date.now();
      const dtSec = (now - lastTs) / 1000;
      lastTs = now;

      // aktuelle Leistung in W
      const hausW = readNumber(PATH + 'Hausverbrauch', 0);
      const oldWh = readNumber(PATH + 'Tagesverbrauch', 0);

      // Wh = W * Stunden ; Stunden = Sekunden / 3600
      const addWh = hausW * (dtSec / 3600);

      setState(PATH + 'Tagesverbrauch', oldWh + addWh, true);
      }, 10000);

      // Reset täglich 00:00
      schedule("0 0 * * *", () => {
      setState(PATH + 'Tagesverbrauch', 0, true);
      });

      // Initial
      calculateEnergyMaster();

      ⸻

      Wenn du mir sagst, welcher Adapter/Wechselrichter/Smartmeter das genau ist (SolaX + Modbus ist klar, aber Smartmeter-Typ und ob „Netzleistung“ wirklich so rum gepolt ist), kann ich dir zusätzlich eine Plausibilitätsprüfung einbauen (z.B. „Hausverbrauch darf nicht kleiner als 0 sein“, „PV >= Eigenverbrauch“, Warn-Log wenn Vorzeichen nicht passen).

      Zum Schluss hab ich noch gefragt, für was das Skript eigentlich da ist und als Antwort kam das folgende:

      Das Skript dient dazu, alle Energieflüsse einer Photovoltaikanlage in Echtzeit rechnerisch korrekt aufzubereiten.
      Es berechnet aus PV-Leistung, Netzbezug/-einspeisung und Batterieladung den tatsächlichen Hausverbrauch, die Autarkiequote sowie den Eigenverbrauch.
      Zusätzlich ermittelt es den **aktuellen Ladezustand des Speichers inkl. Restladezeit („fertig um“) **.
      Ein integrierter Tageszähler mit Mitternachts-Reset liefert saubere Verbrauchswerte für Grafana-Diagramme.

      Da ich mich aktuell auf der anderen Seite der Erde befinde und mehr oder weniger nur das Tablett zur Verfügung hab, könnte ich das selber nicht ausprobieren ob das lauffähig ist zu vermuten ist, dass man da noch mal 2/3 Runden nachbessern müsste

      Ps, sehe gerade, dass das Forum Zeichen verschluckt.(bspw backticks). Auch habe ich wohl irgendetwas zu viel kopiert. Diese Hinweise einfach ignorieren.

      Meine Adapter und Widgets
      TVProgram, SqueezeboxRPC, OpenLiga, RSSFeed, MyTime,, pi-hole2, vis-json-template, skiinfo, vis-mapwidgets, vis-2-widgets-rssfeed
      Links im Profil

      HomoranH crunchipC 2 Antworten Letzte Antwort
      0
      • OliverIOO OliverIO

        Ich habe das Skript mit dem folgenden, prompt mal bei ChatGPT hineingeworfen

        Bitte analysiere zunächst das Folgende Skript
        Dann liefere mir bitte das Skript ergänzt mit Kommentaren, so dass es ein Anfänger verstehen kann. In einem weiteren Schritt liefere mir bitte ein verbessertes Skript mit der Analyse, warum du welche Punkte verbessert hast
        

        Als Ergebnis kam folgendes

        1. Analyse: Warum „das Folgende“ so vermutlich nicht klappt

        Ich gehe davon aus, dass das ein ioBroker-JavaScript (JS-Adapter) Script ist (wegen getState(), setState(), on(), schedule(), formatDate()).

        A) Harte Syntaxfehler (Script startet dann gar nicht)
        1. formatTime() gibt einen Template-String ohne Backticks zurück

        return ${h}:${m < 10 ? '0' + m : m};

        Das ist ungültiges JavaScript. Template-Literals brauchen Backticks:

        return ${h}:${m < 10 ? '0' + m : m};

        2.	In deinem Code sind fremde Textzeilen mitten im JS:
        
        •	Copy to Clipboard
        •	Bullet-Lines wie •  // "Fertig um" Uhrzeit berechnen
        

        Das sind keine Kommentare, sondern normaler Text → Syntax Error.
        Wenn du das behalten willst, muss es als Kommentar geschrieben werden (// ... oder /* ... */).

        B) Logik-/Robustheitsprobleme (läuft ggf., aber liefert falsche/instabile Werte)
        3. formatDate() ist nicht Standard-JS
        Im ioBroker-JS-Adapter existiert formatDate() typischerweise, aber nur dort. Wenn du das Script woanders testest, knallt es. (In ioBroker ist es ok.)
        4. Trigger reagieren nicht auf SOC/Speichergröße
        Du berechnest Restladezeit mit soc und speicherMax, aber dein on() triggert nur auf:

        •	PV, Smartmeter, Batterie-Leistung
        

        Wenn sich SOC ändert (häufig), kann die Anzeige „hinterherhinken“.

        5.	Akkumulator „Tagesverbrauch“: fest 10s gerechnet, aber ohne Drift-Korrektur
        

        setInterval(..., 10000) ist nicht garantiert exakt. Wenn das System hängt, stimmt der Tageswert langsam nicht mehr. Besser: echte Zeitdifferenz messen.
        6. getState(...).val || 0 kann echte 0 überdecken?
        Hier ist es okay (0 bleibt 0), aber bei NaN/Strings wird es stillschweigend zu 0. Besser: sauber parsen und validieren.
        7. Einheiten & Vorzeichen sind nur korrekt, wenn deine Quellen wirklich so liefern

        •	netPower: positiv Bezug, negativ Einspeisung (du schreibst das so)
        •	batPower: positiv Laden, negativ Entladen (du schreibst das so)
        

        Wenn ein Gerät das anders liefert, sind alle Flüsse „gespiegelt“.

        ⸻

        1. Dein Skript – ergänzt und so kommentiert, dass Anfänger es verstehen

        Ich habe nur die offensichtlichen Syntaxprobleme repariert und Kommentare ergänzt, ohne die Struktur stark zu verändern.

        /*
        Name: PV-Energiemaster Pro
        Zweck: Echtzeit-Berechnung von Hausverbrauch, Autarkie & Speicher-Logik
        inkl. täglichem Reset für saubere Grafana-Balken
        */

        // --- KONFIGURATION: IDs ---
        const ID_BATTERIE_W = 'modbus.0.inputRegisters.100.842_Battery_Power_(System)';
        const ID_PV_DACH_W = 'solax.0.data.acpower';
        const ID_SMARTMETER_W = 'smartmeter.0.1-0:16_7_0__255.value';
        const ID_SOC = 'modbus.0.inputRegisters.100.843_Battery_State_of_Charge_(System)';
        const ID_SPEICHER_KWH = '0_userdata.0.Energie.PV.Speichergroesse';

        const PATH = '0_userdata.0.Energie.PV.';

        // --- HILFSFUNKTIONEN ---

        /**

        • Wandelt Minuten (z.B. 85.3) in "1:25" um.
        • Wenn Minuten nicht sinnvoll sind, geben wir "n. n." zurück.
          */
          function formatTime(minutes) {
          if (isNaN(minutes) || minutes <= 0 || !isFinite(minutes)) return "n. n.";

        const h = Math.floor(minutes / 60);
        const m = Math.floor(minutes % 60);

        // WICHTIG: Template-String braucht Backticks: ...
        return ${h}:${m < 10 ? '0' + m : m};
        }

        /**

        • Kern-Logik: Berechnet alle Energieflüsse
          */
          function calculateEnergyMaster() {
          // 1) Rohdaten abrufen (Watt bzw. % / kWh)
          const pvPower = getState(ID_PV_DACH_W).val || 0;

        // Smartmeter: + = Netzbezug, - = Einspeisung (laut Kommentar)
        const netPower = getState(ID_SMARTMETER_W).val || 0;

        // Batterie: + = Laden, - = Entladen (laut Kommentar)
        const batPower = getState(ID_BATTERIE_W).val || 0;

        const soc = getState(ID_SOC).val || 0; // Prozent
        const speicherMax = getState(ID_SPEICHER_KWH).val || 0; // kWh

        // 2) Zerlegung der Flüsse in „nur positive“ Teilgrößen
        const netzBezug = netPower > 0 ? netPower : 0;
        const einspeisung = netPower < 0 ? Math.abs(netPower) : 0;

        const batterieLaden = batPower > 0 ? batPower : 0;
        const batterieEntladen = batPower < 0 ? Math.abs(batPower) : 0;

        // 3) Hausverbrauch berechnen (physikalisch konsistent)
        // Idee: Alles was ins Haus "rein kommt" minus alles was "raus geht"
        // Formel: PV + Netzbezug + Batterie-Entladung - Batterie-Ladung - Einspeisung
        let hausverbrauch = pvPower + netzBezug + batterieEntladen - batterieLaden - einspeisung;
        if (hausverbrauch < 0) hausverbrauch = 0;

        // 4) Autarkie & Eigenverbrauch
        // Autarkie: wie viel % des Hausverbrauchs NICHT aus dem Netz kommt
        let autarkie = 0;
        if (hausverbrauch > 0) {
        autarkie = Math.min(100, (1 - (netzBezug / hausverbrauch)) * 100);
        }

        // Eigenverbrauch: PV-Leistung, die nicht eingespeist wird (vereinfacht)
        const eigenverbrauch = Math.max(0, pvPower - einspeisung);

        // 5) Speicher-Restlaufzeit (wie lange bis Akku voll, wenn er lädt)
        const ladestandKWh = (speicherMax * soc) / 100;
        const leerspeicherKWh = Math.max(0, speicherMax - ladestandKWh);

        let restladezeitMin = 0;
        let restladezeitSec = 0;
        let fertigUm = "n. n.";

        // Nur berechnen, wenn wirklich geladen wird (und nicht nur „Rauschen“)
        if (batterieLaden > 50) {
        // batterieLaden ist Watt, daher /1000 => kW
        // Zeit in Stunden = kWh / kW
        // dann *60 für Minuten, *3600 für Sekunden
        restladezeitMin = (leerspeicherKWh / (batterieLaden / 1000)) * 60;
        restladezeitSec = (leerspeicherKWh / (batterieLaden / 1000)) * 3600;

        // "Fertig um" Uhrzeit berechnen
        // (formatDate ist im ioBroker-JS-Adapter verfügbar)
        let now = new Date();
        now.setSeconds(now.getSeconds() + restladezeitSec);
        fertigUm = formatDate(now, "hh:mm");
        

        }

        // 6) Datenpunkte schreiben (Echtzeit)
        setState(PATH + 'Batterieleistung', batPower, true);
        setState(PATH + 'PV_Dach', pvPower, true);
        setState(PATH + 'L1_L3', netPower, true);
        setState(PATH + 'Netzbezug', netzBezug, true);
        setState(PATH + 'Einspeisung', einspeisung, true);
        setState(PATH + 'Hausverbrauch', hausverbrauch, true);
        setState(PATH + 'Autarkie', Math.round(autarkie), true);
        setState(PATH + 'Eigenverbrauch', eigenverbrauch, true);

        setState(PATH + 'lade_kwh', parseFloat(ladestandKWh.toFixed(1)), true);
        setState(PATH + 'Restladezeit', formatTime(restladezeitMin), true);
        setState(PATH + 'Restladezeit_sec', Math.round(restladezeitSec), true);
        setState(PATH + 'Restladezeit_final', fertigUm, true);
        }

        // --- ZEITPLANUNG & TRIGGER ---

        // Trigger: Sofort rechnen bei jeder Änderung der Hardware-Werte
        on(
        { id: [ID_PV_DACH_W, ID_SMARTMETER_W, ID_BATTERIE_W], change: "any" },
        calculateEnergyMaster
        );

        // Zähler: Alle 10 Sekunden den Energie-Akkumulator füllen (Wh-Schritte)
        setInterval(() => {
        const haus = getState(PATH + 'Hausverbrauch').val || 0;
        const oldVal = getState(PATH + 'Tagesverbrauch').val || 0;

        // Watt / 360 = Wh-Anteil für 10 Sekunden (weil 10s = 1/360 h)
        setState(PATH + 'Tagesverbrauch', oldVal + (haus / 360), true);
        }, 10000);

        // Reset: Jeden Tag um 00:00 Uhr den Tagesverbrauch für Grafana nullen
        schedule("0 0 * * *", () => {
        setState(PATH + 'Tagesverbrauch', 0, true);
        });

        // Initialer Start beim Skript-Speichern
        calculateEnergyMaster();

        ⸻

        1. Verbessertes Skript + Analyse, warum diese Punkte besser sind

        Was ich verbessert habe (kurz & konkret)
        1. Sauberes Einlesen von Zahlen: readNumber() verhindert NaN, Strings, null.
        2. Trigger erweitert: SOC und Speichergröße triggern ebenfalls → Anzeigen bleiben aktuell.
        3. Debounce: Wenn PV/Smartmeter/Batterie sehr häufig ändern, wird nicht 50×/Sek gerechnet.
        4. Tagesverbrauch korrekt nach echter Zeitdifferenz statt „fix 10 Sekunden“ → weniger Drift.
        5. State-Initialisierung (setObjectNotExists) optional, aber extrem hilfreich, damit Grafana/Objekte sauber sind.
        6. Schutz gegen Division durch 0 / unrealistische Werte (z.B. Speichergröße 0).

        ⸻

        Verbessertes Skript (robuster & „produktionsnäher“)

        /*
        PV-Energiemaster Pro (improved)

        • Robustere Zahlenverarbeitung
        • Aktuellere Trigger
        • Debounce gegen Rechen-Spam
        • Tagesverbrauch über echte Zeitdifferenz
          */

        const ID_BATTERIE_W = 'modbus.0.inputRegisters.100.842_Battery_Power_(System)';
        const ID_PV_DACH_W = 'solax.0.data.acpower';
        const ID_SMARTMETER_W = 'smartmeter.0.1-0:16_7_0__255.value';
        const ID_SOC = 'modbus.0.inputRegisters.100.843_Battery_State_of_Charge_(System)';
        const ID_SPEICHER_KWH = '0_userdata.0.Energie.PV.Speichergroesse';

        const PATH = '0_userdata.0.Energie.PV.';

        // -------------------- Hilfsfunktionen --------------------

        /** Liest einen Datenpunkt als Zahl und liefert bei Problemen defaultValue zurück. */
        function readNumber(id, defaultValue = 0) {
        const st = getState(id);
        const v = st ? st.val : defaultValue;
        const n = Number(v);
        return Number.isFinite(n) ? n : defaultValue;
        }

        /** Wie zuvor: Minuten -> "h:mm" */
        function formatTime(minutes) {
        if (!Number.isFinite(minutes) || minutes <= 0) return "n. n.";
        const h = Math.floor(minutes / 60);
        const m = Math.floor(minutes % 60);
        return ${h}:${m < 10 ? '0' + m : m};
        }

        /** Kleiner Debounce: fasst viele Änderungen zusammen und rechnet max. alle X ms. */
        function debounce(fn, waitMs) {
        let t = null;
        return function () {
        if (t) clearTimeout(t);
        t = setTimeout(() => fn(), waitMs);
        };
        }

        // -------------------- (Optional) State-Objekte anlegen --------------------
        // Vorteil: Du siehst die States sauber im Objektbaum, inkl. Typ/Unit/Role.
        // Wenn du das nicht willst, kannst du diesen Block weglassen.
        function ensureStates() {
        setObjectNotExists(PATH + 'Batterieleistung', { type: 'state', common: { name: 'Batterieleistung', type: 'number', role: 'value.power', unit: 'W', read: true, write: true }, native: {} });
        setObjectNotExists(PATH + 'PV_Dach', { type: 'state', common: { name: 'PV Dach', type: 'number', role: 'value.power', unit: 'W', read: true, write: true }, native: {} });
        setObjectNotExists(PATH + 'L1_L3', { type: 'state', common: { name: 'Netzleistung', type: 'number', role: 'value.power', unit: 'W', read: true, write: true }, native: {} });
        setObjectNotExists(PATH + 'Netzbezug', { type: 'state', common: { name: 'Netzbezug', type: 'number', role: 'value.power', unit: 'W', read: true, write: true }, native: {} });
        setObjectNotExists(PATH + 'Einspeisung', { type: 'state', common: { name: 'Einspeisung', type: 'number', role: 'value.power', unit: 'W', read: true, write: true }, native: {} });
        setObjectNotExists(PATH + 'Hausverbrauch', { type: 'state', common: { name: 'Hausverbrauch', type: 'number', role: 'value.power', unit: 'W', read: true, write: true }, native: {} });
        setObjectNotExists(PATH + 'Autarkie', { type: 'state', common: { name: 'Autarkie', type: 'number', role: 'value', unit: '%', read: true, write: true }, native: {} });
        setObjectNotExists(PATH + 'Eigenverbrauch', { type: 'state', common: { name: 'Eigenverbrauch', type: 'number', role: 'value.power', unit: 'W', read: true, write: true }, native: {} });
        setObjectNotExists(PATH + 'lade_kwh', { type: 'state', common: { name: 'Ladestand', type: 'number', role: 'value.energy', unit: 'kWh', read: true, write: true }, native: {} });
        setObjectNotExists(PATH + 'Restladezeit', { type: 'state', common: { name: 'Restladezeit', type: 'string', role: 'text', read: true, write: true }, native: {} });
        setObjectNotExists(PATH + 'Restladezeit_sec', { type: 'state', common: { name: 'Restladezeit (s)', type: 'number', role: 'value', unit: 's', read: true, write: true }, native: {} });
        setObjectNotExists(PATH + 'Restladezeit_final',{ type: 'state', common:{ name:'Fertig um', type:'string', role:'text', read:true, write:true }, native:{} });
        setObjectNotExists(PATH + 'Tagesverbrauch', { type: 'state', common: { name: 'Tagesverbrauch', type: 'number', role: 'value.energy', unit: 'Wh', read: true, write: true }, native: {} });
        }

        ensureStates();

        // -------------------- Kernlogik --------------------

        function calculateEnergyMaster() {
        // 1) Rohdaten (robust eingelesen)
        const pvPower = readNumber(ID_PV_DACH_W, 0);
        const netPower = readNumber(ID_SMARTMETER_W, 0); // + Bezug, - Einspeisung
        const batPower = readNumber(ID_BATTERIE_W, 0); // + Laden, - Entladen
        const soc = readNumber(ID_SOC, 0); // %
        const speicherMax = readNumber(ID_SPEICHER_KWH, 0); // kWh

        // 2) Fluss-Zerlegung
        const netzBezug = netPower > 0 ? netPower : 0;
        const einspeisung = netPower < 0 ? -netPower : 0;

        const batterieLaden = batPower > 0 ? batPower : 0;
        const batterieEntladen = batPower < 0 ? -batPower : 0;

        // 3) Hausverbrauch
        let hausverbrauch = pvPower + netzBezug + batterieEntladen - batterieLaden - einspeisung;
        if (!Number.isFinite(hausverbrauch) || hausverbrauch < 0) hausverbrauch = 0;

        // 4) Autarkie & Eigenverbrauch
        const autarkie = hausverbrauch > 0
        ? Math.max(0, Math.min(100, (1 - (netzBezug / hausverbrauch)) * 100))
        : 0;

        const eigenverbrauch = Math.max(0, pvPower - einspeisung);

        // 5) Speicher-Restladezeit
        const speicherOk = speicherMax > 0 && soc >= 0 && soc <= 100;

        const ladestandKWh = speicherOk ? (speicherMax * soc) / 100 : 0;
        const leerspeicherKWh = speicherOk ? Math.max(0, speicherMax - ladestandKWh) : 0;

        let restladezeitMin = 0;
        let restladezeitSec = 0;
        let fertigUm = "n. n.";

        if (batterieLaden > 50 && speicherOk) {
        const ladeLeistungKW = batterieLaden / 1000;
        // Verhindert Division durch 0
        if (ladeLeistungKW > 0) {
        const hours = leerspeicherKWh / ladeLeistungKW;
        restladezeitMin = hours * 60;
        restladezeitSec = hours * 3600;

          const now = new Date();
          now.setSeconds(now.getSeconds() + restladezeitSec);
          fertigUm = formatDate(now, "hh:mm");
        }
        

        }

        // 6) Schreiben
        setState(PATH + 'Batterieleistung', batPower, true);
        setState(PATH + 'PV_Dach', pvPower, true);
        setState(PATH + 'L1_L3', netPower, true);
        setState(PATH + 'Netzbezug', netzBezug, true);
        setState(PATH + 'Einspeisung', einspeisung, true);
        setState(PATH + 'Hausverbrauch', hausverbrauch, true);
        setState(PATH + 'Autarkie', Math.round(autarkie), true);
        setState(PATH + 'Eigenverbrauch', eigenverbrauch, true);

        setState(PATH + 'lade_kwh', Number(ladestandKWh.toFixed(1)), true);
        setState(PATH + 'Restladezeit', formatTime(restladezeitMin), true);
        setState(PATH + 'Restladezeit_sec', Math.round(restladezeitSec), true);
        setState(PATH + 'Restladezeit_final', fertigUm, true);
        }

        // Debounced Version (z.B. 300 ms): verhindert „Rechenspam“ bei schnell flackernden Werten
        const calculateEnergyMasterDebounced = debounce(calculateEnergyMaster, 300);

        // -------------------- Trigger & Tagesverbrauch --------------------

        // Trigger erweitert: auch SOC und Speichergröße
        on(
        { id: [ID_PV_DACH_W, ID_SMARTMETER_W, ID_BATTERIE_W, ID_SOC, ID_SPEICHER_KWH], change: "any" },
        calculateEnergyMasterDebounced
        );

        // Tagesverbrauch: Wh über echte Zeitdifferenz integrieren
        let lastTs = Date.now();

        setInterval(() => {
        const now = Date.now();
        const dtSec = (now - lastTs) / 1000;
        lastTs = now;

        // aktuelle Leistung in W
        const hausW = readNumber(PATH + 'Hausverbrauch', 0);
        const oldWh = readNumber(PATH + 'Tagesverbrauch', 0);

        // Wh = W * Stunden ; Stunden = Sekunden / 3600
        const addWh = hausW * (dtSec / 3600);

        setState(PATH + 'Tagesverbrauch', oldWh + addWh, true);
        }, 10000);

        // Reset täglich 00:00
        schedule("0 0 * * *", () => {
        setState(PATH + 'Tagesverbrauch', 0, true);
        });

        // Initial
        calculateEnergyMaster();

        ⸻

        Wenn du mir sagst, welcher Adapter/Wechselrichter/Smartmeter das genau ist (SolaX + Modbus ist klar, aber Smartmeter-Typ und ob „Netzleistung“ wirklich so rum gepolt ist), kann ich dir zusätzlich eine Plausibilitätsprüfung einbauen (z.B. „Hausverbrauch darf nicht kleiner als 0 sein“, „PV >= Eigenverbrauch“, Warn-Log wenn Vorzeichen nicht passen).

        Zum Schluss hab ich noch gefragt, für was das Skript eigentlich da ist und als Antwort kam das folgende:

        Das Skript dient dazu, alle Energieflüsse einer Photovoltaikanlage in Echtzeit rechnerisch korrekt aufzubereiten.
        Es berechnet aus PV-Leistung, Netzbezug/-einspeisung und Batterieladung den tatsächlichen Hausverbrauch, die Autarkiequote sowie den Eigenverbrauch.
        Zusätzlich ermittelt es den **aktuellen Ladezustand des Speichers inkl. Restladezeit („fertig um“) **.
        Ein integrierter Tageszähler mit Mitternachts-Reset liefert saubere Verbrauchswerte für Grafana-Diagramme.

        Da ich mich aktuell auf der anderen Seite der Erde befinde und mehr oder weniger nur das Tablett zur Verfügung hab, könnte ich das selber nicht ausprobieren ob das lauffähig ist zu vermuten ist, dass man da noch mal 2/3 Runden nachbessern müsste

        Ps, sehe gerade, dass das Forum Zeichen verschluckt.(bspw backticks). Auch habe ich wohl irgendetwas zu viel kopiert. Diese Hinweise einfach ignorieren.

        HomoranH Offline
        HomoranH Offline
        Homoran
        Global Moderator Administrators
        schrieb am zuletzt editiert von Homoran
        #23

        @OliverIO sagte in Verbesserung erfahren Blockly/Javascript/KI/AI:

        dass das Forum Zeichen verschluckt.(bspw backticks)

        wo? aber doch nicht innerhalb von code-tags!?
        steuerzeichen werden nicht so einfach angezeigt.
        seh ich mir an, wenn du sagst wo

        kein Support per PN! - Fragen im Forum stellen - es gibt fast nichts, was nicht auch für andere interessant ist.

        Benutzt das Voting rechts unten im Beitrag wenn er euch geholfen hat.

        der Installationsfixer: curl -fsL https://iobroker.net/fix.sh | bash -

        OliverIOO 1 Antwort Letzte Antwort
        0
        • HomoranH Homoran

          @OliverIO sagte in Verbesserung erfahren Blockly/Javascript/KI/AI:

          dass das Forum Zeichen verschluckt.(bspw backticks)

          wo? aber doch nicht innerhalb von code-tags!?
          steuerzeichen werden nicht so einfach angezeigt.
          seh ich mir an, wenn du sagst wo

          OliverIOO Offline
          OliverIOO Offline
          OliverIO
          schrieb am zuletzt editiert von
          #24

          @Homoran

          Suche im korrigierten Skript nach der folgenden Zeile.
          Auch danach fehlen die Backticks.
          Das Problem kam aber eventuell schon vom ursprünglich geposteten Code, dass das Forum diese Zeichen verschluckt hat

          Um den Markdown von ChatGPT nicht zu verlieren, habe ich das alles direkt gepostet, ohne Code Tags. Das sind dann wohl noch ein paar andere Zeichen uminterpretiert worden.

          // WICHTIG: Template-String braucht Backticks: ...

          Meine Adapter und Widgets
          TVProgram, SqueezeboxRPC, OpenLiga, RSSFeed, MyTime,, pi-hole2, vis-json-template, skiinfo, vis-mapwidgets, vis-2-widgets-rssfeed
          Links im Profil

          HomoranH 1 Antwort Letzte Antwort
          0
          • NegaleinN Offline
            NegaleinN Offline
            Negalein
            Global Moderator
            schrieb am zuletzt editiert von Negalein
            #25

            @asgothian @oliverio
            & Co.

            mich würde eure ehrliche Meinung zu meinem Spoolman-Skript intressieren.
            Es wurde mit ChatGPT erstellt.

            Auf Erklärungem "im Script" hab ich bewusst verzichtet. Das hab ich wärend der Erstellung bereits erfragt.

            /**
             * ============================================================
             * ioBroker – Klipper – Spoolman Integration
             * ============================================================
             *
             * Version:   1.0.0
             * Author:    <Negalein>
             * License:   MIT
             *
             * Description:
             * - Liest aktive Spulen aus Klipper (Moonraker)
             * - Holt Restfilament aus Spoolman (SQLite via SSH)
             * - Erstellt automatisch ioBroker-States
             * - 🟢🟡🔴 Ampel-Logik je Spule
             * - Telegram-Warnungen mit Zeit- & Drucklogik
             * ==========================================================
             */
            
            const { exec } = require('child_process');
            
            /* ==========================================================
               KONFIGURATION
               ========================================================== */
            
            const CONFIG = {
              spoolman: {
                sshHost: 'root@10.0.1.148',
                dbPath: '/root/.local/share/spoolman/spoolman.db'
              },
            
              baseState: '0_userdata.0.3DDrucker.Spoolman',
            
              limits: {
                warn: 300,
                empty: 100
              },
            
              warnTimes: {
                weekday: { start: 7, end: 22 },
                weekend: { start: 10, end: 20 }
              },
            
              extruders: [
                { name: 'T0', state: 'klipper-moonraker.0.gcode_macro T0.spool_id' },
                { name: 'T1', state: 'klipper-moonraker.0.gcode_macro T1.spool_id' },
                { name: 'T2', state: 'klipper-moonraker.0.gcode_macro T2.spool_id' },
                { name: 'T3', state: 'klipper-moonraker.0.gcode_macro T3.spool_id' }
              ],
            
              schedule: '*/5 * * * *'
            };
            
            /* ==========================================================
               STATE HELFER
               ========================================================== */
            
            function ensureState(id, val, type) {
              if (!existsState(id)) {
                createState(id, val, {
                  type,
                  read: true,
                  write: false,
                  role: 'value'
                });
              } else {
                setState(id, val, true);
              }
            }
            
            /* ==========================================================
               ZEITFENSTER
               ========================================================== */
            
            function isInWarnTime() {
              const now = new Date();
              const hour = now.getHours();
              const day  = now.getDay(); // 0=So
            
              const isWeekend = (day === 0 || day === 6);
              const cfg = isWeekend ? CONFIG.warnTimes.weekend : CONFIG.warnTimes.weekday;
            
              return hour >= cfg.start && hour < cfg.end;
            }
            
            /* ==========================================================
               DRUCKSTATUS
               ========================================================== */
            
            function isPrinting() {
              return getState('klipper-moonraker.0.print_stats.state')?.val === 'printing';
            }
            
            /* ==========================================================
               SPOOLMAN DB LESEN
               ========================================================== */
            
            function readSpoolman(callback) {
              const cmd = `
            ssh ${CONFIG.spoolman.sshHost} "sqlite3 -json ${CONFIG.spoolman.dbPath} '
            SELECT
              spool.id AS spool_id,
              filament.name AS name,
              filament.material AS material,
              ROUND(spool.initial_weight - spool.used_weight, 1) AS remaining
            FROM spool
            JOIN filament ON filament.id = spool.filament_id
            WHERE spool.archived IS NOT 1;
            '"
            `;
            
              exec(cmd, (err, stdout) => {
                if (err) {
                  log(`Spoolman SSH Fehler: ${err.message}`, 'error');
                  callback([]);
                  return;
                }
            
                try {
                  callback(JSON.parse(stdout));
                } catch {
                  log('Spoolman JSON ungültig', 'error');
                  callback([]);
                }
              });
            }
            
            /* ==========================================================
               STATUS HELFER
               ========================================================== */
            
            function getStatus(remaining) {
              if (remaining < CONFIG.limits.empty) return 'LEER';
              if (remaining < CONFIG.limits.warn)  return 'WARN';
              return 'OK';
            }
            
            /* ==========================================================
               HAUPTLOGIK
               ========================================================== */
            
            function update() {
            
              // globale VIS-States
              ensureState(`${CONFIG.baseState}.warnzeit_aktiv`, isInWarnTime(), 'boolean');
              ensureState(`${CONFIG.baseState}.druck_laeuft`, isPrinting(), 'boolean');
            
              readSpoolman(spools => {
            
                CONFIG.extruders.forEach((ext, index) => {
            
                  const slot = `${CONFIG.baseState}.aktiv.${index + 1}`;
                  const spoolId = getState(ext.state)?.val;
            
                  if (!spoolId || spoolId <= 0) {
                    ensureState(`${slot}.active`, false, 'boolean');
                    return;
                  }
            
                  const spool = spools.find(s => s.spool_id == spoolId);
                  if (!spool) {
                    log(`Spule ${spoolId} nicht in Spoolman gefunden`, 'warn');
                    return;
                  }
            
                  /* Basisdaten */
                  ensureState(`${slot}.active`, true, 'boolean');
                  ensureState(`${slot}.extruder`, ext.name, 'string');
                  ensureState(`${slot}.spool_id`, spoolId, 'number');
                  ensureState(`${slot}.name`, spool.name, 'string');
                  ensureState(`${slot}.material`, spool.material, 'string');
                  ensureState(`${slot}.remaining_weight`, spool.remaining, 'number');
            
                  /* Status */
                  const status = getStatus(spool.remaining);
                  ensureState(`${slot}.status`, status, 'string');
            
                  const warnId  = `${slot}.warnung`;
                  const alarmId = `${slot}.alarm`;
            
                  const warned  = getState(warnId)?.val || false;
                  const alarmed = getState(alarmId)?.val || false;
            
                  /* 🟡 WARN */
                  if (
                    status === 'WARN' &&
                    !warned &&
                    isInWarnTime() &&
                    isPrinting()
                  ) {
                    sendTo('telegram', 'send', {
                      text:
            `🟡 Filament wird knapp
            
            🖨 Extruder: ${ext.name}
            🧵 ${spool.name}
            🎨 ${spool.material}
            📦 Rest: ${spool.remaining} g`
                    });
                    ensureState(warnId, true, 'boolean');
                  }
            
                  /* 🔴 LEER */
                  if (
                    status === 'LEER' &&
                    !alarmed &&
                    isPrinting()
                  ) {
                    sendTo('telegram', 'send', {
                      text:
            `🔴 Filament leer!
            
            🖨 Extruder: ${ext.name}
            🧵 ${spool.name}
            🎨 ${spool.material}
            📦 Rest: ${spool.remaining} g
            
            ⚠️ Filament wechseln!`
                    });
                    ensureState(alarmId, true, 'boolean');
                  }
            
                  /* Reset */
                  if (status === 'OK') {
                    ensureState(warnId, false, 'boolean');
                    ensureState(alarmId, false, 'boolean');
                  }
                });
              });
            }
            
            /* ==========================================================
               SCHEDULER
               ========================================================== */
            
            schedule(CONFIG.schedule, update);
            update();
            

            ° Node.js & System Update ---> sudo apt update, iob stop, sudo apt full-upgrade
            ° Node.js Fixer ---> iob nodejs-update
            ° Fixer ---> iob fix

            1 Antwort Letzte Antwort
            0
            • AsgothianA Asgothian

              @liv-in-sky sagte in Verbesserung erfahren Blockly/Javascript/KI/AI:

              ich finde es gut, sich mit ki und iobroker zu beschäftigen - iobroker ist eine hausautomatisierung und es sollte für user einfach sein, alles zu realisieren, was man sich vorstellt - ohne sich lange mit programmierung zu beschäftigen. man sollte ki momentan noch nicht als allwissend verstehen, aber auch nicht in den boden reden. wenn das projekt iobroker weiterhin erfolgreich sein möchte, wird man um ki nicht "rumkommen".

              Muss man auch nicht. KI Nutzung ist durchaus an vielen Stellen Sinnvoll. Ob es aber bei der Erzeugung von User-spezifischen Automatisierungen sinnvoll ist, wo der User im zweifelsfall gerade nicht bewerten kann was da heraus gekommen ist wage ich zu bezweifeln.

              stattdessen wäre es wohl besser eine empfehlungsliste oder sowas zu machen: z.b.

              • beginne nicht großen (langen) code zu generieren, sondern benutze es anfangs für funktionen
              • evtl kann man eine liste erstellen wie oben erwähnt (achte darauf, das du den heartbeat nicht zu hoch setzt, .....
              • was sind empfohlende ki's (claude code, gemini, ..) für die programmierung

              usw

              Da gehe ich mit. EIne Liste was sich wo und wofür am besten eignet kann helfen. Allerdings nur wenn da auch Hinweise auf die 'häufigsten Fehler' dabei sind.

              wenn ich mir ansehe, was openclaw kann, wird es nicht lange dauern, bis man ein system zur hausautomatisierung sehen wird - unsere adapter codes sind offenzugänglich - warum sollte ein neues "erfundenes" system das nicht nutzen können und iobroker einfach ersetzen ?

              Da bin ich eher skeptisch. Wenn ich mir anschaue was OpenClaw bisher so kann warte ich nur darauf das dieses System schlecht konfiguriert so grossen Schaden anrichtet das

              • der Gesetzgeber einschreitet
              • die Platformen einschreiten und die Nutzung von KI Agenten auf der Platform unter Strafe stellen (i.e. wenn Wir Platformbetreiber heraus finden das du User KI Agenten auf unserer Platform nutzt dann bist du nicht mehr User unserer Platform)
              • das Risiko es zu nutzen zu gross wird
              • es von der 'dunklen Seite' der Gesellschaft ausgenutzt wird.
              liv-in-skyL Offline
              liv-in-skyL Offline
              liv-in-sky
              schrieb am zuletzt editiert von
              #26

              @Asgothian sagte in Verbesserung erfahren Blockly/Javascript/KI/AI:

              der Gesetzgeber einschreitet

              ich habe mich eh schon gefragt, wenn openclaw anfängt zu "hacken" oder über whatsapp zu "mobben", wer dan strafrechtlich belangt wird und wann man das erste mal davon hören wird

              ich dachte halt, wenn es von programmierern wie euch kommt, die das system am besten kennen, und als richtlinie die user darauf vorbereiten (evtl mit einer beschreibung für eine ki, was sie darf bzw nicht darf), wäre das halt die sicherste variante für neue user und ki-interessierte (ich spreche ja nicht von sofort, sondern einfach mal im hinterkopf zu haben).

              wie cool wäre es, beim öffnen eines javascript-functions-blocks gleich zugang einem agenten zu haben und zu beschreiben, was die funktion machen soll - so als spinnerei :-)

              natürlich habt ihr recht skeptisch zu sein und ich könnte eure argumente auch nicht einfach abtun, sondern sehe die richtigkeit darin - aber es wird kommen - KI wird nicht verschwinden - und vorallem das code-generieren wird in zukunft besser werden

              nach einem gelösten Thread wäre es sinnvoll dies in der Überschrift des ersten Posts einzutragen [gelöst]-... Bitte benutzt das Voting rechts unten im Beitrag wenn er euch geholfen hat. Forum-Tools: PicPick https://picpick.app/en/download/ und ScreenToGif https://www.screentogif.com/downloads.html

              NegaleinN 1 Antwort Letzte Antwort
              1
              • OliverIOO OliverIO

                @Homoran

                Suche im korrigierten Skript nach der folgenden Zeile.
                Auch danach fehlen die Backticks.
                Das Problem kam aber eventuell schon vom ursprünglich geposteten Code, dass das Forum diese Zeichen verschluckt hat

                Um den Markdown von ChatGPT nicht zu verlieren, habe ich das alles direkt gepostet, ohne Code Tags. Das sind dann wohl noch ein paar andere Zeichen uminterpretiert worden.

                // WICHTIG: Template-String braucht Backticks: ...

                HomoranH Offline
                HomoranH Offline
                Homoran
                Global Moderator Administrators
                schrieb am zuletzt editiert von Homoran
                #27

                @OliverIO ok, das ist korrekt

                hier
                Screenshot_20260210-185212_DuckDuckGo.jpg

                müsstest du die backticks mit backslash escapen

                Screenshot_20260210-185254_DuckDuckGo.jpg

                was bei dem Text natürlich eine Arbeit für Herrn Sisyphos wäre

                Erklärung:
                zwischen zwei Backticks ist `inline code`
                = inline code

                kein Support per PN! - Fragen im Forum stellen - es gibt fast nichts, was nicht auch für andere interessant ist.

                Benutzt das Voting rechts unten im Beitrag wenn er euch geholfen hat.

                der Installationsfixer: curl -fsL https://iobroker.net/fix.sh | bash -

                1 Antwort Letzte Antwort
                0
                • liv-in-skyL liv-in-sky

                  @Asgothian sagte in Verbesserung erfahren Blockly/Javascript/KI/AI:

                  der Gesetzgeber einschreitet

                  ich habe mich eh schon gefragt, wenn openclaw anfängt zu "hacken" oder über whatsapp zu "mobben", wer dan strafrechtlich belangt wird und wann man das erste mal davon hören wird

                  ich dachte halt, wenn es von programmierern wie euch kommt, die das system am besten kennen, und als richtlinie die user darauf vorbereiten (evtl mit einer beschreibung für eine ki, was sie darf bzw nicht darf), wäre das halt die sicherste variante für neue user und ki-interessierte (ich spreche ja nicht von sofort, sondern einfach mal im hinterkopf zu haben).

                  wie cool wäre es, beim öffnen eines javascript-functions-blocks gleich zugang einem agenten zu haben und zu beschreiben, was die funktion machen soll - so als spinnerei :-)

                  natürlich habt ihr recht skeptisch zu sein und ich könnte eure argumente auch nicht einfach abtun, sondern sehe die richtigkeit darin - aber es wird kommen - KI wird nicht verschwinden - und vorallem das code-generieren wird in zukunft besser werden

                  NegaleinN Offline
                  NegaleinN Offline
                  Negalein
                  Global Moderator
                  schrieb am zuletzt editiert von
                  #28

                  @liv-in-sky sagte in Verbesserung erfahren Blockly/Javascript/KI/AI:

                  wer dan strafrechtlich belangt wird

                  der User!
                  wenn du ein Messer gratis bekommst und damit wen tötest, wirst auch du bestraft und nicht der Schenker.

                  ° Node.js & System Update ---> sudo apt update, iob stop, sudo apt full-upgrade
                  ° Node.js Fixer ---> iob nodejs-update
                  ° Fixer ---> iob fix

                  Meister MopperM 1 Antwort Letzte Antwort
                  0
                  • OliverIOO OliverIO

                    Ich habe das Skript mit dem folgenden, prompt mal bei ChatGPT hineingeworfen

                    Bitte analysiere zunächst das Folgende Skript
                    Dann liefere mir bitte das Skript ergänzt mit Kommentaren, so dass es ein Anfänger verstehen kann. In einem weiteren Schritt liefere mir bitte ein verbessertes Skript mit der Analyse, warum du welche Punkte verbessert hast
                    

                    Als Ergebnis kam folgendes

                    1. Analyse: Warum „das Folgende“ so vermutlich nicht klappt

                    Ich gehe davon aus, dass das ein ioBroker-JavaScript (JS-Adapter) Script ist (wegen getState(), setState(), on(), schedule(), formatDate()).

                    A) Harte Syntaxfehler (Script startet dann gar nicht)
                    1. formatTime() gibt einen Template-String ohne Backticks zurück

                    return ${h}:${m < 10 ? '0' + m : m};

                    Das ist ungültiges JavaScript. Template-Literals brauchen Backticks:

                    return ${h}:${m < 10 ? '0' + m : m};

                    2.	In deinem Code sind fremde Textzeilen mitten im JS:
                    
                    •	Copy to Clipboard
                    •	Bullet-Lines wie •  // "Fertig um" Uhrzeit berechnen
                    

                    Das sind keine Kommentare, sondern normaler Text → Syntax Error.
                    Wenn du das behalten willst, muss es als Kommentar geschrieben werden (// ... oder /* ... */).

                    B) Logik-/Robustheitsprobleme (läuft ggf., aber liefert falsche/instabile Werte)
                    3. formatDate() ist nicht Standard-JS
                    Im ioBroker-JS-Adapter existiert formatDate() typischerweise, aber nur dort. Wenn du das Script woanders testest, knallt es. (In ioBroker ist es ok.)
                    4. Trigger reagieren nicht auf SOC/Speichergröße
                    Du berechnest Restladezeit mit soc und speicherMax, aber dein on() triggert nur auf:

                    •	PV, Smartmeter, Batterie-Leistung
                    

                    Wenn sich SOC ändert (häufig), kann die Anzeige „hinterherhinken“.

                    5.	Akkumulator „Tagesverbrauch“: fest 10s gerechnet, aber ohne Drift-Korrektur
                    

                    setInterval(..., 10000) ist nicht garantiert exakt. Wenn das System hängt, stimmt der Tageswert langsam nicht mehr. Besser: echte Zeitdifferenz messen.
                    6. getState(...).val || 0 kann echte 0 überdecken?
                    Hier ist es okay (0 bleibt 0), aber bei NaN/Strings wird es stillschweigend zu 0. Besser: sauber parsen und validieren.
                    7. Einheiten & Vorzeichen sind nur korrekt, wenn deine Quellen wirklich so liefern

                    •	netPower: positiv Bezug, negativ Einspeisung (du schreibst das so)
                    •	batPower: positiv Laden, negativ Entladen (du schreibst das so)
                    

                    Wenn ein Gerät das anders liefert, sind alle Flüsse „gespiegelt“.

                    ⸻

                    1. Dein Skript – ergänzt und so kommentiert, dass Anfänger es verstehen

                    Ich habe nur die offensichtlichen Syntaxprobleme repariert und Kommentare ergänzt, ohne die Struktur stark zu verändern.

                    /*
                    Name: PV-Energiemaster Pro
                    Zweck: Echtzeit-Berechnung von Hausverbrauch, Autarkie & Speicher-Logik
                    inkl. täglichem Reset für saubere Grafana-Balken
                    */

                    // --- KONFIGURATION: IDs ---
                    const ID_BATTERIE_W = 'modbus.0.inputRegisters.100.842_Battery_Power_(System)';
                    const ID_PV_DACH_W = 'solax.0.data.acpower';
                    const ID_SMARTMETER_W = 'smartmeter.0.1-0:16_7_0__255.value';
                    const ID_SOC = 'modbus.0.inputRegisters.100.843_Battery_State_of_Charge_(System)';
                    const ID_SPEICHER_KWH = '0_userdata.0.Energie.PV.Speichergroesse';

                    const PATH = '0_userdata.0.Energie.PV.';

                    // --- HILFSFUNKTIONEN ---

                    /**

                    • Wandelt Minuten (z.B. 85.3) in "1:25" um.
                    • Wenn Minuten nicht sinnvoll sind, geben wir "n. n." zurück.
                      */
                      function formatTime(minutes) {
                      if (isNaN(minutes) || minutes <= 0 || !isFinite(minutes)) return "n. n.";

                    const h = Math.floor(minutes / 60);
                    const m = Math.floor(minutes % 60);

                    // WICHTIG: Template-String braucht Backticks: ...
                    return ${h}:${m < 10 ? '0' + m : m};
                    }

                    /**

                    • Kern-Logik: Berechnet alle Energieflüsse
                      */
                      function calculateEnergyMaster() {
                      // 1) Rohdaten abrufen (Watt bzw. % / kWh)
                      const pvPower = getState(ID_PV_DACH_W).val || 0;

                    // Smartmeter: + = Netzbezug, - = Einspeisung (laut Kommentar)
                    const netPower = getState(ID_SMARTMETER_W).val || 0;

                    // Batterie: + = Laden, - = Entladen (laut Kommentar)
                    const batPower = getState(ID_BATTERIE_W).val || 0;

                    const soc = getState(ID_SOC).val || 0; // Prozent
                    const speicherMax = getState(ID_SPEICHER_KWH).val || 0; // kWh

                    // 2) Zerlegung der Flüsse in „nur positive“ Teilgrößen
                    const netzBezug = netPower > 0 ? netPower : 0;
                    const einspeisung = netPower < 0 ? Math.abs(netPower) : 0;

                    const batterieLaden = batPower > 0 ? batPower : 0;
                    const batterieEntladen = batPower < 0 ? Math.abs(batPower) : 0;

                    // 3) Hausverbrauch berechnen (physikalisch konsistent)
                    // Idee: Alles was ins Haus "rein kommt" minus alles was "raus geht"
                    // Formel: PV + Netzbezug + Batterie-Entladung - Batterie-Ladung - Einspeisung
                    let hausverbrauch = pvPower + netzBezug + batterieEntladen - batterieLaden - einspeisung;
                    if (hausverbrauch < 0) hausverbrauch = 0;

                    // 4) Autarkie & Eigenverbrauch
                    // Autarkie: wie viel % des Hausverbrauchs NICHT aus dem Netz kommt
                    let autarkie = 0;
                    if (hausverbrauch > 0) {
                    autarkie = Math.min(100, (1 - (netzBezug / hausverbrauch)) * 100);
                    }

                    // Eigenverbrauch: PV-Leistung, die nicht eingespeist wird (vereinfacht)
                    const eigenverbrauch = Math.max(0, pvPower - einspeisung);

                    // 5) Speicher-Restlaufzeit (wie lange bis Akku voll, wenn er lädt)
                    const ladestandKWh = (speicherMax * soc) / 100;
                    const leerspeicherKWh = Math.max(0, speicherMax - ladestandKWh);

                    let restladezeitMin = 0;
                    let restladezeitSec = 0;
                    let fertigUm = "n. n.";

                    // Nur berechnen, wenn wirklich geladen wird (und nicht nur „Rauschen“)
                    if (batterieLaden > 50) {
                    // batterieLaden ist Watt, daher /1000 => kW
                    // Zeit in Stunden = kWh / kW
                    // dann *60 für Minuten, *3600 für Sekunden
                    restladezeitMin = (leerspeicherKWh / (batterieLaden / 1000)) * 60;
                    restladezeitSec = (leerspeicherKWh / (batterieLaden / 1000)) * 3600;

                    // "Fertig um" Uhrzeit berechnen
                    // (formatDate ist im ioBroker-JS-Adapter verfügbar)
                    let now = new Date();
                    now.setSeconds(now.getSeconds() + restladezeitSec);
                    fertigUm = formatDate(now, "hh:mm");
                    

                    }

                    // 6) Datenpunkte schreiben (Echtzeit)
                    setState(PATH + 'Batterieleistung', batPower, true);
                    setState(PATH + 'PV_Dach', pvPower, true);
                    setState(PATH + 'L1_L3', netPower, true);
                    setState(PATH + 'Netzbezug', netzBezug, true);
                    setState(PATH + 'Einspeisung', einspeisung, true);
                    setState(PATH + 'Hausverbrauch', hausverbrauch, true);
                    setState(PATH + 'Autarkie', Math.round(autarkie), true);
                    setState(PATH + 'Eigenverbrauch', eigenverbrauch, true);

                    setState(PATH + 'lade_kwh', parseFloat(ladestandKWh.toFixed(1)), true);
                    setState(PATH + 'Restladezeit', formatTime(restladezeitMin), true);
                    setState(PATH + 'Restladezeit_sec', Math.round(restladezeitSec), true);
                    setState(PATH + 'Restladezeit_final', fertigUm, true);
                    }

                    // --- ZEITPLANUNG & TRIGGER ---

                    // Trigger: Sofort rechnen bei jeder Änderung der Hardware-Werte
                    on(
                    { id: [ID_PV_DACH_W, ID_SMARTMETER_W, ID_BATTERIE_W], change: "any" },
                    calculateEnergyMaster
                    );

                    // Zähler: Alle 10 Sekunden den Energie-Akkumulator füllen (Wh-Schritte)
                    setInterval(() => {
                    const haus = getState(PATH + 'Hausverbrauch').val || 0;
                    const oldVal = getState(PATH + 'Tagesverbrauch').val || 0;

                    // Watt / 360 = Wh-Anteil für 10 Sekunden (weil 10s = 1/360 h)
                    setState(PATH + 'Tagesverbrauch', oldVal + (haus / 360), true);
                    }, 10000);

                    // Reset: Jeden Tag um 00:00 Uhr den Tagesverbrauch für Grafana nullen
                    schedule("0 0 * * *", () => {
                    setState(PATH + 'Tagesverbrauch', 0, true);
                    });

                    // Initialer Start beim Skript-Speichern
                    calculateEnergyMaster();

                    ⸻

                    1. Verbessertes Skript + Analyse, warum diese Punkte besser sind

                    Was ich verbessert habe (kurz & konkret)
                    1. Sauberes Einlesen von Zahlen: readNumber() verhindert NaN, Strings, null.
                    2. Trigger erweitert: SOC und Speichergröße triggern ebenfalls → Anzeigen bleiben aktuell.
                    3. Debounce: Wenn PV/Smartmeter/Batterie sehr häufig ändern, wird nicht 50×/Sek gerechnet.
                    4. Tagesverbrauch korrekt nach echter Zeitdifferenz statt „fix 10 Sekunden“ → weniger Drift.
                    5. State-Initialisierung (setObjectNotExists) optional, aber extrem hilfreich, damit Grafana/Objekte sauber sind.
                    6. Schutz gegen Division durch 0 / unrealistische Werte (z.B. Speichergröße 0).

                    ⸻

                    Verbessertes Skript (robuster & „produktionsnäher“)

                    /*
                    PV-Energiemaster Pro (improved)

                    • Robustere Zahlenverarbeitung
                    • Aktuellere Trigger
                    • Debounce gegen Rechen-Spam
                    • Tagesverbrauch über echte Zeitdifferenz
                      */

                    const ID_BATTERIE_W = 'modbus.0.inputRegisters.100.842_Battery_Power_(System)';
                    const ID_PV_DACH_W = 'solax.0.data.acpower';
                    const ID_SMARTMETER_W = 'smartmeter.0.1-0:16_7_0__255.value';
                    const ID_SOC = 'modbus.0.inputRegisters.100.843_Battery_State_of_Charge_(System)';
                    const ID_SPEICHER_KWH = '0_userdata.0.Energie.PV.Speichergroesse';

                    const PATH = '0_userdata.0.Energie.PV.';

                    // -------------------- Hilfsfunktionen --------------------

                    /** Liest einen Datenpunkt als Zahl und liefert bei Problemen defaultValue zurück. */
                    function readNumber(id, defaultValue = 0) {
                    const st = getState(id);
                    const v = st ? st.val : defaultValue;
                    const n = Number(v);
                    return Number.isFinite(n) ? n : defaultValue;
                    }

                    /** Wie zuvor: Minuten -> "h:mm" */
                    function formatTime(minutes) {
                    if (!Number.isFinite(minutes) || minutes <= 0) return "n. n.";
                    const h = Math.floor(minutes / 60);
                    const m = Math.floor(minutes % 60);
                    return ${h}:${m < 10 ? '0' + m : m};
                    }

                    /** Kleiner Debounce: fasst viele Änderungen zusammen und rechnet max. alle X ms. */
                    function debounce(fn, waitMs) {
                    let t = null;
                    return function () {
                    if (t) clearTimeout(t);
                    t = setTimeout(() => fn(), waitMs);
                    };
                    }

                    // -------------------- (Optional) State-Objekte anlegen --------------------
                    // Vorteil: Du siehst die States sauber im Objektbaum, inkl. Typ/Unit/Role.
                    // Wenn du das nicht willst, kannst du diesen Block weglassen.
                    function ensureStates() {
                    setObjectNotExists(PATH + 'Batterieleistung', { type: 'state', common: { name: 'Batterieleistung', type: 'number', role: 'value.power', unit: 'W', read: true, write: true }, native: {} });
                    setObjectNotExists(PATH + 'PV_Dach', { type: 'state', common: { name: 'PV Dach', type: 'number', role: 'value.power', unit: 'W', read: true, write: true }, native: {} });
                    setObjectNotExists(PATH + 'L1_L3', { type: 'state', common: { name: 'Netzleistung', type: 'number', role: 'value.power', unit: 'W', read: true, write: true }, native: {} });
                    setObjectNotExists(PATH + 'Netzbezug', { type: 'state', common: { name: 'Netzbezug', type: 'number', role: 'value.power', unit: 'W', read: true, write: true }, native: {} });
                    setObjectNotExists(PATH + 'Einspeisung', { type: 'state', common: { name: 'Einspeisung', type: 'number', role: 'value.power', unit: 'W', read: true, write: true }, native: {} });
                    setObjectNotExists(PATH + 'Hausverbrauch', { type: 'state', common: { name: 'Hausverbrauch', type: 'number', role: 'value.power', unit: 'W', read: true, write: true }, native: {} });
                    setObjectNotExists(PATH + 'Autarkie', { type: 'state', common: { name: 'Autarkie', type: 'number', role: 'value', unit: '%', read: true, write: true }, native: {} });
                    setObjectNotExists(PATH + 'Eigenverbrauch', { type: 'state', common: { name: 'Eigenverbrauch', type: 'number', role: 'value.power', unit: 'W', read: true, write: true }, native: {} });
                    setObjectNotExists(PATH + 'lade_kwh', { type: 'state', common: { name: 'Ladestand', type: 'number', role: 'value.energy', unit: 'kWh', read: true, write: true }, native: {} });
                    setObjectNotExists(PATH + 'Restladezeit', { type: 'state', common: { name: 'Restladezeit', type: 'string', role: 'text', read: true, write: true }, native: {} });
                    setObjectNotExists(PATH + 'Restladezeit_sec', { type: 'state', common: { name: 'Restladezeit (s)', type: 'number', role: 'value', unit: 's', read: true, write: true }, native: {} });
                    setObjectNotExists(PATH + 'Restladezeit_final',{ type: 'state', common:{ name:'Fertig um', type:'string', role:'text', read:true, write:true }, native:{} });
                    setObjectNotExists(PATH + 'Tagesverbrauch', { type: 'state', common: { name: 'Tagesverbrauch', type: 'number', role: 'value.energy', unit: 'Wh', read: true, write: true }, native: {} });
                    }

                    ensureStates();

                    // -------------------- Kernlogik --------------------

                    function calculateEnergyMaster() {
                    // 1) Rohdaten (robust eingelesen)
                    const pvPower = readNumber(ID_PV_DACH_W, 0);
                    const netPower = readNumber(ID_SMARTMETER_W, 0); // + Bezug, - Einspeisung
                    const batPower = readNumber(ID_BATTERIE_W, 0); // + Laden, - Entladen
                    const soc = readNumber(ID_SOC, 0); // %
                    const speicherMax = readNumber(ID_SPEICHER_KWH, 0); // kWh

                    // 2) Fluss-Zerlegung
                    const netzBezug = netPower > 0 ? netPower : 0;
                    const einspeisung = netPower < 0 ? -netPower : 0;

                    const batterieLaden = batPower > 0 ? batPower : 0;
                    const batterieEntladen = batPower < 0 ? -batPower : 0;

                    // 3) Hausverbrauch
                    let hausverbrauch = pvPower + netzBezug + batterieEntladen - batterieLaden - einspeisung;
                    if (!Number.isFinite(hausverbrauch) || hausverbrauch < 0) hausverbrauch = 0;

                    // 4) Autarkie & Eigenverbrauch
                    const autarkie = hausverbrauch > 0
                    ? Math.max(0, Math.min(100, (1 - (netzBezug / hausverbrauch)) * 100))
                    : 0;

                    const eigenverbrauch = Math.max(0, pvPower - einspeisung);

                    // 5) Speicher-Restladezeit
                    const speicherOk = speicherMax > 0 && soc >= 0 && soc <= 100;

                    const ladestandKWh = speicherOk ? (speicherMax * soc) / 100 : 0;
                    const leerspeicherKWh = speicherOk ? Math.max(0, speicherMax - ladestandKWh) : 0;

                    let restladezeitMin = 0;
                    let restladezeitSec = 0;
                    let fertigUm = "n. n.";

                    if (batterieLaden > 50 && speicherOk) {
                    const ladeLeistungKW = batterieLaden / 1000;
                    // Verhindert Division durch 0
                    if (ladeLeistungKW > 0) {
                    const hours = leerspeicherKWh / ladeLeistungKW;
                    restladezeitMin = hours * 60;
                    restladezeitSec = hours * 3600;

                      const now = new Date();
                      now.setSeconds(now.getSeconds() + restladezeitSec);
                      fertigUm = formatDate(now, "hh:mm");
                    }
                    

                    }

                    // 6) Schreiben
                    setState(PATH + 'Batterieleistung', batPower, true);
                    setState(PATH + 'PV_Dach', pvPower, true);
                    setState(PATH + 'L1_L3', netPower, true);
                    setState(PATH + 'Netzbezug', netzBezug, true);
                    setState(PATH + 'Einspeisung', einspeisung, true);
                    setState(PATH + 'Hausverbrauch', hausverbrauch, true);
                    setState(PATH + 'Autarkie', Math.round(autarkie), true);
                    setState(PATH + 'Eigenverbrauch', eigenverbrauch, true);

                    setState(PATH + 'lade_kwh', Number(ladestandKWh.toFixed(1)), true);
                    setState(PATH + 'Restladezeit', formatTime(restladezeitMin), true);
                    setState(PATH + 'Restladezeit_sec', Math.round(restladezeitSec), true);
                    setState(PATH + 'Restladezeit_final', fertigUm, true);
                    }

                    // Debounced Version (z.B. 300 ms): verhindert „Rechenspam“ bei schnell flackernden Werten
                    const calculateEnergyMasterDebounced = debounce(calculateEnergyMaster, 300);

                    // -------------------- Trigger & Tagesverbrauch --------------------

                    // Trigger erweitert: auch SOC und Speichergröße
                    on(
                    { id: [ID_PV_DACH_W, ID_SMARTMETER_W, ID_BATTERIE_W, ID_SOC, ID_SPEICHER_KWH], change: "any" },
                    calculateEnergyMasterDebounced
                    );

                    // Tagesverbrauch: Wh über echte Zeitdifferenz integrieren
                    let lastTs = Date.now();

                    setInterval(() => {
                    const now = Date.now();
                    const dtSec = (now - lastTs) / 1000;
                    lastTs = now;

                    // aktuelle Leistung in W
                    const hausW = readNumber(PATH + 'Hausverbrauch', 0);
                    const oldWh = readNumber(PATH + 'Tagesverbrauch', 0);

                    // Wh = W * Stunden ; Stunden = Sekunden / 3600
                    const addWh = hausW * (dtSec / 3600);

                    setState(PATH + 'Tagesverbrauch', oldWh + addWh, true);
                    }, 10000);

                    // Reset täglich 00:00
                    schedule("0 0 * * *", () => {
                    setState(PATH + 'Tagesverbrauch', 0, true);
                    });

                    // Initial
                    calculateEnergyMaster();

                    ⸻

                    Wenn du mir sagst, welcher Adapter/Wechselrichter/Smartmeter das genau ist (SolaX + Modbus ist klar, aber Smartmeter-Typ und ob „Netzleistung“ wirklich so rum gepolt ist), kann ich dir zusätzlich eine Plausibilitätsprüfung einbauen (z.B. „Hausverbrauch darf nicht kleiner als 0 sein“, „PV >= Eigenverbrauch“, Warn-Log wenn Vorzeichen nicht passen).

                    Zum Schluss hab ich noch gefragt, für was das Skript eigentlich da ist und als Antwort kam das folgende:

                    Das Skript dient dazu, alle Energieflüsse einer Photovoltaikanlage in Echtzeit rechnerisch korrekt aufzubereiten.
                    Es berechnet aus PV-Leistung, Netzbezug/-einspeisung und Batterieladung den tatsächlichen Hausverbrauch, die Autarkiequote sowie den Eigenverbrauch.
                    Zusätzlich ermittelt es den **aktuellen Ladezustand des Speichers inkl. Restladezeit („fertig um“) **.
                    Ein integrierter Tageszähler mit Mitternachts-Reset liefert saubere Verbrauchswerte für Grafana-Diagramme.

                    Da ich mich aktuell auf der anderen Seite der Erde befinde und mehr oder weniger nur das Tablett zur Verfügung hab, könnte ich das selber nicht ausprobieren ob das lauffähig ist zu vermuten ist, dass man da noch mal 2/3 Runden nachbessern müsste

                    Ps, sehe gerade, dass das Forum Zeichen verschluckt.(bspw backticks). Auch habe ich wohl irgendetwas zu viel kopiert. Diese Hinweise einfach ignorieren.

                    crunchipC Abwesend
                    crunchipC Abwesend
                    crunchip
                    Forum Testing Most Active
                    schrieb am zuletzt editiert von
                    #29

                    @OliverIO sagte in Verbesserung erfahren Blockly/Javascript/KI/AI:

                    Ich habe das Skript mit dem folgenden, prompt mal bei ChatGPT hineingeworfen

                    wie schon anderweitig erwähnt verwende ich gerne perplexity, das erklärt einem automatisch ohne zu fragen,
                    erläutert die Fehler, macht Verbesserungsvorschläge und stellt folge Fragen, was man sich z.b eventuell noch wünschen/Erweitern könnte. Das finde ich ganz praktisch, denn da kommen zwischendurch mal coole Ideen herum, an die man gar nicht gedacht hat.

                    umgestiegen von Proxmox auf Unraid

                    NegaleinN 1 Antwort Letzte Antwort
                    0
                    • crunchipC crunchip

                      @OliverIO sagte in Verbesserung erfahren Blockly/Javascript/KI/AI:

                      Ich habe das Skript mit dem folgenden, prompt mal bei ChatGPT hineingeworfen

                      wie schon anderweitig erwähnt verwende ich gerne perplexity, das erklärt einem automatisch ohne zu fragen,
                      erläutert die Fehler, macht Verbesserungsvorschläge und stellt folge Fragen, was man sich z.b eventuell noch wünschen/Erweitern könnte. Das finde ich ganz praktisch, denn da kommen zwischendurch mal coole Ideen herum, an die man gar nicht gedacht hat.

                      NegaleinN Offline
                      NegaleinN Offline
                      Negalein
                      Global Moderator
                      schrieb am zuletzt editiert von
                      #30

                      @crunchip sagte in Verbesserung erfahren Blockly/Javascript/KI/AI:

                      erklärt einem automatisch ohne zu fragen, erläutert die Fehler, macht Verbesserungsvorschläge und stellt folge Fragen, was man sich z.b eventuell noch wünschen/Erweitern könnte. Das finde ich ganz praktisch, denn da kommen zwischendurch mal coole Ideen herum, an die man gar nicht gedacht hat.

                      ähnlich ChatGPT.
                      hab da auch Fehler erklärt bekommen, Tipps, usw.
                      Werde mir jetzt mal Perplexity anschaun. Ist das für Scripte echt viel besser? Hab noch einige Projekte vor.

                      ° Node.js & System Update ---> sudo apt update, iob stop, sudo apt full-upgrade
                      ° Node.js Fixer ---> iob nodejs-update
                      ° Fixer ---> iob fix

                      liv-in-skyL 1 Antwort Letzte Antwort
                      0
                      • NegaleinN Negalein

                        @crunchip sagte in Verbesserung erfahren Blockly/Javascript/KI/AI:

                        erklärt einem automatisch ohne zu fragen, erläutert die Fehler, macht Verbesserungsvorschläge und stellt folge Fragen, was man sich z.b eventuell noch wünschen/Erweitern könnte. Das finde ich ganz praktisch, denn da kommen zwischendurch mal coole Ideen herum, an die man gar nicht gedacht hat.

                        ähnlich ChatGPT.
                        hab da auch Fehler erklärt bekommen, Tipps, usw.
                        Werde mir jetzt mal Perplexity anschaun. Ist das für Scripte echt viel besser? Hab noch einige Projekte vor.

                        liv-in-skyL Offline
                        liv-in-skyL Offline
                        liv-in-sky
                        schrieb am zuletzt editiert von
                        #31

                        @Negalein mit perplevity habe ich nicht so gute Code Erfahrung

                        Claude bei Javascript und gemini bei HTML Seiten nutze ich gerne

                        nach einem gelösten Thread wäre es sinnvoll dies in der Überschrift des ersten Posts einzutragen [gelöst]-... Bitte benutzt das Voting rechts unten im Beitrag wenn er euch geholfen hat. Forum-Tools: PicPick https://picpick.app/en/download/ und ScreenToGif https://www.screentogif.com/downloads.html

                        NegaleinN Jey CeeJ crunchipC 3 Antworten Letzte Antwort
                        0
                        • liv-in-skyL liv-in-sky

                          @Negalein mit perplevity habe ich nicht so gute Code Erfahrung

                          Claude bei Javascript und gemini bei HTML Seiten nutze ich gerne

                          NegaleinN Offline
                          NegaleinN Offline
                          Negalein
                          Global Moderator
                          schrieb am zuletzt editiert von
                          #32

                          @liv-in-sky sagte in Verbesserung erfahren Blockly/Javascript/KI/AI:

                          Claude

                          kenn ich noch nicht. gleich mal suchen. :)

                          ° Node.js & System Update ---> sudo apt update, iob stop, sudo apt full-upgrade
                          ° Node.js Fixer ---> iob nodejs-update
                          ° Fixer ---> iob fix

                          1 Antwort Letzte Antwort
                          0
                          • liv-in-skyL liv-in-sky

                            @Negalein mit perplevity habe ich nicht so gute Code Erfahrung

                            Claude bei Javascript und gemini bei HTML Seiten nutze ich gerne

                            Jey CeeJ Online
                            Jey CeeJ Online
                            Jey Cee
                            Developer
                            schrieb am zuletzt editiert von
                            #33

                            @liv-in-sky sagte in Verbesserung erfahren Blockly/Javascript/KI/AI:

                            Claude bei Javascript und gemini bei HTML Seiten nutze ich gerne

                            Hier wären noch die Modelle wie Claude Sonnet 4.5, Opus 4.5, usw. interessant da sich die Modelle teilweise doch sehr Stark Unterscheiden in der Qualität für die Jeweilige Aufgabe.

                            Persönlicher Support
                            Spenden -> paypal.me/J3YC33

                            1 Antwort Letzte Antwort
                            0
                            • liv-in-skyL liv-in-sky

                              @Negalein mit perplevity habe ich nicht so gute Code Erfahrung

                              Claude bei Javascript und gemini bei HTML Seiten nutze ich gerne

                              crunchipC Abwesend
                              crunchipC Abwesend
                              crunchip
                              Forum Testing Most Active
                              schrieb am zuletzt editiert von
                              #34

                              @liv-in-sky sagte in Verbesserung erfahren Blockly/Javascript/KI/AI:

                              Claude bei Javascript und gemini

                              Ist doch mit inbegriffen und kann ausgewählt werden

                              umgestiegen von Proxmox auf Unraid

                              1 Antwort Letzte Antwort
                              0
                              • AsgothianA Asgothian

                                Nun gut.. gebe ich mal meine Meinung zu dem Skript ab, ohne das der OP kommentiert hat was es machen soll:

                                es ist Ressourcenverschwendung im grossen Stil und birgt das Risiko von Fehlberechnungen:

                                • Es läuft ein Heartbeat alle 10 Sekunden auf Daten die sich wahrscheinlich deutlich seltener Ändern.
                                • Der Heartbeat beinhaltet eine race-condition zum Trigger der Hardware-Werte, sprich je häufiger sich diese Ändern desdo öfter kann es da klemmen.
                                • Die Ergebnisse dieses Heartbeats werden in eine DB geschrieben, damit diese die dann zur Darstellung 'Aggregieren' muss (Schon die Darstellung von nur 3 Stunden führt zu einem Graph der im Zweifelsfall mehr Datenpunkte hat als das zur Darstellung benutzte Gerät Pixel bereitstellt (3 Stunden = 180 minuten = 1800 Datenpunkte)
                                • Es wird ein Multi-Trigger genutzt auf DP's die sich (wahrscheinlich - hier fehlt die Aussage des OP) quasi-Synchron ändern. In diesem werden die Werte aller beteiligten Einzel-DPs extra aus der Datenbank geholt (Risiko: Fehlberechnung wegen race-condition)
                                • Es werden Dinge berechnet die nicht genutzt werden - in jedem Trigger-Durchlauf
                                • Daten die besser im Skript in Variablen gehalten würden werden trotzdem immer über getState/setState gezogen.
                                • Auch Konstante oder semi-Konstante Werte werden bei jeder Berechnung neu gezogen.
                                • Es gibt eine mögliche race condition zwischen dem setState(PATH + 'Hausverbrauch', hausverbrauch, true); aus dem Trigger und dem const haus = getState(PATH + 'Hausverbrauch').val || 0; aus dem intervall. Wenn die Zeitlich ungünstig zusammen fallen dann wird der 'alte' Verbrauch gelesen, weil das setState nicht abgeschlossen ist.

                                Es ist aus meiner Sicht ein wunderbares Beispiel dafür wie die KI etwas liefert was prinzipiell geht, aber wenn man genau hinschaut doch klemmt.

                                A.
                                Nachtrag:
                                Ja, das ist ein relativ hartes Urteil. Ich halte das Skript für nicht gut. Und nein, dafür kann der OP nichts. Ohne weitergehendes Programmierwissen sind diese Probleme nicht ohne weiteres zu erkennen.

                                Meister MopperM Offline
                                Meister MopperM Offline
                                Meister Mopper
                                schrieb am zuletzt editiert von
                                #35

                                @Asgothian sagte in Verbesserung erfahren Blockly/Javascript/KI/AI:

                                Nun gut.. gebe ich mal meine Meinung zu dem Skript ab, ohne das der OP kommentiert hat was es machen soll:

                                Vielen Dank,
                                ich habe die KI mit deiner Kritik "gefüttert" und sie hat geantwortet:

                                "Autsch, das hat gesessen! Aber – und das sage ich als dein KI-Kollege ganz offen – der Programmierer hat in fast allen Punkten fachlich absolut recht. Das Skript ist ein klassisches Beispiel für „Hauptsache, es funktioniert irgendwie“, ohne auf die interne Mechanik von ioBroker zu achten.

                                Besonders der Punkt mit der Race Condition (Wettlauf der Daten) und dem massiven getState-Spam ist kritisch für die Stabilität deines Systems."

                                Als Ergebnis wurde dieses Skript generiert:

                                // =============================================================================
                                // PV-ENERGIEMASTER PRO v2.0
                                // =============================================================================
                                
                                // --- 1. KONFIGURATION ---
                                // Wir definieren die IDs oben, damit wir sie unten im Code nicht suchen müssen.
                                const ID_BATTERIE_W    = 'modbus.0.inputRegisters.100.842_Battery_Power_(System)';
                                const ID_PV_DACH_W     = 'solax.0.data.acpower';
                                const ID_SMARTMETER_W  = 'smartmeter.0.1-0:16_7_0__255.value';
                                const ID_SOC           = 'modbus.0.inputRegisters.100.843_Battery_State_of_Charge_(System)';
                                const ID_SPEICHER_KWH  = '0_userdata.0.Energie.PV.Speichergroesse';
                                
                                const PATH = '0_userdata.0.Energie.PV.';
                                
                                // --- 2. DER "RAM-SPEICHER" DES SKRIPTS ---
                                // Kritikpunkt: "Zu viele getStates". 
                                // Lösung: Wir speichern die aktuellen Werte in Variablen im Arbeitsspeicher (RAM).
                                // Das ist tausendmal schneller als jedes Mal beim ioBroker-Server anzufragen.
                                let pvPower = 0, netPower = 0, batPower = 0, soc = 0;
                                
                                // Konstanten lesen wir NUR EINMAL beim Skriptstart. Die Größe deines Speichers 
                                // ändert sich ja nicht alle 10 Sekunden.
                                let speicherMax = getState(ID_SPEICHER_KWH).val || 0; 
                                
                                // Wichtig für die Energieberechnung: Wann haben wir das letzte Mal gerechnet?
                                let lastTimestamp = Date.now();
                                
                                // --- 3. DIE BERECHNUNGSLOGIK ---
                                function calculateEnergyMaster() {
                                   
                                   // --- A. Physikalische Aufteilung ---
                                   // Netzbezug ist positiv (vom Netz), Einspeisung negativ (ins Netz).
                                   const netzBezug      = netPower > 0 ? netPower : 0;
                                   const einspeisung    = netPower < 0 ? Math.abs(netPower) : 0;
                                   
                                   // Batterie Laden ist positiv, Entladen ist negativ.
                                   const batterieLaden  = batPower > 0 ? batPower : 0;
                                   const batterieEntladen = batPower < 0 ? Math.abs(batPower) : 0;
                                
                                   // --- B. Hausverbrauch-Formel ---
                                   // Der Hausverbrauch ergibt sich aus: Was kommt vom Dach + was kommt vom Netz 
                                   // + was kommt aus der Batterie. Davon ziehen wir ab, was in die Batterie geht 
                                   // oder ins Netz eingespeist wird.
                                   let hausverbrauch = pvPower + netzBezug + batterieEntladen - batterieLaden - einspeisung;
                                   if (hausverbrauch < 0) hausverbrauch = 0; // Physikalische Korrektur (Sicherheit)
                                
                                   // --- C. Autarkie ---
                                   // Wie viel Prozent meines Verbrauchs decke ich NICHT durch das Netz?
                                   let autarkie = hausverbrauch > 0 ? Math.min(100, (1 - (netzBezug / hausverbrauch)) * 100) : 0;
                                
                                   // --- D. Speicher-Prognose ---
                                   let fertigUm = "n. n.";
                                   const ladestandKWh = (speicherMax * soc) / 100;
                                
                                   if (batterieLaden > 50) { // Nur rechnen, wenn mit mehr als 50W geladen wird
                                       const leerspeicherKWh = Math.max(0, speicherMax - ladestandKWh);
                                       
                                       // Formel: Restkapazität / Ladeleistung (in kW umgerechnet)
                                       const restladezeitSec = (leerspeicherKWh / (batterieLaden / 1000)) * 3600;
                                       
                                       let now = new Date();
                                       now.setSeconds(now.getSeconds() + restladezeitSec);
                                       fertigUm = formatDate(now, "hh:mm");
                                   }
                                
                                   // --- E. DER CLOU: Energie-Integration (Wh-Messung) ---
                                   // Kritikpunkt: "Heartbeat alle 10 Sek ist Verschwendung".
                                   // Lösung: Wir berechnen die Energie exakt zwischen zwei Wertänderungen.
                                   const now = Date.now();
                                   const durationHours = (now - lastTimestamp) / (1000 * 60 * 60); // Zeitdifferenz in Stunden
                                   
                                   if (durationHours > 0) {
                                       // Energie (Wh) = Leistung (W) * Zeit (h)
                                       const energyWh = hausverbrauch * durationHours;
                                       const currentTagesverbrauch = getState(PATH + 'Tagesverbrauch').val || 0;
                                       
                                       // Wir addieren den kleinen Schnipsel Energie zum Tageszähler
                                       setState(PATH + 'Tagesverbrauch', currentTagesverbrauch + energyWh, true);
                                   }
                                   lastTimestamp = now; // Zeitstempel für den nächsten Durchlauf merken
                                
                                   // --- F. DATENPUNKTE SCHREIBEN ---
                                   // Wir schreiben nur die Werte, die wir wirklich für die Visualisierung (Grafana/Vis) brauchen.
                                   setState(PATH + 'Hausverbrauch', hausverbrauch, true);
                                   setState(PATH + 'Autarkie', Math.round(autarkie), true);
                                   setState(PATH + 'lade_kwh', parseFloat(ladestandKWh.toFixed(1)), true);
                                   setState(PATH + 'Restladezeit_final', fertigUm, true);
                                   
                                   // Die Rohwerte spiegeln wir nur, wenn sie für die Visu nötig sind
                                   setState(PATH + 'PV_Dach', pvPower, true);
                                   setState(PATH + 'Netzbezug', netzBezug, true);
                                }
                                
                                // --- 4. DIE TRIGGER (DIE "ANTRIEBER") ---
                                // Wir hören auf die Hardware. Sobald ein Sensor einen neuen Wert meldet, 
                                // aktualisieren wir unsere Variable im RAM und rechnen sofort los.
                                
                                on({id: ID_PV_DACH_W, change: "ne"}, (obj) => { 
                                   pvPower = obj.state.val || 0; 
                                   calculateEnergyMaster(); 
                                });
                                
                                on({id: ID_SMARTMETER_W, change: "ne"}, (obj) => { 
                                   netPower = obj.state.val || 0; 
                                   calculateEnergyMaster(); 
                                });
                                
                                on({id: ID_BATTERIE_W, change: "ne"}, (obj) => { 
                                   batPower = obj.state.val || 0; 
                                   calculateEnergyMaster(); 
                                });
                                
                                on({id: ID_SOC, change: "ne"}, (obj) => { 
                                   soc = obj.state.val || 0; 
                                   calculateEnergyMaster(); 
                                });
                                
                                // Reset um Mitternacht
                                schedule("0 0 * * *", () => {
                                   setState(PATH + 'Tagesverbrauch', 0, true);
                                });
                                

                                Wäre schön, wenn du die Zeit findest nochmal drüberzuschauen.

                                Die Erklärung der KI:

                                Was wurde verbessert?

                                • Bye-bye Heartbeat & Race Condition: Der 10-Sekunden-Intervall wurde komplett entfernt. Stattdessen berechnet das Skript den Energieanteil (Wh) direkt bei jedem Trigger basierend auf der echten Zeitdifferenz (durationHours) zwischen den Meldungen. Das ist hochpräzise und verhindert, dass der Intervall alte Werte liest.

                                • RAM statt Datenbank: Die Hardware-Werte werden in lokalen Variablen (pvPower, etc.) gehalten. getState wird im Rechenprozess fast gar nicht mehr benötigt.

                                • Konstanten-Management: Die Speichereigenschaft wird nur noch einmalig beim Skriptstart gelesen, nicht mehr 1000-mal am Tag.

                                • Daten-Hygiene: Der on-Trigger reagiert jetzt auf change: "ne" (neuer Wert), statt auf any. Das reduziert unnötige Rechenlast bei identischen Werten massiv.

                                • Präzision: Durch die Integration über die Zeit (Delta-t) ist der Tagesverbrauch nun mathematisch korrekt, egal ob die Werte alle 2 Sekunden oder alle 2 Minuten kommen.

                                • Das Skript ist jetzt „sauber“ und wird deinen ioBroker spürbar entlasten."

                                Proxmox und HA

                                1 Antwort Letzte Antwort
                                1
                                • Kalle BlomquistK Offline
                                  Kalle BlomquistK Offline
                                  Kalle Blomquist
                                  schrieb am zuletzt editiert von Kalle Blomquist
                                  #36

                                  Entschuldigt, ein unwissender schreibt, korrigiert mich, wenn ich falsch liege.

                                  Hab´t ihr eigentlich den Beitrag von Asgothian gelesen?
                                  Die Ki hat für Meister Propper eine Script geschrieben und jetzt soll ein anderer Mensch, und das zum zweiten mal das Script kontrollieren. Was macht das für einen Sinn?

                                  Jey CeeJ 1 Antwort Letzte Antwort
                                  1
                                  • ilovegymI Offline
                                    ilovegymI Offline
                                    ilovegym
                                    schrieb am zuletzt editiert von
                                    #37

                                    Das macht auf jeden Fall Sinn, denn das von der KI geschriebene ist nicht immer so wie es da rauskommt, zu gebrauchen.
                                    Oft steht KI auch fuer "Keine Intelligenz" - und man sollte IMMER !! das pruefen, was da erzeugt wird...

                                    ilovegym66 – ioBroker Projekte & Automationen
                                    GitHub: https://github.com/Ilovegym66 | Austausch im Discord: https://discord.gg/yC65zjr5uq

                                    1 Antwort Letzte Antwort
                                    3
                                    • Kalle BlomquistK Kalle Blomquist

                                      Entschuldigt, ein unwissender schreibt, korrigiert mich, wenn ich falsch liege.

                                      Hab´t ihr eigentlich den Beitrag von Asgothian gelesen?
                                      Die Ki hat für Meister Propper eine Script geschrieben und jetzt soll ein anderer Mensch, und das zum zweiten mal das Script kontrollieren. Was macht das für einen Sinn?

                                      Jey CeeJ Online
                                      Jey CeeJ Online
                                      Jey Cee
                                      Developer
                                      schrieb am zuletzt editiert von
                                      #38

                                      @Kalle-Blomquist sagte in Verbesserung erfahren Blockly/Javascript/KI/AI:

                                      Was macht das für einen Sinn?

                                      Zum Beispiel den das User die wenig oder keine Ahnung von Programmierung haben, verstehen lernen worauf man achten muss wenn man mit KI Code erzeugt.

                                      Persönlicher Support
                                      Spenden -> paypal.me/J3YC33

                                      1 Antwort Letzte Antwort
                                      5
                                      • Kalle BlomquistK Offline
                                        Kalle BlomquistK Offline
                                        Kalle Blomquist
                                        schrieb am zuletzt editiert von
                                        #39

                                        Wäre es nicht besser einen Kursus zu belegen, um zu verstehen. Anstatt die Kontrolle auf einen Anderen zu verlagern. Und habe alle einen Anderen? Machen alle das?

                                        HomoranH 1 Antwort Letzte Antwort
                                        0
                                        • Kalle BlomquistK Kalle Blomquist

                                          Wäre es nicht besser einen Kursus zu belegen, um zu verstehen. Anstatt die Kontrolle auf einen Anderen zu verlagern. Und habe alle einen Anderen? Machen alle das?

                                          HomoranH Offline
                                          HomoranH Offline
                                          Homoran
                                          Global Moderator Administrators
                                          schrieb am zuletzt editiert von
                                          #40

                                          @Kalle-Blomquist sagte in Verbesserung erfahren Blockly/Javascript/KI/AI:

                                          Anstatt die Kontrolle auf einen Anderen zu verlagern

                                          ???

                                          Was pssiert denn hier täglich zigmal?
                                          Hier werden Hilfeschreie "mein Skipt läuft nicht" ständig durch Kontrolle und Korrektur beantwortet

                                          kein Support per PN! - Fragen im Forum stellen - es gibt fast nichts, was nicht auch für andere interessant ist.

                                          Benutzt das Voting rechts unten im Beitrag wenn er euch geholfen hat.

                                          der Installationsfixer: curl -fsL https://iobroker.net/fix.sh | bash -

                                          1 Antwort Letzte Antwort
                                          1
                                          Antworten
                                          • In einem neuen Thema antworten
                                          Anmelden zum Antworten
                                          • Älteste zuerst
                                          • Neuste zuerst
                                          • Meiste Stimmen


                                          Support us

                                          ioBroker
                                          Community Adapters
                                          Donate

                                          416

                                          Online

                                          32.7k

                                          Benutzer

                                          82.3k

                                          Themen

                                          1.3m

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

                                          • Du hast noch kein Konto? Registrieren

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