Navigation

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

    NEWS

    • Monatsrückblick – September 2025

    • Neues Video "KI im Smart Home" - ioBroker plus n8n

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

    H
    • Profile
    • Following 0
    • Followers 0
    • Topics 60
    • Posts 633
    • Best 16
    • Groups 2

    hotspot_2

    @hotspot_2

    Starter

    17
    Reputation
    55
    Profile views
    633
    Posts
    0
    Followers
    0
    Following
    Joined Last Online

    hotspot_2 Follow
    Pro Starter

    Best posts made by hotspot_2

    • RE: Shellys ("Alt und Plus") über MQTT Adapter

      @mickym Das mitgeben des Payload Datentyps in der iobroker-out löst das Problem. Dann funktioniert es bestens.

      18 Plus1 habe ich schon drin, funktioniert alles bestens.

      posted in ioBroker Allgemein
      H
      hotspot_2
    • RE: Matter-Controller (Matter Geräte in ioBroker einbinden)

      @apollon77 Hallo, ich habe den Bug bei AVM gemeldet (Ticket-ID 6666976).

      Ich werde berichten falls es dann Ergebnisse / Erkenntnisse gibt.

      posted in ioBroker Allgemein
      H
      hotspot_2
    • RE: Grafana Stromverbrauch richtig Darstellen Shelly Plug S

      @spacerx

      Danke für den Hinweis. Das mit dem Skript, speichern usw. hab ich schon alles am laufen und selbst recherchiert. Grafana tue ich mir noch etwas schwer und hatte mir gedacht das vielleicht irgendjemand etwas hat an dem man sich orientieren kann. Diagramme darstellen, Werte darstellen usw. das hab ich auch hinbekommen, aber ein erweitertes Grafana Panel (z.B. mit mehr Werten, Funktionen usw.), dachte ich mir, wäre mal interessant.

      😉

      posted in Grafana
      H
      hotspot_2
    • RE: jarvis v3.0.0 - just another remarkable vis

      @mcu Ok, alles klar. Ich hab jetzt einfach einen Export des kompletten Jarvis Baums in den Objekten gemacht.

      posted in Tester
      H
      hotspot_2
    • RE: Shellys ("Alt und Plus") über MQTT Adapter

      @mickym Danke!

      5caaa48a-45b6-4375-9132-2d82ade8b3f9-image.png

      So sieht meine i3 Einbindung jetzt aus.

      Macht insgesamt richtig Spaß. Toll das Du mir das gesamte Vorgehen gezeigt hast, kommt man viel besser rein in die Thematik.

      posted in ioBroker Allgemein
      H
      hotspot_2
    • RE: Support Adapter Energiefluss-erweitert v0.7.7

      @homoran Das mit den Werten vom Equipment hat einwandfrei geklappt. Ich hatte noch den Punkt das ich in einem Kasten mit der Füllung anzeigen wieviel von der PV maximalen PV Leistung aktuell erbracht wird. Hat am Anfang nicht funktioniert bis ich dann doch noch entdeckt habe das man von Prozent auf Wert umsteigen kann und nun klappt alles hervorragend.

      posted in Visualisierung
      H
      hotspot_2
    • RE: jarvis v3.2.x - just another remarkable vis

      @mcu Alles klar, hat nichts geholfen. Hab jetzt ein issue erstellt.

      posted in Tester
      H
      hotspot_2
    • RE: go-e Adapter

      @nik82 sagte in Go e-charger:

      @hotspot_2
      Haben wir doch genau in den letzten zwei Seiten besprochen.
      Nimm doch das interne Script vom Adapter selber, läuft super.

      Perfekt. Danke! Dann teste ich das mal so und schaue mal.

      Um das zu prüfen kann ich mir ja alle Werte anzeigen lassen in JARVIS oder so.

      posted in ioBroker Allgemein
      H
      hotspot_2
    • RE: Support Adapter Energiefluss-erweitert v0.7.7

      @int17 sagte in Support Adapter Energiefluss-erweitert v0.6.2:

      Dann habe ich in meinem iobroker gesehen, dass der Adapter in der Übersicht zweimal gelistet ist, obwohl nur eine Instanz da ist.
      Doppelt.jpg

      Dei Frage mit der zweiten Instanz kann ich DIr beantworten, da ich sie auch schon gestellt habe. Wenn Du den Cloud Adapter oder eine zweite Webinstanz installiert hast dann taucht der energiefluss-erweiter Adapter in der Übersicht zweimal auf. Ist bei mir auch so es läuft aber nur eine Instanz des Adapters (das kannst Du unter Instanzen selbst mal prüfen).

      posted in Visualisierung
      H
      hotspot_2
    • RE: Welches Kabel / Litze für Eigenbau Temperaturfühler nodemcu

      @da_woody Super. Besten Dank! Ich habe jetzt mal 0,32 mm bestellt.

      Vielen Dank, bin gespannt ob alles funktioniert ;-).

      posted in Hardware
      H
      hotspot_2

    Latest posts made by hotspot_2

    • RE: Skript läuft plötzlich nicht mehr

      So, bin - mein Eindruck - ein deutliches Stück weiter gekommen. Ich habe ein Regelwerk festgelegt das wir nun für die Skript Aktivitäten nutzen und uns auf verschiedene Dinge verständigt (wie z.B. Versionierung usw.). Auch wann es sinnvoll ist Helper (functions) anzulegen und wann nicht.

      Das geht in die richtige Richtung und ich lerne auch was dabei.

      Macht immer mehr Spaß das Ganze.

      /**
       * BKW Live Power + Daily/Total Energy (MQTT push)
       * Version: 0.1.0
       * Changelog:
       * - 0.1.0 (2025-10-01) Start of versioning; compact event-driven integration; self-consumption in mW; no existence checks
       *
       * Summary:
       * - Reads Shelly MQTT JSON (status.switch:0 → apower in W)
       * - Net per BKW = max(0, apower_W - self_W); self is provided in mW (integer)
       * - Integrates energy event-based using full elapsed time (Δt) since last event
       * - Daily close at 00:00: writes kWh per BKW + total (Grafana) and updates cumulative totals
       *
       * Notes:
       * - Assumes all datapoints exist; no auto-creation, no existence checks
       * - Rounds kWh once when writing the kWh states
       */
      
      // ========================== Configuration ==========================
      
      /** Common prefix for all user states */
      const PREFIX = '0_userdata.0.pvundstrom.bkws.';
      
      /** Shelly MQTT inputs (JSON strings with field "apower") */
      const MQTT_JSON_BKW1 = 'mqtt.0.shellies.sonstiges.bkw1.status.switch:0';
      const MQTT_JSON_BKW2 = 'mqtt.0.shellies.sonstiges.bkw2.status.switch:0';
      
      /** WR self-consumption (mW, integer, e.g. 400 → 0.4 W) */
      const SELF_BKW1_mW = PREFIX + '1.wr_selfconsumption_mw';
      const SELF_BKW2_mW = PREFIX + '2.wr_selfconsumption_mw';
      
      /** Live power outputs (W, NET after self-consumption) */
      const OUT_PWR_BKW1 = PREFIX + '1.power_w';
      const OUT_PWR_BKW2 = PREFIX + '2.power_w';
      const OUT_PWR_ALL  = PREFIX + 'all.power_w';
      
      /** Energy states BKW1 */
      const BKW1_TODAY_Wh      = PREFIX + '1.today_wh';
      const BKW1_TODAY_kWh     = PREFIX + '1.today_kwh';
      const BKW1_LASTTS_MS     = PREFIX + '1.lastts_ms';
      const BKW1_GRAFANA_DAILY = PREFIX + '1.grafana_daily_kwh';
      const BKW1_TOTAL_KWH     = PREFIX + '1.total_kwh';
      
      /** Energy states BKW2 */
      const BKW2_TODAY_Wh      = PREFIX + '2.today_wh';
      const BKW2_TODAY_kWh     = PREFIX + '2.today_kwh';
      const BKW2_LASTTS_MS     = PREFIX + '2.lastts_ms';
      const BKW2_GRAFANA_DAILY = PREFIX + '2.grafana_daily_kwh';
      const BKW2_TOTAL_KWH     = PREFIX + '2.total_kwh';
      
      /** Aggregated energy states */
      const OUT_TOTAL_TODAY_Wh  = PREFIX + 'all.today_wh';
      const OUT_TOTAL_TODAY_kWh = PREFIX + 'all.today_kwh';
      const OUT_TOTAL_GRAFANA   = PREFIX + 'all.grafana_daily_kwh';
      const OUT_TOTAL_CUM_KWH   = PREFIX + 'all.total_kwh';
      
      /** Tolerances & formatting */
      const EPS_W         = 1;   // write threshold for live power
      const ROUND_DISPLAY = 3;   // kWh rounding for display/storage
      const RUN_AT_0005   = false; // optional second daily close at 00:05
      
      /** Debug switch */
      const DEBUG = false;
      function dbg(msg) { if (DEBUG) log('[BKW] ' + msg); }
      
      // Device map
      const DEVICES = [
        {
          name: 'bkw1',
          inJsonId: MQTT_JSON_BKW1,
          selfMwId: SELF_BKW1_mW,
          outId:    OUT_PWR_BKW1,
          todayWh:  BKW1_TODAY_Wh,
          todayKWh: BKW1_TODAY_kWh,
          lastTs:   BKW1_LASTTS_MS,
          grafDaily:BKW1_GRAFANA_DAILY,
          totalKWh: BKW1_TOTAL_KWH,
        },
        {
          name: 'bkw2',
          inJsonId: MQTT_JSON_BKW2,
          selfMwId: SELF_BKW2_mW,
          outId:    OUT_PWR_BKW2,
          todayWh:  BKW2_TODAY_Wh,
          todayKWh: BKW2_TODAY_kWh,
          lastTs:   BKW2_LASTTS_MS,
          grafDaily:BKW2_GRAFANA_DAILY,
          totalKWh: BKW2_TOTAL_KWH,
        },
      ];
      
      // ========================== Lightweight helpers ==========================
      
      /** Number coercion with fallback */
      function toNumber(v, fb = 0) { const n = Number(v); return Number.isFinite(n) ? n : fb; }
      
      /** Changed-only numeric write with epsilon */
      function setIfChangedNumber(id, next, eps = 0, ack = true) {
        const cur = Number(getState(id)?.val);
        const within = Number.isFinite(cur) ? Math.abs(cur - next) <= eps : false;
        if (!within) setState(id, next, ack);
      }
      
      /** Parse Shelly JSON → apower (W), clamped to >=0 */
      function parseApowerW(jsonVal) {
        let ap = 0;
        if (typeof jsonVal === 'string') {
          try { ap = Number(JSON.parse(jsonVal)?.apower) || 0; }
          catch (e) {
            const sample = jsonVal.slice(0, 120);
            log('[BKW] JSON parse failed (' + sample + '): ' + e.message, 'warn');
          }
        }
        if (!Number.isFinite(ap) || ap < 0) ap = 0;
        return ap;
      }
      
      /** Read self-consumption in W from mW state (>=0) */
      function readSelfW(id) {
        const mw = toNumber(getState(id)?.val, 0);
        return mw > 0 ? (mw / 1000) : 0;
      }
      
      // ========================== Aggregate live power ==========================
      let aggTimer = null;
      function scheduleAggregateLive() {
        if (aggTimer) return;
        aggTimer = setTimeout(() => {
          aggTimer = null;
          try {
            let sum = 0;
            for (const d of DEVICES) {
              const v = toNumber(getState(d.outId)?.val, 0);
              if (v > 0) sum += v;
            }
            if (sum < 0) sum = 0;
            setIfChangedNumber(OUT_PWR_ALL, sum, EPS_W, true);
          } catch (e) {
            log('[BKW] aggregate update failed: ' + (e && e.message ? e.message : e), 'warn');
          }
        }, 300); // small debounce for aggregate
      }
      
      // ========================== Energy integration per device ==========================
      const lastTsCache = new Map();
      
      /**
       * Integrate energy using full Δt since last event; update live power and daily totals.
       * @param {*} dev device entry from DEVICES
       * @param {number} apowerW AC power from Shelly (W, >=0)
       * @param {number} selfW   WR self-consumption (W, >=0)
       */
      function integrate(dev, apowerW, selfW) {
        const now = Date.now();
        let last = lastTsCache.get(dev.name);
        if (!Number.isFinite(last)) last = toNumber(getState(dev.lastTs)?.val, now);
      
        // Full elapsed time since last event (no cap), protect against negative
        let dt = now - last;
        if (dt < 0) dt = 0;
      
        // Advance lastTs immediately to avoid Δt growth on next event
        setState(dev.lastTs, now, true);
        lastTsCache.set(dev.name, now);
      
        // Net power (never negative)
        const P = apowerW > 0 ? apowerW : 0;
        const S = selfW   > 0 ? selfW   : 0;
        const netW = Math.max(0, P - S);
      
        // Live power (changed-only)
        setIfChangedNumber(dev.outId, netW, EPS_W, true);
      
        // No energy when net power is zero
        if (netW <= 0) return;
      
        // W * (ms → h) = Wh
        const WhInc = netW * (dt / 3600000);
        let todayWh = toNumber(getState(dev.todayWh)?.val, 0) + WhInc;
        if (todayWh < 0) todayWh = 0;
      
        const todayKWh = todayWh / 1000;
      
        setState(dev.todayWh,  todayWh, true);
        setState(dev.todayKWh, Number(todayKWh.toFixed(ROUND_DISPLAY)), true);
      
        // Update aggregated "today"
        let sumWh = 0;
        for (const d2 of DEVICES) {
          const v = toNumber(getState(d2.todayWh)?.val, 0);
          if (v > 0) sumWh += v;
        }
        if (sumWh < 0) sumWh = 0;
      
        setState(OUT_TOTAL_TODAY_Wh,  sumWh, true);
        setState(OUT_TOTAL_TODAY_kWh, Number((sumWh / 1000).toFixed(ROUND_DISPLAY)), true);
      
        dbg(`[${dev.name}] P=${P}W, self=${S}W, net=${netW}W, dt=${dt}ms, Wh+=${WhInc.toFixed(3)}`);
      }
      
      // ========================== Init & Event binding ==========================
      (function init() {
        try {
          // Initialize live power and lastTs cache from current states
          for (const d of DEVICES) {
            const ap   = parseApowerW(getState(d.inJsonId)?.val);
            const self = readSelfW(d.selfMwId);
            const net  = Math.max(0, ap - self);
            setIfChangedNumber(d.outId, net, EPS_W, true);
      
            const lastStored = toNumber(getState(d.lastTs)?.val, NaN);
            lastTsCache.set(d.name, Number.isFinite(lastStored) ? lastStored : Date.now());
          }
          scheduleAggregateLive();
      
          // Event listeners
          for (const d of DEVICES) {
            // MQTT JSON changed or re-written → integrate
            on({ id: d.inJsonId, change: 'any' }, (obj) => {
              try {
                const ap   = parseApowerW(obj?.state?.val);
                const self = readSelfW(d.selfMwId);
                integrate(d, ap, self);
                scheduleAggregateLive();
              } catch (e) {
                log('[BKW] onJSON ' + d.name + ' failed: ' + (e && e.message ? e.message : e), 'warn');
              }
            });
      
            // Self-consumption changed (mW) → recompute/integrate at current apower
            on({ id: d.selfMwId, change: 'ne' }, () => {
              try {
                const ap   = parseApowerW(getState(d.inJsonId)?.val);
                const self = readSelfW(d.selfMwId);
                integrate(d, ap, self);
                scheduleAggregateLive();
              } catch (e) {
                log('[BKW] onSelf ' + d.name + ' failed: ' + (e && e.message ? e.message : e), 'warn');
              }
            });
          }
      
          log('[BKW] Live power + energy script started (MQTT push).', 'info');
        } catch (e) {
          log('[BKW] init failed: ' + (e && e.message ? e.message : e), 'error');
        }
      })();
      
      // ========================== Daily close ==========================
      /**
       * Daily close: write yesterday's kWh (ts = prev day 23:59:59.999),
       * accumulate totals, reset today's counters and lastTs.
       */
      function dailyClose() {
        try {
          const tsStartToday = new Date().setHours(0, 0, 0, 0);
          const tsPrevDayEnd = tsStartToday - 1;
      
          let totalDayKWh = 0;
      
          for (const d of DEVICES) {
            let dayKWh = toNumber(getState(d.todayKWh)?.val, 0);
            if (dayKWh < 0) dayKWh = 0;
      
            // Grafana daily (fixed timestamp)
            setState(d.grafDaily, { val: Number(dayKWh.toFixed(ROUND_DISPLAY)), ts: tsPrevDayEnd }, true);
      
            // Cumulative per device
            const cum = Math.max(0, toNumber(getState(d.totalKWh)?.val, 0) + dayKWh);
            setState(d.totalKWh, Number(cum.toFixed(ROUND_DISPLAY)), true);
      
            // Running totals reset
            setState(d.todayWh,  0, true);
            setState(d.todayKWh, 0, true);
      
            // Refresh lastTs
            const now = Date.now();
            setState(d.lastTs, now, true);
            lastTsCache.set(d.name, now);
      
            totalDayKWh += dayKWh;
          }
      
          if (totalDayKWh < 0) totalDayKWh = 0;
      
          // Aggregated daily + cumulative
          setState(OUT_TOTAL_GRAFANA, { val: Number(totalDayKWh.toFixed(ROUND_DISPLAY)), ts: tsPrevDayEnd }, true);
      
          const totalCum = Math.max(0, toNumber(getState(OUT_TOTAL_CUM_KWH)?.val, 0) + totalDayKWh);
          setState(OUT_TOTAL_CUM_KWH, Number(totalCum.toFixed(ROUND_DISPLAY)), true);
      
          // Reset aggregated "today"
          setState(OUT_TOTAL_TODAY_Wh,  0, true);
          setState(OUT_TOTAL_TODAY_kWh, 0, true);
      
          log(`[BKW] Daily close: total=${totalDayKWh.toFixed(ROUND_DISPLAY)} kWh; cum=${totalCum.toFixed(ROUND_DISPLAY)} kWh`);
        } catch (e) {
          log('[BKW] daily close failed: ' + (e && e.message ? e.message : e), 'warn');
        }
      }
      
      // Cron 00:00
      schedule('0 0 * * *', dailyClose);
      
      // Optional 00:05
      if (RUN_AT_0005) {
        schedule('5 0 * * *', () => {
          try { dailyClose(); log('[BKW] 00:05 refresh executed.'); }
          catch (e) { log('[BKW] 00:05 error: ' + e, 'warn'); }
        });
      }
      
      posted in JavaScript
      H
      hotspot_2
    • RE: Skript läuft plötzlich nicht mehr

      Jetzt habe ich mal einen ernsten Ton genutzt und die Regeln übermittelt bestimmt.

      Das Ergebnis sieht für meine Augen schon besser aus.

      // ==================================================
      // BKW Live Power + Daily/Total Energy (no-create, MQTT push)
      // - Reads Shelly MQTT JSON (status.switch:0 → apower)
      // - Net power per BKW = max(0, apower - wr_selfconsumption_w)
      // - Event-based daily energy integration (no polling)
      // - Daily close at 00:00: writes kWh per BKW + total (Grafana), updates TOTAL_KWH
      // - No state creation/history; writes only if targets exist
      // ==================================================
      
      // ---------- CONFIG ----------
      
      // Common prefix for all user states
      const PREFIX = '0_userdata.0.pvundstrom.bkws.';
      
      // MQTT inputs
      const MQTT_JSON_BKW1 = 'mqtt.0.shellies.sonstiges.bkw1.status.switch:0';
      const MQTT_JSON_BKW2 = 'mqtt.0.shellies.sonstiges.bkw2.status.switch:0';
      
      // Self-consumption per BKW (W)
      const SELF_BKW1 = PREFIX + '1.wr_selfconsumption_w';
      const SELF_BKW2 = PREFIX + '2.wr_selfconsumption_w';
      
      // Live power outputs (W)
      const OUT_PWR_BKW1 = PREFIX + '1.power_w';
      const OUT_PWR_BKW2 = PREFIX + '2.power_w';
      const OUT_PWR_ALL  = PREFIX + 'all.power_w';
      
      // Energy states BKW1
      const BKW1_TODAY_Wh      = PREFIX + '1.today_wh';
      const BKW1_TODAY_kWh     = PREFIX + '1.today_kwh';
      const BKW1_LASTTS_MS     = PREFIX + '1.lastts_ms';
      const BKW1_GRAFANA_DAILY = PREFIX + '1.grafana_daily_kwh';
      const BKW1_TOTAL_KWH     = PREFIX + '1.total_kwh';
      
      // Energy states BKW2
      const BKW2_TODAY_Wh      = PREFIX + '2.today_wh';
      const BKW2_TODAY_kWh     = PREFIX + '2.today_kwh';
      const BKW2_LASTTS_MS     = PREFIX + '2.lastts_ms';
      const BKW2_GRAFANA_DAILY = PREFIX + '2.grafana_daily_kwh';
      const BKW2_TOTAL_KWH     = PREFIX + '2.total_kwh';
      
      // Aggregated energy states
      const OUT_TOTAL_TODAY_Wh  = PREFIX + 'all.today_wh';
      const OUT_TOTAL_TODAY_kWh = PREFIX + 'all.today_kwh';
      const OUT_TOTAL_GRAFANA   = PREFIX + 'all.grafana_daily_kwh';
      const OUT_TOTAL_CUM_KWH   = PREFIX + 'all.total_kwh';
      
      const EPS_W = 1;
      const AGG_DEBOUNCE_MS = 300;
      const MAX_DT_MS = 5000;
      const FALLBACK_DT_MS = 1000;
      const ROUND_DISPLAY = 3;
      const RUN_AT_0005 = false;
      
      // Debug switch
      const DEBUG = false;
      function dbg(msg) { if (DEBUG) log('[BKW] ' + msg); }
      
      // ---------- DEVICE MAP ----------
      const DEVICES = [
        {
          name: 'bkw1',
          inJsonId: MQTT_JSON_BKW1,
          selfId:   SELF_BKW1,
          outId:    OUT_PWR_BKW1,
          todayWh:  BKW1_TODAY_Wh,
          todayKWh: BKW1_TODAY_kWh,
          lastTs:   BKW1_LASTTS_MS,
          grafDaily:BKW1_GRAFANA_DAILY,
          totalKWh: BKW1_TOTAL_KWH,
        },
        {
          name: 'bkw2',
          inJsonId: MQTT_JSON_BKW2,
          selfId:   SELF_BKW2,
          outId:    OUT_PWR_BKW2,
          todayWh:  BKW2_TODAY_Wh,
          todayKWh: BKW2_TODAY_kWh,
          lastTs:   BKW2_LASTTS_MS,
          grafDaily:BKW2_GRAFANA_DAILY,
          totalKWh: BKW2_TOTAL_KWH,
        },
        // add more BKWs with same structure if needed
      ];
      
      // ---------- HELPERS ----------
      /**
       * Convert to finite number with fallback.
       * @param {any} value
       * @param {number} fallback
       * @returns {number}
       */
      function toNumberOr(value, fallback) {
        const num = Number(value);
        return Number.isFinite(num) ? num : (Number.isFinite(fallback) ? fallback : 0);
      }
      
      const existsCache = new Map();
      /**
       * Check object existence (cached). Warn once if missing.
       * @param {string} id
       * @returns {boolean}
       */
      function objectExists(id) {
        if (existsCache.has(id)) return existsCache.get(id);
        const ok = !!getObject(id);
        existsCache.set(id, ok);
        if (!ok) log('[BKW] Missing object: ' + id, 'warn');
        return ok;
      }
      
      /**
       * Write only if object exists AND the value changed beyond epsilon.
       * Allows explicit write of zero even within epsilon.
       * @param {string} id
       * @param {any} nextVal
       * @param {number} eps
       * @param {boolean} ack
       */
      function writeIfChanged(id, nextVal, eps, ack) {
        if (!objectExists(id)) return;
        const cur = getState(id);
        const curVal = cur ? cur.val : undefined;
      
        if (typeof nextVal === 'number') {
          const curNum = Number(curVal);
          if (Number.isFinite(curNum)) {
            const withinEps = Math.abs(curNum - nextVal) <= eps;
            const zeroOverride = nextVal === 0 && curNum > 0;
            if (withinEps && !zeroOverride) return;
          }
        } else if (curVal === nextVal) {
          return;
        }
        setState(id, nextVal, ack);
      }
      
      /**
       * Write only if object exists (no change check).
       * @param {string} id
       * @param {any} nextVal
       * @param {boolean} ack
       */
      function writeIfExists(id, nextVal, ack) {
        if (!objectExists(id)) return;
        setState(id, nextVal, ack);
      }
      
      // ---------- AGGREGATE POWER ----------
      let aggTimer = null;
      /** Debounced update of aggregate power state. */
      function scheduleAggregateUpdate() {
        if (aggTimer) return;
        aggTimer = setTimeout(function () {
          aggTimer = null;
          try {
            let sum = 0;
            for (let i = 0; i < DEVICES.length; i++) {
              const d = DEVICES[i];
              if (!objectExists(d.outId)) continue;
              const s = getState(d.outId);
              sum += s ? toNumberOr(s.val, 0) : 0;
            }
            writeIfChanged(OUT_PWR_ALL, sum, EPS_W, true);
          } catch (e) {
            log('[BKW] aggregate update failed: ' + (e && e.message ? e.message : e), 'warn');
          }
        }, AGG_DEBOUNCE_MS);
      }
      
      // ---------- ENERGY INTEGRATION ----------
      const lastTsCache = new Map();
      
      /**
       * Integrate device energy based on elapsed time and net power.
       * Updates: lastTs, todayWh, todayKWh and aggregated today totals.
       * @param {{name:string,lastTs:string,todayWh:string,todayKWh:string}} dev
       * @param {number} netPowerW
       */
      function integrateDeviceEnergy(dev, netPowerW) {
        const now = Date.now();
        let last = lastTsCache.get(dev.name);
        if (!Number.isFinite(last)) last = toNumberOr(getState(dev.lastTs)?.val, now);
      
        let dt = now - last;
        if (dt < 0 || dt > MAX_DT_MS) {
          dt = FALLBACK_DT_MS;
          dbg('[' + dev.name + '] implausible dt → fallback ' + dt + 'ms');
        }
      
        // If no power, just advance lastTs to keep dt bounded.
        if (netPowerW <= 0) {
          writeIfExists(dev.lastTs, now, true);
          lastTsCache.set(dev.name, now);
          return;
        }
      
        const WhInc = netPowerW * (dt / 3600000);
        let todayWh = toNumberOr(getState(dev.todayWh)?.val, 0) + WhInc;
        if (todayWh < 0) todayWh = 0;
        const todayKWh = todayWh / 1000;
      
        writeIfExists(dev.lastTs, now, true);
        lastTsCache.set(dev.name, now);
        writeIfExists(dev.todayWh, todayWh, true);
        writeIfChanged(dev.todayKWh, Number(todayKWh.toFixed(ROUND_DISPLAY)), 0, true);
      
        // Update aggregated "today"
        let sumWh = 0;
        for (let i = 0; i < DEVICES.length; i++) {
          sumWh += toNumberOr(getState(DEVICES[i].todayWh)?.val, 0);
        }
        writeIfExists(OUT_TOTAL_TODAY_Wh, sumWh, true);
        writeIfChanged(OUT_TOTAL_TODAY_kWh, Number((sumWh / 1000).toFixed(ROUND_DISPLAY)), 0, true);
      }
      
      // ---------- INIT ----------
      (function init() {
        try {
          // Check existence (no creation)
          for (let i = 0; i < DEVICES.length; i++) {
            const d = DEVICES[i];
            objectExists(d.inJsonId); objectExists(d.selfId); objectExists(d.outId);
            objectExists(d.todayWh);  objectExists(d.todayKWh); objectExists(d.lastTs);
            objectExists(d.grafDaily);objectExists(d.totalKWh);
          }
          objectExists(OUT_PWR_ALL);
          objectExists(OUT_TOTAL_TODAY_Wh);
          objectExists(OUT_TOTAL_TODAY_kWh);
          objectExists(OUT_TOTAL_GRAFANA);
          objectExists(OUT_TOTAL_CUM_KWH);
      
          // Initial live power & lastTs cache
          for (let i = 0; i < DEVICES.length; i++) {
            const d = DEVICES[i];
            if (!objectExists(d.inJsonId) || !objectExists(d.outId)) continue;
            if (!objectExists(d.selfId)) log('[BKW] Note: self-consumption missing for ' + d.name + ' → 0 W', 'warn');
      
            // Inline apower parse + net calculation
            let ap = 0;
            const jState = getState(d.inJsonId);
            if (jState && typeof jState.val === 'string') {
              try {
                const j = JSON.parse(jState.val);
                ap = Number(j && j.apower) || 0;
              } catch (e) {
                const sample = typeof jState.val === 'string' ? jState.val.slice(0, 120) : '<non-string>';
                log('[BKW] JSON parse failed (' + sample + '): ' + e.message, 'warn');
              }
            }
            const self = toNumberOr(getState(d.selfId)?.val, 0);
            const net = Math.max(0, ap - self);
      
            writeIfChanged(d.outId, net, EPS_W, true);
      
            const lastStored = toNumberOr(getState(d.lastTs)?.val, NaN);
            lastTsCache.set(d.name, Number.isFinite(lastStored) ? lastStored : Date.now());
          }
          scheduleAggregateUpdate();
      
          // Event listeners
          for (let i = 0; i < DEVICES.length; i++) {
            const d = DEVICES[i];
      
            // MQTT JSON changed
            if (objectExists(d.inJsonId)) {
              on({ id: d.inJsonId, change: 'ne' }, (obj) => {
                try {
                  // Inline apower parse
                  let ap = 0;
                  if (obj && obj.state && typeof obj.state.val === 'string') {
                    try {
                      const j = JSON.parse(obj.state.val);
                      ap = Number(j && j.apower) || 0;
                    } catch (e) {
                      const sample = typeof obj.state.val === 'string' ? obj.state.val.slice(0, 120) : '<non-string>';
                      log('[BKW] JSON parse failed (' + sample + '): ' + e.message, 'warn');
                    }
                  }
                  const self = toNumberOr(getState(d.selfId)?.val, 0);
                  const net = Math.max(0, ap - self);
      
                  writeIfChanged(d.outId, net, EPS_W, true);
                  scheduleAggregateUpdate();
                  integrateDeviceEnergy(d, net);
                } catch (e) {
                  log('[BKW] onJSON ' + d.name + ' failed: ' + (e && e.message ? e.message : e), 'warn');
                }
              });
            }
      
            // Self-consumption changed
            if (objectExists(d.selfId)) {
              on({ id: d.selfId, change: 'ne' }, () => {
                try {
                  // Inline apower parse + net
                  let ap = 0;
                  const jState = getState(d.inJsonId);
                  if (jState && typeof jState.val === 'string') {
                    try {
                      const j = JSON.parse(jState.val);
                      ap = Number(j && j.apower) || 0;
                    } catch (e) {
                      const sample = typeof jState.val === 'string' ? jState.val.slice(0, 120) : '<non-string>';
                      log('[BKW] JSON parse failed (' + sample + '): ' + e.message, 'warn');
                    }
                  }
                  const self = toNumberOr(getState(d.selfId)?.val, 0);
                  const net = Math.max(0, ap - self);
      
                  writeIfChanged(d.outId, net, EPS_W, true);
                  scheduleAggregateUpdate();
                  integrateDeviceEnergy(d, net);
                } catch (e) {
                  log('[BKW] onSelf ' + d.name + ' failed: ' + (e && e.message ? e.message : e), 'warn');
                }
              });
            }
          }
      
          log('[BKW] Live power + energy script started (MQTT push, no-create).', 'info');
        } catch (e) {
          log('[BKW] init failed: ' + (e && e.message ? e.message : e), 'error');
        }
      })();
      
      // ---------- DAILY CLOSE ----------
      /**
       * Daily close: write yesterday's kWh (ts = prev day 23:59:59.999),
       * accumulate totals, reset today's counters.
       */
      function dailyClose() {
        try {
          const tsStartToday = new Date().setHours(0, 0, 0, 0);
          const tsPrevDayEnd = tsStartToday - 1;
      
          let totalDayKWh = 0;
          for (let i = 0; i < DEVICES.length; i++) {
            const d = DEVICES[i];
            const dayKWh = toNumberOr(getState(d.todayKWh)?.val, 0);
            totalDayKWh += dayKWh;
      
            writeIfExists(d.grafDaily, { val: Number(dayKWh.toFixed(ROUND_DISPLAY)), ts: tsPrevDayEnd }, true);
      
            const cum = toNumberOr(getState(d.totalKWh)?.val, 0) + dayKWh;
            writeIfExists(d.totalKWh, Number(cum.toFixed(ROUND_DISPLAY)), true);
      
            writeIfExists(d.todayWh, 0, true);
            writeIfExists(d.todayKWh, 0, true);
      
            const now = Date.now();
            writeIfExists(d.lastTs, now, true);
            lastTsCache.set(d.name, now);
          }
      
          writeIfExists(OUT_TOTAL_GRAFANA, { val: Number(totalDayKWh.toFixed(ROUND_DISPLAY)), ts: tsPrevDayEnd }, true);
      
          const totalCum = toNumberOr(getState(OUT_TOTAL_CUM_KWH)?.val, 0) + totalDayKWh;
          writeIfExists(OUT_TOTAL_CUM_KWH, Number(totalCum.toFixed(ROUND_DISPLAY)), true);
      
          writeIfExists(OUT_TOTAL_TODAY_Wh, 0, true);
          writeIfExists(OUT_TOTAL_TODAY_kWh, 0, true);
      
          log('[BKW] Daily close: total=' + totalDayKWh.toFixed(ROUND_DISPLAY) + ' kWh; cum=' + totalCum.toFixed(ROUND_DISPLAY) + ' kWh');
        } catch (e) {
          log('[BKW] daily close failed: ' + (e && e.message ? e.message : e), 'warn');
        }
      }
      
      // Cron 00:00
      schedule('0 0 * * *', dailyClose);
      
      // Optional 00:05
      if (RUN_AT_0005) {
        schedule('5 0 * * *', function () {
          try { dailyClose(); log('[BKW] 00:05 refresh executed.'); }
          catch (e) { log('[BKW] 00:05 error: ' + e, 'warn'); }
        });
      }
      
      posted in JavaScript
      H
      hotspot_2
    • RE: Skript läuft plötzlich nicht mehr

      @ticaki Danke für die Tipps. Das werde ich testen und auch hier mal berichten wie dann der Code aussieht.

      Das ChatGPT sehr gut in der Lage ist sich Dinge zu merken hab ich auch schon gemerkt. Ich habe beispielsweise mal erklärt wie ich gerne Objekte in iobroker organisiere im Bereich userdata. Also welche Besamung, wann ich ein Unterordner anlege usw. Das brauche ich nun bei weiteren Javascript Programmierprojekten nicht mehr wiederholen. Das sitzt und alle Objekte werden so wie gewünscht benannt.

      Ich werde jetzt mal eine rauere Gangart pflegen und mal schauen wie dann die Ergebnisse aussehen ;-).

      posted in JavaScript
      H
      hotspot_2
    • RE: Skript läuft plötzlich nicht mehr

      @arteck Ich verstehe Dich nicht falsch. Ich kann das komplett nachvollziehen was Du sagst.

      Ich "programmiere" gerade auf eine Art und Weise die zwar zum Ergebnis führt in einigen Fällen aber Wartbarkeit, Lesbarer Code und viele andere Aspekte die zum Programmieren gehören werden da nicht berücksichtigt. Es ist halt wirklich sehr zielorientiert, das ist aber nicht das Einzigste und das wird irgendwann zu Problem führen. Für ein paar Codeschnipsel für iobroker kann man das eventuell noch tolerieren aber wenn's größer wird wird das schwierig.

      Und nein, so hätte ich Units nicht definiert und das hat mir auch sofort klar gemacht was Du damit sagen möchtest. Danke dafür.

      Ich bleibe da am Ball, lese mich weiter ein und nutze den nicht ganz optimalen Code als Vehikel um weiter zu kommen.

      Danke für die Analyse das ich den Code zumindest einsetzen kann. Stabiler als vorher läuft er auf jeden Fall mal.

      posted in JavaScript
      H
      hotspot_2
    • RE: Skript läuft plötzlich nicht mehr

      @asgothian Ok. Ich habe jetzt nochmal von vorne gestartet und reagiere jetzt auf MQTT Push bei Änderungen von den Leistungswerten und schreibe nur das mal in Objekte. Leistung BKW1, Leistung BKW2 und Gesamtleistung.

      Jetzt schau ich mir mal an wie stabil das läuft und dann gehe ich weiter um die Tages-, Monats- und Jahresleistung zu berechnen.

      // ===============================================
      // BKW Live Power (MQTT JSON parse, no-create, sync)
      // - Liest MQTT-JSON aus status.switch:0 (Shelly Plus Plug S)
      // - Netto-Leistung je BKW = max(0, apower - wr_selfconsumption_w)
      // - Gesamtleistung = Summe der Netto-Leistungen
      // - Legt KEINE States an (schreibt nur in vorhandene Objekte)
      // - Nutzt getState/setState (ressourcenschonend)
      // - Schreibt nur bei echter Änderung (mit Toleranz) + bündelt Aggregat
      // ===============================================
      
      // ---------- KONSTANTEN: HIER ANPASSEN ----------
      const MQTT_JSON_BKW1 = 'mqtt.0.shellies.sonstiges.bkw1.status.switch:0';
      const MQTT_JSON_BKW2 = 'mqtt.0.shellies.sonstiges.bkw2.status.switch:0';
      
      // Eigenverbrauch (W) je BKW (Objekte existieren bereits; sonst wird nur gewarnt)
      const SELF_BKW1      = '0_userdata.0.pvundstrom.bkws.1.wr_selfconsumption_w';
      const SELF_BKW2      = '0_userdata.0.pvundstrom.bkws.2.wr_selfconsumption_w';
      
      // Ziel-States (müssen existieren; number, role=value.power, unit=W)
      const OUT_PWR_BKW1   = '0_userdata.0.pvundstrom.bkws.1.power_w';
      const OUT_PWR_BKW2   = '0_userdata.0.pvundstrom.bkws.2.power_w';
      const OUT_PWR_ALL    = '0_userdata.0.pvundstrom.bkws.all.power_w';
      
      // Toleranz & Aggregat-Entprellung
      const EPS_W = 1;              // nur schreiben, wenn Änderung > 1 W
      const AGG_DEBOUNCE_MS = 300;  // bündelt schnelle Mehrfachänderungen
      
      // ---------- interner Aufbau ----------
      const DEVICES = [
        { name: 'bkw1', inJsonId: MQTT_JSON_BKW1, selfId: SELF_BKW1, outId: OUT_PWR_BKW1 },
        { name: 'bkw2', inJsonId: MQTT_JSON_BKW2, selfId: SELF_BKW2, outId: OUT_PWR_BKW2 },
      ];
      
      // ---------- Helpers ----------
      const n = (v, fb=0) => Number.isFinite(Number(v)) ? Number(v) : fb;
      
      const _existCache = new Map();
      function objExists(id) {
        if (_existCache.has(id)) return _existCache.get(id);
        const ok = !!getObject(id);
        _existCache.set(id, ok);
        if (!ok) log(`[BKW] Objekt existiert nicht: ${id}`, 'warn');
        return ok;
      }
      
      // schreibt nur, wenn Ziel existiert UND sich der Wert (mit EPS) geändert hat
      function setChangedNoCreate(id, nextVal, eps = 0, ack = true) {
        if (!objExists(id)) return; // KEINE Anlage
        const cur = getState(id);
        const curVal = cur ? cur.val : undefined;
      
        let same = false;
        if (typeof nextVal === 'number') {
          same = Number.isFinite(Number(curVal)) && Math.abs(Number(curVal) - Number(nextVal)) <= eps;
        } else {
          same = curVal === nextVal;
        }
        if (same) return;
      
        setState(id, nextVal, ack);
      }
      
      // robustes JSON-Parsing für status.switch:0 → apower (W)
      function parseApowerFromState(stateObj) {
        if (!stateObj || typeof stateObj.val !== 'string') return 0;
        try {
          const json = JSON.parse(stateObj.val);
          const ap = Number(json?.apower);
          return Number.isFinite(ap) ? ap : 0;
        } catch (e) {
          const sample = typeof stateObj.val === 'string' ? stateObj.val.slice(0, 120) : '<non-string>';
          log(`[BKW] JSON parse failed (${sample}): ${e.message}`, 'warn');
          return 0;
        }
      }
      
      // Netto-Leistung: max(0, apower - self)
      function computeNetPower(dev) {
        const sJson = getState(dev.inJsonId);
        const sSelf = getState(dev.selfId);
        const ap    = parseApowerFromState(sJson);
        const self  = n(sSelf?.val, 0);
        const net   = Math.max(0, ap - self);
        return { ap, self, net };
      }
      
      // ---------- Aggregat ----------
      let aggTimer = null;
      function scheduleAgg() {
        if (aggTimer) return;
        aggTimer = setTimeout(() => {
          aggTimer = null;
          try {
            let sum = 0;
            for (const d of DEVICES) {
              if (!objExists(d.outId)) continue;
              const s = getState(d.outId);
              sum += s ? n(s.val, 0) : 0;
            }
            setChangedNoCreate(OUT_PWR_ALL, sum, EPS_W, true);
          } catch (e) {
            log(`[BKW] update aggregate failed: ${e && e.message ? e.message : e}`, 'warn');
          }
        }, AGG_DEBOUNCE_MS);
      }
      
      // ---------- Start ----------
      (() => {
        try {
          // Existenz der benutzten IDs einmalig prüfen (ohne Anlegen)
          for (const d of DEVICES) {
            objExists(d.inJsonId);
            objExists(d.selfId);
            objExists(d.outId);
          }
          objExists(OUT_PWR_ALL);
      
          // Initiale Übernahme (rein aus ioBroker-States, kein Gerät-Poll)
          for (const d of DEVICES) {
            if (!objExists(d.inJsonId) || !objExists(d.outId)) continue;
            if (!objExists(d.selfId)) log(`[BKW] Hinweis: Eigenverbrauchs-Objekt fehlt für ${d.name} → wird als 0 W behandelt`, 'warn');
      
            const { net } = computeNetPower(d);
            setChangedNoCreate(d.outId, net, EPS_W, true);
          }
          scheduleAgg();
      
          // Event-Listener: auf Änderungen am JSON ODER am Eigenverbrauch reagieren
          for (const d of DEVICES) {
            // MQTT JSON (status.switch:0)
            if (objExists(d.inJsonId)) {
              on({ id: d.inJsonId, change: 'ne' }, (obj) => {
                try {
                  const ap = parseApowerFromState(obj.state);
                  const selfS = getState(d.selfId);
                  const self = n(selfS?.val, 0);
                  const net = Math.max(0, ap - self);
                  setChangedNoCreate(d.outId, net, EPS_W, true);
                  scheduleAgg();
                } catch (e) {
                  log(`[BKW] onJSON ${d.name} failed: ${e && e.message ? e.message : e}`, 'warn');
                }
              });
            }
            // Eigenverbrauch
            if (objExists(d.selfId)) {
              on({ id: d.selfId, change: 'ne' }, () => {
                try {
                  const { net } = computeNetPower(d);
                  setChangedNoCreate(d.outId, net, EPS_W, true);
                  scheduleAgg();
                } catch (e) {
                  log(`[BKW] onSelf ${d.name} failed: ${e && e.message ? e.message : e}`, 'warn');
                }
              });
            }
          }
      
          log('[BKW] Live-Power Script gestartet (MQTT JSON, no-create, sync).', 'info');
        } catch (e) {
          log(`[BKW] init failed: ${e && e.message ? e.message : e}`, 'error');
        }
      })();
      
      posted in JavaScript
      H
      hotspot_2
    • RE: Skript läuft plötzlich nicht mehr

      So, ich habe jetzt mal etwas optimiert. Das MQTT Push Thema ist sehr interessant! Das hatte ich bisher so nicht auf dem Schirm. Das eröffnet auch Möglichkeiten mit manchen Dingen ganz anders umzugehen als bisher (Node-Red MQTT abfragen, in Objekte schreiben und dann mit den Objekten arbeiten so bin ich gerade eher unterwegs. Auch mehr mit Blockly und Node Red). Aber Javascript hat mein Interesse geweckt und da ich schon mal Turbo Pascal und Delphi programmiert habe ich etwas Grundwissen Programmierung. Will mich mit ChatGPT Unterstützung da auf jeden Fall weiter einarbeiten.

      Hier mal das optimierte Script, wer nochmal drüberschauen möchte und kann ist gerne eingeladen.

      1// ===============================================
      // BKW_Runtime (no-create version)
      // - Legt KEINE States an
      // - Schreibt NUR in bestehende States (IDs konfigurierbar)
      // - Watchdog/Heartbeat nur per Log (keine Meta-States)
      // ===============================================
      
      // --------------- CONFIG -----------------
      const CONFIG = {
        DEVICES: [
          {
            name: 'bkw1',
            // Eingangswerte (müssen existieren)
            POWER: '0_userdata.0.shellies.sonstiges.bkw1.apower',        // W (aktuelle Leistung)
            TOTAL: '0_userdata.0.shellies.sonstiges.bkw1.aenergy_total', // Wh (kumulativ)
      
            // Basis-Pfad für Ziel-/Interimswerte (müssen existieren!)
            BASE:  '0_userdata.0.pvundstrom.bkws.1',
          },
          {
            name: 'bkw2',
            POWER: '0_userdata.0.shellies.sonstiges.bkw2.apower',
            TOTAL: '0_userdata.0.shellies.sonstiges.bkw2.aenergy_total',
            BASE:  '0_userdata.0.pvundstrom.bkws.2',
          }
        ],
      
        // Aggregat-Zielpfad (muss existieren!)
        AGG_BASE: '0_userdata.0.pvundstrom.bkws.all',
      
        // Watchdog: Re-Init wenn x Minuten keine Events
        WATCHDOG_IDLE_MIN: 1,
      };
      // ------------- END CONFIG ---------------
      
      
      // ---- State-Builder pro Device (nur IDs, keine Anlage) ----
      function S(dev){
        const b = dev.BASE;
        return {
          // Eingänge:
          self_w:        `${b}.wr_selfconsumption_w`,
      
          // Outputs:
          power_w:       `${b}.power_w`,
          power_net_w:   `${b}.power_net_w`,
          producing:     `${b}.producing`,
      
          dt_wh:         `${b}.energy_today_net_wh`,
          mt_wh:         `${b}.energy_month_net_wh`,
          yt_wh:         `${b}.energy_year_net_wh`,
          dt_kwh:        `${b}.energy_today_net_kwh`,
          mt_kwh:        `${b}.energy_month_net_kwh`,
          yt_kwh:        `${b}.energy_year_net_kwh`,
          lf_wh:         `${b}.energy_lifetime_net_wh`,
          lf_kwh:        `${b}.energy_lifetime_net_kwh`,
      
          // interne Marker (müssen existieren!):
          int_last_total:`${b}.int_total_wh_last`,     // letzter TOTAL (Wh)
          int_net_eff:   `${b}.int_neteffective_wh`,   // effektiv eingespeiste Wh (nur wenn ap>self)
          int_bd:        `${b}.int_baseline_day_net_wh`,
          int_bm:        `${b}.int_baseline_month_net_wh`,
          int_by:        `${b}.int_baseline_year_net_wh`,
        };
      }
      
      // ---- Helpers ----
      const n = (v, fb=0) => Number.isFinite(Number(v)) ? Number(v) : fb;
      const toKWh = (wh) => Math.round((wh/1000)*1000)/1000;
      function val(id){ const s = getState(id); return s ? Number(s.val)||0 : 0; }
      
      // ---- Logging / Watchdog (OHNE States) ----
      let ERRCOUNT = 0;
      let lastEvent = Date.now();
      const WD_MAX_IDLE_MS = CONFIG.WATCHDOG_IDLE_MIN * 60 * 1000;
      
      function iso(ts=Date.now()){ return new Date(ts).toISOString(); }
      async function safe(label, fn){
        try { return await fn(); }
        catch(e){
          ERRCOUNT++;
          log(`[BKW] ${label} FAILED: ${e && e.message ? e.message : e}`, 'warn');
        }
      }
      async function touchEvent(){ lastEvent = Date.now(); }
      schedule('*/1 * * * *', () => log(`[BKW] heartbeat ${iso()}`, 'debug'));
      schedule('*/2 * * * *', async () => {
        const idle = Date.now() - lastEvent;
        if (idle > WD_MAX_IDLE_MS) {
          log(`[BKW] Watchdog: idle ${Math.round(idle/1000)}s → re-init listeners`, 'warn');
          for (const d of CONFIG.DEVICES) await safe(`reinit.${d.name}`, async () => initDevice(d));
          await safe('updateAggregates.watchdog', updateAggregates);
          lastEvent = Date.now();
        }
      });
      
      // ---- Kernlogik ----
      async function updateProducingFlags(dev, apNow){
        const s = S(dev);
        const ap = apNow != null ? apNow : n((await getStateAsync(dev.POWER))?.val, 0);
        const selfW = n((await getStateAsync(s.self_w))?.val, 0);
        const producing = ap > selfW;
      
        await setStateAsync(s.power_w, ap, true);
        await setStateAsync(s.power_net_w, Math.max(0, ap - selfW), true);
        await setStateAsync(s.producing, producing, true);
      }
      
      async function writeNet(dev){
        const s = S(dev);
        const netEff = n((await getStateAsync(s.int_net_eff))?.val, 0);
        const bd = n((await getStateAsync(s.int_bd))?.val, 0);
        const bm = n((await getStateAsync(s.int_bm))?.val, 0);
        const by = n((await getStateAsync(s.int_by))?.val, 0);
      
        const d = Math.max(0, netEff - bd);
        const m = Math.max(0, netEff - bm);
        const y = Math.max(0, netEff - by);
      
        await setStateAsync(s.dt_wh, d, true);
        await setStateAsync(s.mt_wh, m, true);
        await setStateAsync(s.yt_wh, y, true);
        await setStateAsync(s.dt_kwh, toKWh(d), true);
        await setStateAsync(s.mt_kwh, toKWh(m), true);
        await setStateAsync(s.yt_kwh, toKWh(y), true);
        await setStateAsync(s.lf_wh,  netEff, true);
        await setStateAsync(s.lf_kwh, toKWh(netEff), true);
      }
      
      async function initDevice(dev){
        const s = S(dev);
      
        // Initiale Flags
        await safe(`init.updateProducingFlags.${dev.name}`, async () => updateProducingFlags(dev));
      
        // Merker laden
        const totalNow = n((await getStateAsync(dev.TOTAL))?.val, 0);
        const lastInit = n((await getStateAsync(s.int_last_total))?.val, NaN);
        if (!Number.isFinite(lastInit)) {
          // NICHT anlegen – vorausgesetzt, der State existiert
          await setStateAsync(s.int_last_total, totalNow, true);
        }
      
        const netEffInit = n((await getStateAsync(s.int_net_eff))?.val, NaN);
        if (!Number.isFinite(netEffInit)) {
          await setStateAsync(s.int_net_eff, 0, true);
        }
      
        const netEff = n((await getStateAsync(s.int_net_eff))?.val, 0);
        if (!Number.isFinite(n((await getStateAsync(s.int_bd))?.val, NaN))) await setStateAsync(s.int_bd, netEff, true);
        if (!Number.isFinite(n((await getStateAsync(s.int_bm))?.val, NaN))) await setStateAsync(s.int_bm, netEff, true);
        if (!Number.isFinite(n((await getStateAsync(s.int_by))?.val, NaN))) await setStateAsync(s.int_by, netEff, true);
      
        await safe(`init.writeNet.${dev.name}`, async () => writeNet(dev));
      
        // Listener neu registrieren (auch bei Re-Init)
        on({ id: dev.POWER, change: 'ne' }, async obj => {
          await touchEvent();
          await safe(`POWER:${dev.name}`, async () => {
            await updateProducingFlags(dev, n(obj.state.val, 0));
          });
        });
      
        on({ id: dev.TOTAL, change: 'ne' }, async obj => {
          await touchEvent();
          await safe(`TOTAL:${dev.name}`, async () => {
            const total = n(obj.state.val, 0);
            const last  = n((await getStateAsync(s.int_last_total))?.val, 0);
      
            // Robust gegen Reset: wenn TOTAL kleiner als last → Basis neu setzen, KEIN Delta
            if (total < last) {
              await setStateAsync(s.int_last_total, total, true);
            } else {
              const delta = total - last;
              await setStateAsync(s.int_last_total, total, true);
      
              const ap = n((await getStateAsync(dev.POWER))?.val, 0);
              const selfW = n((await getStateAsync(s.self_w))?.val, 0);
              if (ap > selfW && delta > 0) {
                const netEffNew = n((await getStateAsync(s.int_net_eff))?.val, 0) + delta;
                await setStateAsync(s.int_net_eff, netEffNew, true);
                await writeNet(dev);
              }
      
              await updateProducingFlags(dev, ap);
              await updateAggregates();
            }
          });
        });
      
        on({ id: s.self_w, change: 'ne' }, async () => {
          await touchEvent();
          await safe(`SELF_W:${dev.name}`, async () => updateProducingFlags(dev));
        });
      }
      
      // Baselines (Mitternacht/Monat/Jahr) – schreiben nur in bestehende States
      async function baselineDay(dev){
        const s=S(dev);
        const v=n((await getStateAsync(s.int_net_eff))?.val,0);
        await setStateAsync(s.int_bd, v, true);
        await writeNet(dev);
      }
      async function baselineMonth(dev){
        const s=S(dev);
        const v=n((await getStateAsync(s.int_net_eff))?.val,0);
        await setStateAsync(s.int_bm, v, true);
        await writeNet(dev);
      }
      async function baselineYear(dev){
        const s=S(dev);
        const v=n((await getStateAsync(s.int_net_eff))?.val,0);
        await setStateAsync(s.int_by, v, true);
        await writeNet(dev);
      }
      
      // Aggregat
      async function updateAggregates(){
        const AGG = CONFIG.AGG_BASE;
        let pRaw=0, pNet=0, d=0, m=0, y=0, lf=0;
        for (const dev of CONFIG.DEVICES) {
          const s = S(dev);
          pRaw += val(s.power_w);
          pNet += val(s.power_net_w);
          d    += val(s.dt_wh);
          m    += val(s.mt_wh);
          y    += val(s.yt_wh);
          lf   += val(s.lf_wh);
        }
        await setStateAsync(`${AGG}.power_w`, pRaw, true);
        await setStateAsync(`${AGG}.power_net_w`, pNet, true);
        await setStateAsync(`${AGG}.producing`, pNet > 0, true);
        await setStateAsync(`${AGG}.energy_today_net_wh`, d, true);
        await setStateAsync(`${AGG}.energy_month_net_wh`, m, true);
        await setStateAsync(`${AGG}.energy_year_net_wh`,  y, true);
        await setStateAsync(`${AGG}.energy_today_net_kwh`, toKWh(d), true);
        await setStateAsync(`${AGG}.energy_month_net_kwh`, toKWh(m), true);
        await setStateAsync(`${AGG}.energy_year_net_kwh`,  toKWh(y), true);
        await setStateAsync(`${AGG}.energy_lifetime_net_wh`,  lf, true);
        await setStateAsync(`${AGG}.energy_lifetime_net_kwh`, toKWh(lf), true);
      }
      
      // ===== Start + Zeitpläne =====
      (async () => {
        // Init Geräte & Listener
        for (const d of CONFIG.DEVICES) await safe(`initDevice.${d.name}`, async () => initDevice(d));
      
        // Baselines
        schedule('0 0 * * *',      async () => { for (const d of CONFIG.DEVICES) await safe(`baselineDay.${d.name}`,   async () => baselineDay(d));   });
        schedule('0 0 1 * *',      async () => { for (const d of CONFIG.DEVICES) await safe(`baselineMonth.${d.name}`, async () => baselineMonth(d)); });
        schedule('0 0 1 1 *',      async () => { for (const d of CONFIG.DEVICES) await safe(`baselineYear.${d.name}`,  async () => baselineYear(d));  });
      
        // Aggregat zyklisch zusätzlich
        schedule('*/1 * * * *', async () => safe('updateAggregates.cron', updateAggregates));
      
        log('[BKW] Script started (no-create mode).', 'info');
      })();
      
      posted in JavaScript
      H
      hotspot_2
    • RE: Skript läuft plötzlich nicht mehr

      @arteck Wie meinst Du das konkret?

      posted in JavaScript
      H
      hotspot_2
    • RE: Skript läuft plötzlich nicht mehr

      @asgothian Alles klar, vielen vielen Dank für eure Hinweise! Ich finde das super wie gut das hier funktioniert das mal ganz am Rande ;-).

      Ich habe jetzt mal meine Methode etwas abgeändert. Ich erstelle gerade mit ChatGPT ein Skript was mal auf MQTT Push umstellt und testet wie oft werden die Werte denn aktualisiert. Ist das erfolgreich dann würde ich das Skript mal dahingehend umstellen nur noch auf Änderungen der Werte zu reagieren. Auch die anderen und zahlreichen Hinweise von euch werde ich mal einfließen lassen.

      Ist es mal ok das ich das mal weiter verfolge und euch dann den Entwurf mal präsentiere. In dem Zug würde ich dann auch meine Idee den Ansatz mal aufzeigen.

      posted in JavaScript
      H
      hotspot_2
    • RE: Skript läuft plötzlich nicht mehr

      Hallo zusammen,

      Das Skript ist in Zusammenarbeit mit mir und ChatGPT entstanden. Ich denke in der heutigen Zeit eine Herangehensweise die man durchaus machen kann. Da die Idee von mir ist, ich mit ChatGPT im Dialog Hinweise gegeben habe und wir dann nach einiger Zeit zu diesem aktuellen Ergebnis gekommen sind habe ich mal völlig selbstbewusst geschrieben das es ich es geschrieben habe. Wenn Dir @arteck völlig klar ist das dieses Skript gar nicht von mir sein kann dann finde ich das erstmal interessant, aber daher habe ich die Entstehungsgeschichte nun mal präzisiert und: für mich ist das völlig fein erstmal.

      @Homoran Das Skript ist ein paar Tage gelaufen. Nach meiner Wahrnehmung hat das Problem begonnen nachdem ich in iobroker für die drei Objekte den History Adapter aktiviert habe. Das Skript, scheint für meine Augen, weiter zu laufen da es im Javascript Adapter nicht gestoppt ist und es keine Log Einträge gibt. Es werden keine Daten mehr in die Objekte geschrieben und damit auch in die influxDB nach unterschiedlichen Zeitabständen nach dem Start. Ich meine das an den geändert Informationen in den drei Objekten zu erkennen und das ab dem gleichen Zeitpunkt die Einträge in der influxDB enden.

      @ticaki Vielen Dank für deinen Input ich werde das mal mit ChatGPT besprechen ;-). Ich habe bereits mal gestern Abend ChatGPT gebeten mal zu prüfen was man tun kann um zu verhindern das das Skript seine Arbeit einstellt (oder was auch immer genau im Detail) und dabei ist das hier dann rausgekommen:

      1// ===============================================
      // BKW_Runtime (no-create version)
      // - Legt KEINE States an
      // - Schreibt NUR in bestehende States (IDs konfigurierbar)
      // - Watchdog/Heartbeat nur per Log (keine Meta-States)
      // ===============================================
      
      // --------------- CONFIG -----------------
      const CONFIG = {
        DEVICES: [
          {
            name: 'bkw1',
            // Eingangswerte (müssen existieren)
            POWER: '0_userdata.0.shellies.sonstiges.bkw1.apower',        // W (aktuelle Leistung)
            TOTAL: '0_userdata.0.shellies.sonstiges.bkw1.aenergy_total', // Wh (kumulativ)
      
            // Basis-Pfad für Ziel-/Interimswerte (müssen existieren!)
            BASE:  '0_userdata.0.pvundstrom.bkws.1',
          },
          {
            name: 'bkw2',
            POWER: '0_userdata.0.shellies.sonstiges.bkw2.apower',
            TOTAL: '0_userdata.0.shellies.sonstiges.bkw2.aenergy_total',
            BASE:  '0_userdata.0.pvundstrom.bkws.2',
          }
        ],
      
        // Aggregat-Zielpfad (muss existieren!)
        AGG_BASE: '0_userdata.0.pvundstrom.bkws.all',
      
        // Watchdog: Re-Init wenn x Minuten keine Events
        WATCHDOG_IDLE_MIN: 1,
      };
      // ------------- END CONFIG ---------------
      
      
      // ---- State-Builder pro Device (nur IDs, keine Anlage) ----
      function S(dev){
        const b = dev.BASE;
        return {
          // Eingänge:
          self_w:        `${b}.wr_selfconsumption_w`,
      
          // Outputs:
          power_w:       `${b}.power_w`,
          power_net_w:   `${b}.power_net_w`,
          producing:     `${b}.producing`,
      
          dt_wh:         `${b}.energy_today_net_wh`,
          mt_wh:         `${b}.energy_month_net_wh`,
          yt_wh:         `${b}.energy_year_net_wh`,
          dt_kwh:        `${b}.energy_today_net_kwh`,
          mt_kwh:        `${b}.energy_month_net_kwh`,
          yt_kwh:        `${b}.energy_year_net_kwh`,
          lf_wh:         `${b}.energy_lifetime_net_wh`,
          lf_kwh:        `${b}.energy_lifetime_net_kwh`,
      
          // interne Marker (müssen existieren!):
          int_last_total:`${b}.int_total_wh_last`,     // letzter TOTAL (Wh)
          int_net_eff:   `${b}.int_neteffective_wh`,   // effektiv eingespeiste Wh (nur wenn ap>self)
          int_bd:        `${b}.int_baseline_day_net_wh`,
          int_bm:        `${b}.int_baseline_month_net_wh`,
          int_by:        `${b}.int_baseline_year_net_wh`,
        };
      }
      
      // ---- Helpers ----
      const n = (v, fb=0) => Number.isFinite(Number(v)) ? Number(v) : fb;
      const toKWh = (wh) => Math.round((wh/1000)*1000)/1000;
      function val(id){ const s = getState(id); return s ? Number(s.val)||0 : 0; }
      
      // ---- Logging / Watchdog (OHNE States) ----
      let ERRCOUNT = 0;
      let lastEvent = Date.now();
      const WD_MAX_IDLE_MS = CONFIG.WATCHDOG_IDLE_MIN * 60 * 1000;
      
      function iso(ts=Date.now()){ return new Date(ts).toISOString(); }
      async function safe(label, fn){
        try { return await fn(); }
        catch(e){
          ERRCOUNT++;
          log(`[BKW] ${label} FAILED: ${e && e.message ? e.message : e}`, 'warn');
        }
      }
      async function touchEvent(){ lastEvent = Date.now(); }
      schedule('*/1 * * * *', () => log(`[BKW] heartbeat ${iso()}`, 'debug'));
      schedule('*/2 * * * *', async () => {
        const idle = Date.now() - lastEvent;
        if (idle > WD_MAX_IDLE_MS) {
          log(`[BKW] Watchdog: idle ${Math.round(idle/1000)}s → re-init listeners`, 'warn');
          for (const d of CONFIG.DEVICES) await safe(`reinit.${d.name}`, async () => initDevice(d));
          await safe('updateAggregates.watchdog', updateAggregates);
          lastEvent = Date.now();
        }
      });
      
      // ---- Kernlogik ----
      async function updateProducingFlags(dev, apNow){
        const s = S(dev);
        const ap = apNow != null ? apNow : n((await getStateAsync(dev.POWER))?.val, 0);
        const selfW = n((await getStateAsync(s.self_w))?.val, 0);
        const producing = ap > selfW;
      
        await setStateAsync(s.power_w, ap, true);
        await setStateAsync(s.power_net_w, Math.max(0, ap - selfW), true);
        await setStateAsync(s.producing, producing, true);
      }
      
      async function writeNet(dev){
        const s = S(dev);
        const netEff = n((await getStateAsync(s.int_net_eff))?.val, 0);
        const bd = n((await getStateAsync(s.int_bd))?.val, 0);
        const bm = n((await getStateAsync(s.int_bm))?.val, 0);
        const by = n((await getStateAsync(s.int_by))?.val, 0);
      
        const d = Math.max(0, netEff - bd);
        const m = Math.max(0, netEff - bm);
        const y = Math.max(0, netEff - by);
      
        await setStateAsync(s.dt_wh, d, true);
        await setStateAsync(s.mt_wh, m, true);
        await setStateAsync(s.yt_wh, y, true);
        await setStateAsync(s.dt_kwh, toKWh(d), true);
        await setStateAsync(s.mt_kwh, toKWh(m), true);
        await setStateAsync(s.yt_kwh, toKWh(y), true);
        await setStateAsync(s.lf_wh,  netEff, true);
        await setStateAsync(s.lf_kwh, toKWh(netEff), true);
      }
      
      async function initDevice(dev){
        const s = S(dev);
      
        // Initiale Flags
        await safe(`init.updateProducingFlags.${dev.name}`, async () => updateProducingFlags(dev));
      
        // Merker laden
        const totalNow = n((await getStateAsync(dev.TOTAL))?.val, 0);
        const lastInit = n((await getStateAsync(s.int_last_total))?.val, NaN);
        if (!Number.isFinite(lastInit)) {
          // NICHT anlegen – vorausgesetzt, der State existiert
          await setStateAsync(s.int_last_total, totalNow, true);
        }
      
        const netEffInit = n((await getStateAsync(s.int_net_eff))?.val, NaN);
        if (!Number.isFinite(netEffInit)) {
          await setStateAsync(s.int_net_eff, 0, true);
        }
      
        const netEff = n((await getStateAsync(s.int_net_eff))?.val, 0);
        if (!Number.isFinite(n((await getStateAsync(s.int_bd))?.val, NaN))) await setStateAsync(s.int_bd, netEff, true);
        if (!Number.isFinite(n((await getStateAsync(s.int_bm))?.val, NaN))) await setStateAsync(s.int_bm, netEff, true);
        if (!Number.isFinite(n((await getStateAsync(s.int_by))?.val, NaN))) await setStateAsync(s.int_by, netEff, true);
      
        await safe(`init.writeNet.${dev.name}`, async () => writeNet(dev));
      
        // Listener neu registrieren (auch bei Re-Init)
        on({ id: dev.POWER, change: 'ne' }, async obj => {
          await touchEvent();
          await safe(`POWER:${dev.name}`, async () => {
            await updateProducingFlags(dev, n(obj.state.val, 0));
          });
        });
      
        on({ id: dev.TOTAL, change: 'ne' }, async obj => {
          await touchEvent();
          await safe(`TOTAL:${dev.name}`, async () => {
            const total = n(obj.state.val, 0);
            const last  = n((await getStateAsync(s.int_last_total))?.val, 0);
      
            // Robust gegen Reset: wenn TOTAL kleiner als last → Basis neu setzen, KEIN Delta
            if (total < last) {
              await setStateAsync(s.int_last_total, total, true);
            } else {
              const delta = total - last;
              await setStateAsync(s.int_last_total, total, true);
      
              const ap = n((await getStateAsync(dev.POWER))?.val, 0);
              const selfW = n((await getStateAsync(s.self_w))?.val, 0);
              if (ap > selfW && delta > 0) {
                const netEffNew = n((await getStateAsync(s.int_net_eff))?.val, 0) + delta;
                await setStateAsync(s.int_net_eff, netEffNew, true);
                await writeNet(dev);
              }
      
              await updateProducingFlags(dev, ap);
              await updateAggregates();
            }
          });
        });
      
        on({ id: s.self_w, change: 'ne' }, async () => {
          await touchEvent();
          await safe(`SELF_W:${dev.name}`, async () => updateProducingFlags(dev));
        });
      }
      
      // Baselines (Mitternacht/Monat/Jahr) – schreiben nur in bestehende States
      async function baselineDay(dev){
        const s=S(dev);
        const v=n((await getStateAsync(s.int_net_eff))?.val,0);
        await setStateAsync(s.int_bd, v, true);
        await writeNet(dev);
      }
      async function baselineMonth(dev){
        const s=S(dev);
        const v=n((await getStateAsync(s.int_net_eff))?.val,0);
        await setStateAsync(s.int_bm, v, true);
        await writeNet(dev);
      }
      async function baselineYear(dev){
        const s=S(dev);
        const v=n((await getStateAsync(s.int_net_eff))?.val,0);
        await setStateAsync(s.int_by, v, true);
        await writeNet(dev);
      }
      
      // Aggregat
      async function updateAggregates(){
        const AGG = CONFIG.AGG_BASE;
        let pRaw=0, pNet=0, d=0, m=0, y=0, lf=0;
        for (const dev of CONFIG.DEVICES) {
          const s = S(dev);
          pRaw += val(s.power_w);
          pNet += val(s.power_net_w);
          d    += val(s.dt_wh);
          m    += val(s.mt_wh);
          y    += val(s.yt_wh);
          lf   += val(s.lf_wh);
        }
        await setStateAsync(`${AGG}.power_w`, pRaw, true);
        await setStateAsync(`${AGG}.power_net_w`, pNet, true);
        await setStateAsync(`${AGG}.producing`, pNet > 0, true);
        await setStateAsync(`${AGG}.energy_today_net_wh`, d, true);
        await setStateAsync(`${AGG}.energy_month_net_wh`, m, true);
        await setStateAsync(`${AGG}.energy_year_net_wh`,  y, true);
        await setStateAsync(`${AGG}.energy_today_net_kwh`, toKWh(d), true);
        await setStateAsync(`${AGG}.energy_month_net_kwh`, toKWh(m), true);
        await setStateAsync(`${AGG}.energy_year_net_kwh`,  toKWh(y), true);
        await setStateAsync(`${AGG}.energy_lifetime_net_wh`,  lf, true);
        await setStateAsync(`${AGG}.energy_lifetime_net_kwh`, toKWh(lf), true);
      }
      
      // ===== Start + Zeitpläne =====
      (async () => {
        // Init Geräte & Listener
        for (const d of CONFIG.DEVICES) await safe(`initDevice.${d.name}`, async () => initDevice(d));
      
        // Baselines
        schedule('0 0 * * *',      async () => { for (const d of CONFIG.DEVICES) await safe(`baselineDay.${d.name}`,   async () => baselineDay(d));   });
        schedule('0 0 1 * *',      async () => { for (const d of CONFIG.DEVICES) await safe(`baselineMonth.${d.name}`, async () => baselineMonth(d)); });
        schedule('0 0 1 1 *',      async () => { for (const d of CONFIG.DEVICES) await safe(`baselineYear.${d.name}`,  async () => baselineYear(d));  });
      
        // Aggregat zyklisch zusätzlich
        schedule('*/1 * * * *', async () => safe('updateAggregates.cron', updateAggregates));
      
        log('[BKW] Script started (no-create mode).', 'info');
      })();
      
      posted in JavaScript
      H
      hotspot_2
    • Skript läuft plötzlich nicht mehr

      Hallo zusammen,

      ich habe ein Javascript geschrieben das mir die aktuelle Leistung von zwei BKWs von Shelly Adaptern abruft (1 x pro Sekunde) und dann die Stromerzeugung pro Tag usw. berechnet und auch noch die Summer über die BKWs errechnet.

      Das Skript hat auch ein paar Tage funktioniert aber nun hört es plötzlich damit auf. Ich habe an den power_net Werten bei den zwei BKWs und in der Summe der beiden den History Adapter aktiviert um damit dann mit Grafena das ganze darstellen zu können. Im Javascript Adapter läuft das Skript aber ganz normal und im Log sehe ich keine Hinweise. Ich sehe aber das keine Daten mehr zu einem bestimmeten Zeitpunkt in die influx DB geschrieben werden und auch die Objekte ab dem Zeitpunkt nicht mehr geändert wurden. Starte ich das Skript neu funktioniert alles normal bis es dann wieder abbricht (aber nicht wirklich sichtbar).

      Habt ihr eine Idee was ich hier tun kann. Das Skript habe ich mal eingefügt.

      // ===== BKW_Runtime – schreibt nur Werte, legt NIE States an =====
      const PATH_BKW1 = '0_userdata.0.pvundstrom.bkws.1';
      const PATH_BKW2 = '0_userdata.0.pvundstrom.bkws.2';
      const AGG_BASE  = '0_userdata.0.pvundstrom.bkws.all';
      
      const DEVICES = [
        {
          name: 'bkw1',
          POWER: '0_userdata.0.shellies.sonstiges.bkw1.apower',        // W
          TOTAL: '0_userdata.0.shellies.sonstiges.bkw1.aenergy_total', // Wh
          BASE: PATH_BKW1
        },
        {
          name: 'bkw2',
          POWER: '0_userdata.0.shellies.sonstiges.bkw2.apower',
          TOTAL: '0_userdata.0.shellies.sonstiges.bkw2.aenergy_total',
          BASE: PATH_BKW2
        }
      ];
      
      function S(dev){
        const b = dev.BASE;
        return {
          self_w:        `${b}.wr_selfconsumption_w`,
          power_w:       `${b}.power_w`,
          power_net_w:   `${b}.power_net_w`,
          producing:     `${b}.producing`,
          dt_wh:         `${b}.energy_today_net_wh`,
          mt_wh:         `${b}.energy_month_net_wh`,
          yt_wh:         `${b}.energy_year_net_wh`,
          dt_kwh:        `${b}.energy_today_net_kwh`,
          mt_kwh:        `${b}.energy_month_net_kwh`,
          yt_kwh:        `${b}.energy_year_net_kwh`,
          lf_wh:         `${b}.energy_lifetime_net_wh`,
          lf_kwh:        `${b}.energy_lifetime_net_kwh`,
          int_last_total:`${b}.int_total_wh_last`,
          int_net_eff:   `${b}.int_neteffective_wh`,
          int_bd:        `${b}.int_baseline_day_net_wh`,
          int_bm:        `${b}.int_baseline_month_net_wh`,
          int_by:        `${b}.int_baseline_year_net_wh`,
        };
      }
      const n = (v, fb=0) => Number.isFinite(Number(v)) ? Number(v) : fb;
      const toKWh = (wh) => Math.round((wh/1000)*1000)/1000;
      
      async function updateProducingFlags(dev, apNow){
        const s = S(dev);
        const ap = apNow != null ? apNow : n((await getStateAsync(dev.POWER))?.val, 0);
        const selfW = n((await getStateAsync(s.self_w))?.val, 0);
        const producing = ap > selfW;
        await setStateAsync(s.power_w, ap, true);
        await setStateAsync(s.power_net_w, Math.max(0, ap - selfW), true);
        await setStateAsync(s.producing, producing, true);
      }
      
      async function writeNet(dev){
        const s = S(dev);
        const netEff = n((await getStateAsync(s.int_net_eff))?.val, 0);
        const bd = n((await getStateAsync(s.int_bd))?.val, 0);
        const bm = n((await getStateAsync(s.int_bm))?.val, 0);
        const by = n((await getStateAsync(s.int_by))?.val, 0);
        const d = Math.max(0, netEff - bd);
        const m = Math.max(0, netEff - bm);
        const y = Math.max(0, netEff - by);
      
        await setStateAsync(s.dt_wh, d, true);
        await setStateAsync(s.mt_wh, m, true);
        await setStateAsync(s.yt_wh, y, true);
        await setStateAsync(s.dt_kwh, toKWh(d), true);
        await setStateAsync(s.mt_kwh, toKWh(m), true);
        await setStateAsync(s.yt_kwh, toKWh(y), true);
        await setStateAsync(s.lf_wh,  netEff, true);
        await setStateAsync(s.lf_kwh, toKWh(netEff), true);
      }
      
      async function initDevice(dev){
        const s = S(dev);
      
        // Initial
        await updateProducingFlags(dev);
        const totalNow = n((await getStateAsync(dev.TOTAL))?.val, 0);
        const lastInit = n((await getStateAsync(s.int_last_total))?.val, NaN);
        if (!Number.isFinite(lastInit)) await setStateAsync(s.int_last_total, totalNow, true);
      
        const netEffInit = n((await getStateAsync(s.int_net_eff))?.val, NaN);
        if (!Number.isFinite(netEffInit)) await setStateAsync(s.int_net_eff, 0, true);
      
        const netEff = n((await getStateAsync(s.int_net_eff))?.val, 0);
        if (!Number.isFinite(n((await getStateAsync(s.int_bd))?.val, NaN))) await setStateAsync(s.int_bd, netEff, true);
        if (!Number.isFinite(n((await getStateAsync(s.int_bm))?.val, NaN))) await setStateAsync(s.int_bm, netEff, true);
        if (!Number.isFinite(n((await getStateAsync(s.int_by))?.val, NaN))) await setStateAsync(s.int_by, netEff, true);
      
        await writeNet(dev);
      
        // Listener
        on({ id: dev.POWER, change: 'ne' }, async obj => {
          await updateProducingFlags(dev, n(obj.state.val, 0));
        });
      
        on({ id: dev.TOTAL, change: 'ne' }, async obj => {
          const total = n(obj.state.val, 0);
          const last  = n((await getStateAsync(s.int_last_total))?.val, 0);
          let delta = 0;
          if (total < last) delta = total; else delta = total - last;
          await setStateAsync(s.int_last_total, total, true);
      
          const ap = n((await getStateAsync(dev.POWER))?.val, 0);
          const selfW = n((await getStateAsync(s.self_w))?.val, 0);
          if (ap > selfW && delta > 0) {
            const netEffNew = n((await getStateAsync(s.int_net_eff))?.val, 0) + delta;
            await setStateAsync(s.int_net_eff, netEffNew, true);
            await writeNet(dev);
          }
          await updateProducingFlags(dev, ap);
          await updateAggregates();
        });
      
        on({ id: s.self_w, change: 'ne' }, async () => {
          await updateProducingFlags(dev);
        });
      }
      
      // Baselines (Mitternacht/Monat/Jahr)
      async function baselineDay(dev){  const s=S(dev); const v=n((await getStateAsync(s.int_net_eff))?.val,0); await setStateAsync(s.int_bd, v, true); await writeNet(dev); }
      async function baselineMonth(dev){const s=S(dev); const v=n((await getStateAsync(s.int_net_eff))?.val,0); await setStateAsync(s.int_bm, v, true); await writeNet(dev); }
      async function baselineYear(dev){ const s=S(dev); const v=n((await getStateAsync(s.int_net_eff))?.val,0); await setStateAsync(s.int_by, v, true); await writeNet(dev); }
      
      // Aggregat
      function val(id){ const s = getState(id); return s ? Number(s.val)||0 : 0; }
      async function updateAggregates(){
        let pRaw=0, pNet=0, d=0, m=0, y=0, lf=0;
        for (const dev of DEVICES) {
          const s = S(dev);
          pRaw += val(s.power_w);
          pNet += val(s.power_net_w);
          d    += val(s.dt_wh);
          m    += val(s.mt_wh);
          y    += val(s.yt_wh);
          lf   += val(s.lf_wh);
        }
        await setStateAsync(`${AGG_BASE}.power_w`, pRaw, true);
        await setStateAsync(`${AGG_BASE}.power_net_w`, pNet, true);
        await setStateAsync(`${AGG_BASE}.producing`, pNet > 0, true);
        await setStateAsync(`${AGG_BASE}.energy_today_net_wh`, d, true);
        await setStateAsync(`${AGG_BASE}.energy_month_net_wh`, m, true);
        await setStateAsync(`${AGG_BASE}.energy_year_net_wh`,  y, true);
        await setStateAsync(`${AGG_BASE}.energy_today_net_kwh`, toKWh(d), true);
        await setStateAsync(`${AGG_BASE}.energy_month_net_kwh`, toKWh(m), true);
        await setStateAsync(`${AGG_BASE}.energy_year_net_kwh`,  toKWh(y), true);
        await setStateAsync(`${AGG_BASE}.energy_lifetime_net_wh`,  lf, true);
        await setStateAsync(`${AGG_BASE}.energy_lifetime_net_kwh`, toKWh(lf), true);
      }
      
      // Start + Zeitpläne
      (async () => {
        for (const d of DEVICES) await initDevice(d);
      
        schedule('0 0 * * *',      async () => { for (const d of DEVICES) await baselineDay(d);   });
        schedule('0 0 1 * *',      async () => { for (const d of DEVICES) await baselineMonth(d); });
        schedule('0 0 1 1 *',      async () => { for (const d of DEVICES) await baselineYear(d);  });
      
        schedule('*/1 * * * *', updateAggregates);
      })();
      
      posted in JavaScript
      H
      hotspot_2
    Community
    Impressum | Datenschutz-Bestimmungen | Nutzungsbedingungen
    The ioBroker Community 2014-2023
    logo