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. Entwicklung
  4. [Neuer Adapter] Senec Home Adapter

NEWS

  • UPDATE 31.10.: Amazon Alexa - ioBroker Skill läuft aus ?
    apollon77A
    apollon77
    48
    3
    8.5k

  • Monatsrückblick – September 2025
    BluefoxB
    Bluefox
    13
    1
    2.0k

  • Neues Video "KI im Smart Home" - ioBroker plus n8n
    BluefoxB
    Bluefox
    15
    1
    2.6k

[Neuer Adapter] Senec Home Adapter

Geplant Angeheftet Gesperrt Verschoben Entwicklung
photovoltaiksenecadapter
386 Beiträge 70 Kommentatoren 92.1k Aufrufe 67 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.
  • icebearI Online
    icebearI Online
    icebear
    schrieb am zuletzt editiert von icebear
    #366

    @dispo112 said in [Neuer Adapter] Senec Home Adapter:

    So, bitte schön. Die Ordnerpfade sind alle standard. Theoretisch könnt ihr das Skript importieren und starten, fertig. Alle neuen Datenpunkte werden im Userdata erzeugt und können dann via Lovelace / VIS abgegriffen werden.

    Nochmal vielen Dank für deine Arbeit. Läuft bei mir seit gestern und die Werte sind soweit völlig O.K.

    Ich hab mich jetzt auch entschieden, nicht mehr darauf zu Warten ob irgendeiner etwas bereitstellt das die API oder die Werte über mein-senec abgegriffen werden. Die Klimzüge über HA sind mir zu aufwendig und keiner weiß wie lange das dann funktioniert bevor Senec da wieder den Riegel vorschiebt.
    Solange wie der Adapter mir die Live-Daten zur Verfügung stellt reicht mir das.

    Ich hab deshalb zusätzlich zu dem Script von @dispo112 noch drei weitere Scripte bei mir implementiert, die laufen seit gestern und liefern meines Erachtens ausreichend genaue Werte, so das sich eine gewisse Statistik aufrechterhalten lässt.

    Hier die drei Scripte die ich zusätzlich noch implementiert hab:

    1. Hausverbrauch (schreibt die Werte in zwei DP's , einmal Wh und einmal kWh und wird täglich um 00:00 Uhr auf '0' gesetzt)
    // ====== Konfiguration START ======
    
    // ID des Datenpunkts, der den aktuellen Hausverbrauch in Watt (W) liefert.
    // Diesen Wert MUSST du an deine ioBroker-Installation anpassen!
    // Beispiel: 'shelly.0.shellypower.power' oder 'modbus.0.holdingRegisters.power_total'
    const currentHouseConsumptionPowerId = "senec.0.ENERGY.GUI_HOUSE_POW";
    
    // ID des Datenpunkts für den täglichen Hausverbrauch in Wh (wird erstellt, falls nicht vorhanden)
    const dailyHouseConsumptionWhId = "0_userdata.0.Energie.Senec.Tages_Hausverbrauch_Wh";
    // ID des Datenpunkts für den täglichen Hausverbrauch in kWh (wird erstellt, falls nicht vorhanden)
    const dailyHouseConsumptionKWhId = "0_userdata.0.Energie.Senec.Tages_Hausverbrauch_kWh";
    
    // ====== Konfiguration ENDE ======
    
    
    // Globale Variablen für die Berechnung
    let lastPowerValue = 0; // Letzter bekannter Leistungswert
    let lastUpdateTime = new Date().getTime(); // Zeitpunkt der letzten Aktualisierung in Millisekunden
    
    // --- Hilfsfunktionen ---
    
    // Funktion zum Erstellen der notwendigen Datenpunkte (States)
    function createMyStates() {
        createState(dailyHouseConsumptionWhId, 0, {
            name: 'Täglicher Hausverbrauch (Wh)',
            type: 'number',
            read: true,
            write: false,
            role: 'value.power.consumption',
            unit: 'Wh',
            desc: 'Summierter Hausverbrauch für den aktuellen Tag in Wattstunden'
        });
        createState(dailyHouseConsumptionKWhId, 0, {
            name: 'Täglicher Hausverbrauch (kWh)',
            type: 'number',
            read: true,
            write: false,
            role: 'value.power.consumption',
            unit: 'kWh',
            desc: 'Summierter Hausverbrauch für den aktuellen Tag in Kilowattstunden'
        });
        log("Datenpunkte für den Hausverbrauch überprüft/erstellt.");
    }
    
    // Funktion, um den initialen Wert aus dem Datenpunkt zu lesen und zu loggen
    async function initializeConsumptionValues() {
        const state = await getStateAsync(dailyHouseConsumptionWhId);
        if (state && state.val !== null) {
            log(`Initialer Wert für täglichen Hausverbrauch Wh: ${state.val} Wh`);
        } else {
            setState(dailyHouseConsumptionWhId, 0, true);
            setState(dailyHouseConsumptionKWhId, 0, true);
            log(`Datenpunkte ${dailyHouseConsumptionWhId} und ${dailyHouseConsumptionKWhId} auf 0 gesetzt.`);
        }
    }
    
    // --- Hauptlogik ---
    
    // 1. Beim Start des Skripts: Datenpunkte erstellen und initialisieren
    createMyStates();
    initializeConsumptionValues();
    
    // 2. Trigger bei Änderung des Hausverbrauchs-Datenpunkts
    on({ id: currentHouseConsumptionPowerId, change: 'ne' }, async function (obj) {
        const currentPower = parseFloat(obj.state.val); // Aktueller Leistungswert in Watt
    
        // Prüfen, ob der Wert gültig ist
        if (isNaN(currentPower)) {
            log(`Ungültiger Leistungswert erhalten: ${obj.state.val}. Berechnung übersprungen.`, 'warn');
            return;
        }
    
        const currentTime = new Date().getTime(); // Aktueller Zeitstempel in Millisekunden
    
        // Initialisierung von lastPowerValue und lastUpdateTime beim ersten Trigger
        if (lastPowerValue === 0 && lastUpdateTime === new Date().getTime()) {
            const powerState = getState(currentHouseConsumptionPowerId);
            if (powerState && powerState.val !== null) {
                lastPowerValue = parseFloat(powerState.val);
            } else {
                lastPowerValue = 0; // Fallback
            }
            lastUpdateTime = currentTime;
            log("Initialisierung der Leistungswerte beim ersten Trigger.");
            return;
        }
    
        const timeDiffSeconds = (currentTime - lastUpdateTime) / 1000; // Zeitdifferenz in Sekunden
    
        // Falls keine Zeit vergangen ist (z.B. zu schnelle Updates oder gleicher Zeitstempel)
        if (timeDiffSeconds <= 0) {
            // log("Zeitdifferenz ist 0 oder negativ, Überspringe Berechnung.", 'debug'); // Kann viele Logs verursachen
            return;
        }
    
        // Berechnung der Energie: Leistung (W) * Zeit (s) = Wattsekunden (Ws)
        const energyWs = currentPower * timeDiffSeconds;
        // Umrechnung von Wattsekunden in Wattstunden (Ws / 3600 = Wh)
        const energyWh = energyWs / 3600;
    
        log(`Aktuelle Leistung: ${currentPower.toFixed(2)} W, Zeitdiff: ${timeDiffSeconds.toFixed(2)} s, Berechnete Energie: ${energyWh.toFixed(2)} Wh`);
    
        // Aktuellen summierten Verbrauch von ioBroker lesen und sicherstellen, dass es eine Zahl ist
        let currentDailyConsumptionWh = parseFloat((await getStateAsync(dailyHouseConsumptionWhId)).val) || 0;
    
        // Energie zum Tagesverbrauch addieren
        currentDailyConsumptionWh += energyWh;
    
        // Werte in die Datenpunkte schreiben
        setState(dailyHouseConsumptionWhId, currentDailyConsumptionWh.toFixed(2), true); // Wh auf 2 Nachkommastellen
        setState(dailyHouseConsumptionKWhId, (currentDailyConsumptionWh / 1000).toFixed(3), true); // kWh auf 3 Nachkommastellen
    
        log(`Hausverbrauch addiert. Neue Summe: ${currentDailyConsumptionWh.toFixed(2)} Wh (${(currentDailyConsumptionWh / 1000).toFixed(3)} kWh)`);
    
        // Werte für die nächste Iteration speichern
        lastPowerValue = currentPower;
        lastUpdateTime = currentTime;
    });
    
    // 3. Reset der Tagessummen um Mitternacht
    schedule('0 0 * * *', async function () {
        log("Mitternacht erreicht, setze täglichen Hausverbrauch zurück.");
        setState(dailyHouseConsumptionWhId, 0, true);
        setState(dailyHouseConsumptionKWhId, 0, true);
    
        // Wichtig: lastUpdateTime muss auch zurückgesetzt werden für korrekte delta-t Berechnung am neuen Tag
        // und lastPowerValue für den ersten Wert des neuen Tages
        lastUpdateTime = new Date().getTime();
        lastPowerValue = parseFloat((await getStateAsync(currentHouseConsumptionPowerId)).val) || 0; // Aktuellen Power-Wert erneut holen
    
        log("Täglicher Hausverbrauch wurde zurückgesetzt.");
    });
    
    // Optional: Info-Log beim Start des Skripts
    log("Skript zum Überwachen des täglichen Hausverbrauchs gestartet.");
    
    1. Akku Be-und Entladung (schreibt die Werte (batIn und batOut) in DP's , einmal Wh und einmal kWh und wird täglich um 00:00 Uhr auf '0' gesetzt)
    // ====== Konfiguration START ======
    // Datenpunkt-ID, die die aktuelle Leistung in Watt liefert.
    // Negativ für BatOut, Positiv für BatIn.
    // Diesen Wert musst du an deine ioBroker-Installation anpassen!
    const powerStateId = "senec.0.ENERGY.GUI_BAT_DATA_POWER"; // Beispiel: smartmeter.0.0_1_123_456_789.power_total_current
    
    // Datenpunkt-ID für die tägliche BatOut in Wh (wird erstellt, falls nicht vorhanden)
    const dailyBatOutWhStateId = "0_userdata.0.Energie.Senec.Bat_Out_Wh";
    // Datenpunkt-ID für die tägliche BatOut in kWh
    const dailyBatOutKWhStateId = "0_userdata.0.Energie.Senec.Bat_Out_kWh";
    
    // Datenpunkt-ID für den täglichen BatIn in Wh (wird erstellt, falls nicht vorhanden)
    const dailyBatInWhStateId = "0_userdata.0.Energie.Senec.Bat_In_Wh";
    // Datenpunkt-ID für den täglichen BatIn in kWh
    const dailyBatInKWhStateId = "0_userdata.0.Energie.Senec.Bat_In_kWh";
    
    // ====== Konfiguration ENDE ======
    
    
    // Globale Variablen für die Berechnung der Tagessummen
    let lastPowerValue = 0; // Letzter bekannter Leistungswert
    let lastUpdateTime = new Date().getTime(); // Zeitpunkt der letzten Aktualisierung in Millisekunden
    
    // Funktion zum Initialisieren der Datenpunkte (falls nicht vorhanden)
    function createStates() {
        createState(dailyBatOutWhStateId, 0, {
            name: 'Daily BatOut (Wh)',
            type: 'number',
            read: true,
            write: false,
            role: 'value.power.consumption',
            unit: 'Wh'
        });
        createState(dailyBatOutKWhStateId, 0, {
            name: 'Daily BatOut (kWh)',
            type: 'number',
            read: true,
            write: false,
            role: 'value.power.consumption',
            unit: 'kWh'
        });
        createState(dailyBatInWhStateId, 0, {
            name: 'Daily BatIn (Wh)',
            type: 'number',
            read: true,
            write: false,
            role: 'value.power.consumption',
            unit: 'Wh'
        });
        createState(dailyBatInKWhStateId, 0, {
            name: 'Daily BatIn (kWh)',
            type: 'number',
            read: true,
            write: false,
            role: 'value.power.consumption',
            unit: 'kWh'
        });
    }
    
    // Beim Start des Skripts Datenpunkte erstellen und initialisieren
    createStates();
    
    // Werte aus den Datenpunkten beim Skriptstart lesen, falls schon vorhanden
    // Prüfen, ob die Datenpunkte bereits einen Wert haben, ansonsten auf 0 setzen
    let initialBatOutState = getState(dailyBatOutWhStateId);
    if (initialBatOutState && initialBatOutState.val !== null) {
        log(`Initialer Wert für BatOut Wh: ${initialBatOutState.val}`);
    } else {
        setState(dailyBatOutWhStateId, 0);
        log(`Datenpunkt ${dailyBatOutWhStateId} auf 0 gesetzt.`);
    }
    
    let initialBatInState = getState(dailyBatInWhStateId);
    if (initialBatInState && initialBatInState.val !== null) {
        log(`Initialer Wert für BatIn Wh: ${initialBatInState.val}`);
    } else {
        setState(dailyBatInWhStateId, 0);
        log(`Datenpunkt ${dailyBatInWhStateId} auf 0 gesetzt.`);
    }
    
    // Trigger bei Änderung des Leistungsdatenpunkts
    on({ id: powerStateId, change: 'ne' }, async function (obj) {
        const currentPower = obj.state.val; // Aktueller Leistungswert in Watt
        const currentTime = new Date().getTime(); // Aktueller Zeitstempel in Millisekunden
    
        // Wenn der Wert zum ersten Mal kommt oder Zeitunterschied zu gering ist
        if (lastPowerValue === 0 && lastUpdateTime === new Date().getTime()) { // Initialisierung
            lastPowerValue = currentPower;
            lastUpdateTime = currentTime;
            log("Initialisierung der Leistungswerte.");
            return;
        }
    
        const timeDiffSeconds = (currentTime - lastUpdateTime) / 1000; // Zeitdifferenz in Sekunden
    
        if (timeDiffSeconds <= 0) { // Falls keine Zeit vergangen ist (z.B. zu schnelle Updates)
            log("Zeitdifferenz ist 0 oder negativ, Überspringe Berechnung.");
            return;
        }
    
        // Leistung * Zeit = Energie (Wattsekunden)
        const energyWs = currentPower * timeDiffSeconds;
        const energyWh = energyWs / 3600; // Umrechnung von Wattsekunden in Wattstunden
    
        log(`Aktuelle Leistung: ${currentPower} W, Zeitdiff: ${timeDiffSeconds.toFixed(2)} s, Berechnete Energie: ${energyWh.toFixed(2)} Wh`);
    
        // Sicherstellen, dass die gelesenen Werte Zahlen sind (durch parseFloat oder den Unary-Plus-Operator)
        let currentBatInWh = parseFloat((await getStateAsync(dailyBatInWhStateId)).val) || 0;
        let currentBatOutWh = parseFloat((await getStateAsync(dailyBatOutWhStateId)).val) || 0;
    
        if (currentPower < 0) { // BatOut
            currentBatOutWh += Math.abs(energyWh); // Absolutwert, damit die Summe positiv ist
            setState(dailyBatOutWhStateId, currentBatOutWh.toFixed(2), true);
            setState(dailyBatOutKWhStateId, (currentBatOutWh / 1000).toFixed(3), true);
            log(`BatOut addiert. Neue Summe: ${currentBatOutWh.toFixed(2)} Wh`);
        } else { // BatIn
            currentBatInWh += energyWh;
            setState(dailyBatInWhStateId, currentBatInWh.toFixed(2), true);
            setState(dailyBatInKWhStateId, (currentBatInWh / 1000).toFixed(3), true);
            log(`BatIn addiert. Neue Summe: ${currentBatInWh.toFixed(2)} Wh`);
        }
    
        // Werte für die nächste Iteration speichern
        lastPowerValue = currentPower;
        lastUpdateTime = currentTime;
    });
    
    
    // Reset der Tagessummen um Mitternacht
    schedule('0 0 * * *', async function () {
        log("Mitternacht erreicht, setze Tagessummen zurück.");
        setState(dailyBatOutWhStateId, 0, true);
        setState(dailyBatOutKWhStateId, 0, true);
        setState(dailyBatInWhStateId, 0, true);
        setState(dailyBatInKWhStateId, 0, true);
        // lastUpdateTime muss auch zurückgesetzt werden, um korrekte delta-t Berechnung am neuen Tag zu gewährleisten
        lastUpdateTime = new Date().getTime();
        lastPowerValue = (await getStateAsync(powerStateId)).val || 0; // Aktuellen Power-Wert erneut holen
    
        log("Tagessummen wurden zurückgesetzt.");
    });
    
    // Optional: Für Debugging, wenn das Skript startet
    log("Skript zum Überwachen von BatOut und BatIn gestartet.");
    
    1. Grid Power (schreibt die Werte in DP's (Einspeisung und Netzbezug), einmal Wh und einmal kWh und wird täglich um 00:00 Uhr auf '0' gesetzt)
    // ====== Konfiguration START ======
    // Datenpunkt-ID, die die aktuelle Leistung in Watt liefert.
    // Negativ für Einspeisung, Positiv für Netzbezug.
    // Diesen Wert musst du an deine ioBroker-Installation anpassen!
    const powerStateId = "senec.0.ENERGY.GUI_GRID_POW"; // Beispiel: smartmeter.0.0_1_123_456_789.power_total_current
    
    // Datenpunkt-ID für die tägliche Einspeisung in Wh (wird erstellt, falls nicht vorhanden)
    const dailyFeedInWhStateId = "0_userdata.0.Energie.Senec.Tages_Einspeisung_Wh";
    // Datenpunkt-ID für die tägliche Einspeisung in kWh
    const dailyFeedInKWhStateId = "0_userdata.0.Energie.Senec.Tages_Einspeisung_kWh";
    
    // Datenpunkt-ID für den täglichen Netzbezug in Wh (wird erstellt, falls nicht vorhanden)
    const dailyGridPurchaseWhStateId = "0_userdata.0.Energie.Senec.Tages_Netzbezug_Wh";
    // Datenpunkt-ID für den täglichen Netzbezug in kWh
    const dailyGridPurchaseKWhStateId = "0_userdata.0.Energie.Senec.Tages_Netzbezug_kWh";
    
    // ====== Konfiguration ENDE ======
    
    
    // Globale Variablen für die Berechnung der Tagessummen
    let lastPowerValue = 0; // Letzter bekannter Leistungswert
    let lastUpdateTime = new Date().getTime(); // Zeitpunkt der letzten Aktualisierung in Millisekunden
    
    // Funktion zum Initialisieren der Datenpunkte (falls nicht vorhanden)
    function createStates() {
        createState(dailyFeedInWhStateId, 0, {
            name: 'Tägliche Einspeisung (Wh)',
            type: 'number',
            read: true,
            write: false,
            role: 'value.power.consumption',
            unit: 'Wh'
        });
        createState(dailyFeedInKWhStateId, 0, {
            name: 'Tägliche Einspeisung (kWh)',
            type: 'number',
            read: true,
            write: false,
            role: 'value.power.consumption',
            unit: 'kWh'
        });
        createState(dailyGridPurchaseWhStateId, 0, {
            name: 'Täglicher Netzbezug (Wh)',
            type: 'number',
            read: true,
            write: false,
            role: 'value.power.consumption',
            unit: 'Wh'
        });
        createState(dailyGridPurchaseKWhStateId, 0, {
            name: 'Täglicher Netzbezug (kWh)',
            type: 'number',
            read: true,
            write: false,
            role: 'value.power.consumption',
            unit: 'kWh'
        });
    }
    
    // Beim Start des Skripts Datenpunkte erstellen und initialisieren
    createStates();
    
    // Werte aus den Datenpunkten beim Skriptstart lesen, falls schon vorhanden
    // Prüfen, ob die Datenpunkte bereits einen Wert haben, ansonsten auf 0 setzen
    let initialFeedInState = getState(dailyFeedInWhStateId);
    if (initialFeedInState && initialFeedInState.val !== null) {
        log(`Initialer Wert für Einspeisung Wh: ${initialFeedInState.val}`);
    } else {
        setState(dailyFeedInWhStateId, 0);
        log(`Datenpunkt ${dailyFeedInWhStateId} auf 0 gesetzt.`);
    }
    
    let initialGridPurchaseState = getState(dailyGridPurchaseWhStateId);
    if (initialGridPurchaseState && initialGridPurchaseState.val !== null) {
        log(`Initialer Wert für Netzbezug Wh: ${initialGridPurchaseState.val}`);
    } else {
        setState(dailyGridPurchaseWhStateId, 0);
        log(`Datenpunkt ${dailyGridPurchaseWhStateId} auf 0 gesetzt.`);
    }
    
    // Trigger bei Änderung des Leistungsdatenpunkts
    on({ id: powerStateId, change: 'ne' }, async function (obj) {
        const currentPower = obj.state.val; // Aktueller Leistungswert in Watt
        const currentTime = new Date().getTime(); // Aktueller Zeitstempel in Millisekunden
    
        // Wenn der Wert zum ersten Mal kommt oder Zeitunterschied zu gering ist
        // Hier ist es wichtig, dass lastPowerValue und lastUpdateTime initialisiert werden
        if (lastPowerValue === 0 && lastUpdateTime === new Date().getTime()) { // Initialisierung
            const powerState = getState(powerStateId);
            if (powerState && powerState.val !== null) {
                lastPowerValue = powerState.val;
            } else {
                lastPowerValue = 0; // Fallback, falls kein Wert verfügbar
            }
            lastUpdateTime = currentTime;
            log("Initialisierung der Leistungswerte beim ersten Trigger.");
            return;
        }
    
    
        const timeDiffSeconds = (currentTime - lastUpdateTime) / 1000; // Zeitdifferenz in Sekunden
    
        if (timeDiffSeconds <= 0) { // Falls keine Zeit vergangen ist (z.B. zu schnelle Updates)
            log("Zeitdifferenz ist 0 oder negativ, Überspringe Berechnung.");
            return;
        }
    
        // Leistung * Zeit = Energie (Wattsekunden)
        const energyWs = currentPower * timeDiffSeconds;
        const energyWh = energyWs / 3600; // Umrechnung von Wattsekunden in Wattstunden
    
        log(`Aktuelle Leistung: ${currentPower} W, Zeitdiff: ${timeDiffSeconds.toFixed(2)} s, Berechnete Energie: ${energyWh.toFixed(2)} Wh`);
    
        // **KORRIGIERTER BEREICH START**
        // Sicherstellen, dass die gelesenen Werte Zahlen sind (durch parseFloat oder den Unary-Plus-Operator)
        let currentFeedInWh = parseFloat((await getStateAsync(dailyFeedInWhStateId)).val) || 0;
        let currentGridPurchaseWh = parseFloat((await getStateAsync(dailyGridPurchaseWhStateId)).val) || 0;
        // **KORRIGIERTER BEREICH ENDE**
    
        if (currentPower < 0) { // Einspeisung
            currentFeedInWh += Math.abs(energyWh); // Absolutwert, damit die Summe positiv ist
            setState(dailyFeedInWhStateId, currentFeedInWh.toFixed(2), true);
            setState(dailyFeedInKWhStateId, (currentFeedInWh / 1000).toFixed(3), true);
            log(`Einspeisung addiert. Neue Summe: ${currentFeedInWh.toFixed(2)} Wh`);
        } else { // Netzbezug
            currentGridPurchaseWh += energyWh;
            setState(dailyGridPurchaseWhStateId, currentGridPurchaseWh.toFixed(2), true);
            setState(dailyGridPurchaseKWhStateId, (currentGridPurchaseWh / 1000).toFixed(3), true);
            log(`Netzbezug addiert. Neue Summe: ${currentGridPurchaseWh.toFixed(2)} Wh`);
        }
    
        // Werte für die nächste Iteration speichern
        lastPowerValue = currentPower;
        lastUpdateTime = currentTime;
    });
    
    
    // Reset der Tagessummen um Mitternacht
    schedule('0 0 * * *', async function () {
        log("Mitternacht erreicht, setze Tagessummen zurück.");
        setState(dailyFeedInWhStateId, 0, true);
        setState(dailyFeedInKWhStateId, 0, true);
        setState(dailyGridPurchaseWhStateId, 0, true);
        setState(dailyGridPurchaseKWhStateId, 0, true);
        // lastUpdateTime muss auch zurückgesetzt werden, um korrekte delta-t Berechnung am neuen Tag zu gewährleisten
        lastUpdateTime = new Date().getTime();
        lastPowerValue = (await getStateAsync(powerStateId)).val || 0; // Aktuellen Power-Wert erneut holen
    
        log("Tagessummen wurden zurückgesetzt.");
    });
    
    // Optional: Für Debugging, wenn das Skript startet
    log("Skript zum Überwachen von Einspeisung und Netzbezug gestartet.");
    

    wer Lust hat kann ja mit testen ob das so passt.

    M R 3 Antworten Letzte Antwort
    2
    • S Offline
      S Offline
      Semmy
      schrieb am zuletzt editiert von
      #367

      Hallo
      Sieht gut aus bis jetzt. Ist es möglich, für diese Datenpunkte jährliche Datenpunkte zu integrieren?

      Viele Grüße
      Semmy

      1 Antwort Letzte Antwort
      0
      • icebearI icebear

        @dispo112 said in [Neuer Adapter] Senec Home Adapter:

        So, bitte schön. Die Ordnerpfade sind alle standard. Theoretisch könnt ihr das Skript importieren und starten, fertig. Alle neuen Datenpunkte werden im Userdata erzeugt und können dann via Lovelace / VIS abgegriffen werden.

        Nochmal vielen Dank für deine Arbeit. Läuft bei mir seit gestern und die Werte sind soweit völlig O.K.

        Ich hab mich jetzt auch entschieden, nicht mehr darauf zu Warten ob irgendeiner etwas bereitstellt das die API oder die Werte über mein-senec abgegriffen werden. Die Klimzüge über HA sind mir zu aufwendig und keiner weiß wie lange das dann funktioniert bevor Senec da wieder den Riegel vorschiebt.
        Solange wie der Adapter mir die Live-Daten zur Verfügung stellt reicht mir das.

        Ich hab deshalb zusätzlich zu dem Script von @dispo112 noch drei weitere Scripte bei mir implementiert, die laufen seit gestern und liefern meines Erachtens ausreichend genaue Werte, so das sich eine gewisse Statistik aufrechterhalten lässt.

        Hier die drei Scripte die ich zusätzlich noch implementiert hab:

        1. Hausverbrauch (schreibt die Werte in zwei DP's , einmal Wh und einmal kWh und wird täglich um 00:00 Uhr auf '0' gesetzt)
        // ====== Konfiguration START ======
        
        // ID des Datenpunkts, der den aktuellen Hausverbrauch in Watt (W) liefert.
        // Diesen Wert MUSST du an deine ioBroker-Installation anpassen!
        // Beispiel: 'shelly.0.shellypower.power' oder 'modbus.0.holdingRegisters.power_total'
        const currentHouseConsumptionPowerId = "senec.0.ENERGY.GUI_HOUSE_POW";
        
        // ID des Datenpunkts für den täglichen Hausverbrauch in Wh (wird erstellt, falls nicht vorhanden)
        const dailyHouseConsumptionWhId = "0_userdata.0.Energie.Senec.Tages_Hausverbrauch_Wh";
        // ID des Datenpunkts für den täglichen Hausverbrauch in kWh (wird erstellt, falls nicht vorhanden)
        const dailyHouseConsumptionKWhId = "0_userdata.0.Energie.Senec.Tages_Hausverbrauch_kWh";
        
        // ====== Konfiguration ENDE ======
        
        
        // Globale Variablen für die Berechnung
        let lastPowerValue = 0; // Letzter bekannter Leistungswert
        let lastUpdateTime = new Date().getTime(); // Zeitpunkt der letzten Aktualisierung in Millisekunden
        
        // --- Hilfsfunktionen ---
        
        // Funktion zum Erstellen der notwendigen Datenpunkte (States)
        function createMyStates() {
            createState(dailyHouseConsumptionWhId, 0, {
                name: 'Täglicher Hausverbrauch (Wh)',
                type: 'number',
                read: true,
                write: false,
                role: 'value.power.consumption',
                unit: 'Wh',
                desc: 'Summierter Hausverbrauch für den aktuellen Tag in Wattstunden'
            });
            createState(dailyHouseConsumptionKWhId, 0, {
                name: 'Täglicher Hausverbrauch (kWh)',
                type: 'number',
                read: true,
                write: false,
                role: 'value.power.consumption',
                unit: 'kWh',
                desc: 'Summierter Hausverbrauch für den aktuellen Tag in Kilowattstunden'
            });
            log("Datenpunkte für den Hausverbrauch überprüft/erstellt.");
        }
        
        // Funktion, um den initialen Wert aus dem Datenpunkt zu lesen und zu loggen
        async function initializeConsumptionValues() {
            const state = await getStateAsync(dailyHouseConsumptionWhId);
            if (state && state.val !== null) {
                log(`Initialer Wert für täglichen Hausverbrauch Wh: ${state.val} Wh`);
            } else {
                setState(dailyHouseConsumptionWhId, 0, true);
                setState(dailyHouseConsumptionKWhId, 0, true);
                log(`Datenpunkte ${dailyHouseConsumptionWhId} und ${dailyHouseConsumptionKWhId} auf 0 gesetzt.`);
            }
        }
        
        // --- Hauptlogik ---
        
        // 1. Beim Start des Skripts: Datenpunkte erstellen und initialisieren
        createMyStates();
        initializeConsumptionValues();
        
        // 2. Trigger bei Änderung des Hausverbrauchs-Datenpunkts
        on({ id: currentHouseConsumptionPowerId, change: 'ne' }, async function (obj) {
            const currentPower = parseFloat(obj.state.val); // Aktueller Leistungswert in Watt
        
            // Prüfen, ob der Wert gültig ist
            if (isNaN(currentPower)) {
                log(`Ungültiger Leistungswert erhalten: ${obj.state.val}. Berechnung übersprungen.`, 'warn');
                return;
            }
        
            const currentTime = new Date().getTime(); // Aktueller Zeitstempel in Millisekunden
        
            // Initialisierung von lastPowerValue und lastUpdateTime beim ersten Trigger
            if (lastPowerValue === 0 && lastUpdateTime === new Date().getTime()) {
                const powerState = getState(currentHouseConsumptionPowerId);
                if (powerState && powerState.val !== null) {
                    lastPowerValue = parseFloat(powerState.val);
                } else {
                    lastPowerValue = 0; // Fallback
                }
                lastUpdateTime = currentTime;
                log("Initialisierung der Leistungswerte beim ersten Trigger.");
                return;
            }
        
            const timeDiffSeconds = (currentTime - lastUpdateTime) / 1000; // Zeitdifferenz in Sekunden
        
            // Falls keine Zeit vergangen ist (z.B. zu schnelle Updates oder gleicher Zeitstempel)
            if (timeDiffSeconds <= 0) {
                // log("Zeitdifferenz ist 0 oder negativ, Überspringe Berechnung.", 'debug'); // Kann viele Logs verursachen
                return;
            }
        
            // Berechnung der Energie: Leistung (W) * Zeit (s) = Wattsekunden (Ws)
            const energyWs = currentPower * timeDiffSeconds;
            // Umrechnung von Wattsekunden in Wattstunden (Ws / 3600 = Wh)
            const energyWh = energyWs / 3600;
        
            log(`Aktuelle Leistung: ${currentPower.toFixed(2)} W, Zeitdiff: ${timeDiffSeconds.toFixed(2)} s, Berechnete Energie: ${energyWh.toFixed(2)} Wh`);
        
            // Aktuellen summierten Verbrauch von ioBroker lesen und sicherstellen, dass es eine Zahl ist
            let currentDailyConsumptionWh = parseFloat((await getStateAsync(dailyHouseConsumptionWhId)).val) || 0;
        
            // Energie zum Tagesverbrauch addieren
            currentDailyConsumptionWh += energyWh;
        
            // Werte in die Datenpunkte schreiben
            setState(dailyHouseConsumptionWhId, currentDailyConsumptionWh.toFixed(2), true); // Wh auf 2 Nachkommastellen
            setState(dailyHouseConsumptionKWhId, (currentDailyConsumptionWh / 1000).toFixed(3), true); // kWh auf 3 Nachkommastellen
        
            log(`Hausverbrauch addiert. Neue Summe: ${currentDailyConsumptionWh.toFixed(2)} Wh (${(currentDailyConsumptionWh / 1000).toFixed(3)} kWh)`);
        
            // Werte für die nächste Iteration speichern
            lastPowerValue = currentPower;
            lastUpdateTime = currentTime;
        });
        
        // 3. Reset der Tagessummen um Mitternacht
        schedule('0 0 * * *', async function () {
            log("Mitternacht erreicht, setze täglichen Hausverbrauch zurück.");
            setState(dailyHouseConsumptionWhId, 0, true);
            setState(dailyHouseConsumptionKWhId, 0, true);
        
            // Wichtig: lastUpdateTime muss auch zurückgesetzt werden für korrekte delta-t Berechnung am neuen Tag
            // und lastPowerValue für den ersten Wert des neuen Tages
            lastUpdateTime = new Date().getTime();
            lastPowerValue = parseFloat((await getStateAsync(currentHouseConsumptionPowerId)).val) || 0; // Aktuellen Power-Wert erneut holen
        
            log("Täglicher Hausverbrauch wurde zurückgesetzt.");
        });
        
        // Optional: Info-Log beim Start des Skripts
        log("Skript zum Überwachen des täglichen Hausverbrauchs gestartet.");
        
        1. Akku Be-und Entladung (schreibt die Werte (batIn und batOut) in DP's , einmal Wh und einmal kWh und wird täglich um 00:00 Uhr auf '0' gesetzt)
        // ====== Konfiguration START ======
        // Datenpunkt-ID, die die aktuelle Leistung in Watt liefert.
        // Negativ für BatOut, Positiv für BatIn.
        // Diesen Wert musst du an deine ioBroker-Installation anpassen!
        const powerStateId = "senec.0.ENERGY.GUI_BAT_DATA_POWER"; // Beispiel: smartmeter.0.0_1_123_456_789.power_total_current
        
        // Datenpunkt-ID für die tägliche BatOut in Wh (wird erstellt, falls nicht vorhanden)
        const dailyBatOutWhStateId = "0_userdata.0.Energie.Senec.Bat_Out_Wh";
        // Datenpunkt-ID für die tägliche BatOut in kWh
        const dailyBatOutKWhStateId = "0_userdata.0.Energie.Senec.Bat_Out_kWh";
        
        // Datenpunkt-ID für den täglichen BatIn in Wh (wird erstellt, falls nicht vorhanden)
        const dailyBatInWhStateId = "0_userdata.0.Energie.Senec.Bat_In_Wh";
        // Datenpunkt-ID für den täglichen BatIn in kWh
        const dailyBatInKWhStateId = "0_userdata.0.Energie.Senec.Bat_In_kWh";
        
        // ====== Konfiguration ENDE ======
        
        
        // Globale Variablen für die Berechnung der Tagessummen
        let lastPowerValue = 0; // Letzter bekannter Leistungswert
        let lastUpdateTime = new Date().getTime(); // Zeitpunkt der letzten Aktualisierung in Millisekunden
        
        // Funktion zum Initialisieren der Datenpunkte (falls nicht vorhanden)
        function createStates() {
            createState(dailyBatOutWhStateId, 0, {
                name: 'Daily BatOut (Wh)',
                type: 'number',
                read: true,
                write: false,
                role: 'value.power.consumption',
                unit: 'Wh'
            });
            createState(dailyBatOutKWhStateId, 0, {
                name: 'Daily BatOut (kWh)',
                type: 'number',
                read: true,
                write: false,
                role: 'value.power.consumption',
                unit: 'kWh'
            });
            createState(dailyBatInWhStateId, 0, {
                name: 'Daily BatIn (Wh)',
                type: 'number',
                read: true,
                write: false,
                role: 'value.power.consumption',
                unit: 'Wh'
            });
            createState(dailyBatInKWhStateId, 0, {
                name: 'Daily BatIn (kWh)',
                type: 'number',
                read: true,
                write: false,
                role: 'value.power.consumption',
                unit: 'kWh'
            });
        }
        
        // Beim Start des Skripts Datenpunkte erstellen und initialisieren
        createStates();
        
        // Werte aus den Datenpunkten beim Skriptstart lesen, falls schon vorhanden
        // Prüfen, ob die Datenpunkte bereits einen Wert haben, ansonsten auf 0 setzen
        let initialBatOutState = getState(dailyBatOutWhStateId);
        if (initialBatOutState && initialBatOutState.val !== null) {
            log(`Initialer Wert für BatOut Wh: ${initialBatOutState.val}`);
        } else {
            setState(dailyBatOutWhStateId, 0);
            log(`Datenpunkt ${dailyBatOutWhStateId} auf 0 gesetzt.`);
        }
        
        let initialBatInState = getState(dailyBatInWhStateId);
        if (initialBatInState && initialBatInState.val !== null) {
            log(`Initialer Wert für BatIn Wh: ${initialBatInState.val}`);
        } else {
            setState(dailyBatInWhStateId, 0);
            log(`Datenpunkt ${dailyBatInWhStateId} auf 0 gesetzt.`);
        }
        
        // Trigger bei Änderung des Leistungsdatenpunkts
        on({ id: powerStateId, change: 'ne' }, async function (obj) {
            const currentPower = obj.state.val; // Aktueller Leistungswert in Watt
            const currentTime = new Date().getTime(); // Aktueller Zeitstempel in Millisekunden
        
            // Wenn der Wert zum ersten Mal kommt oder Zeitunterschied zu gering ist
            if (lastPowerValue === 0 && lastUpdateTime === new Date().getTime()) { // Initialisierung
                lastPowerValue = currentPower;
                lastUpdateTime = currentTime;
                log("Initialisierung der Leistungswerte.");
                return;
            }
        
            const timeDiffSeconds = (currentTime - lastUpdateTime) / 1000; // Zeitdifferenz in Sekunden
        
            if (timeDiffSeconds <= 0) { // Falls keine Zeit vergangen ist (z.B. zu schnelle Updates)
                log("Zeitdifferenz ist 0 oder negativ, Überspringe Berechnung.");
                return;
            }
        
            // Leistung * Zeit = Energie (Wattsekunden)
            const energyWs = currentPower * timeDiffSeconds;
            const energyWh = energyWs / 3600; // Umrechnung von Wattsekunden in Wattstunden
        
            log(`Aktuelle Leistung: ${currentPower} W, Zeitdiff: ${timeDiffSeconds.toFixed(2)} s, Berechnete Energie: ${energyWh.toFixed(2)} Wh`);
        
            // Sicherstellen, dass die gelesenen Werte Zahlen sind (durch parseFloat oder den Unary-Plus-Operator)
            let currentBatInWh = parseFloat((await getStateAsync(dailyBatInWhStateId)).val) || 0;
            let currentBatOutWh = parseFloat((await getStateAsync(dailyBatOutWhStateId)).val) || 0;
        
            if (currentPower < 0) { // BatOut
                currentBatOutWh += Math.abs(energyWh); // Absolutwert, damit die Summe positiv ist
                setState(dailyBatOutWhStateId, currentBatOutWh.toFixed(2), true);
                setState(dailyBatOutKWhStateId, (currentBatOutWh / 1000).toFixed(3), true);
                log(`BatOut addiert. Neue Summe: ${currentBatOutWh.toFixed(2)} Wh`);
            } else { // BatIn
                currentBatInWh += energyWh;
                setState(dailyBatInWhStateId, currentBatInWh.toFixed(2), true);
                setState(dailyBatInKWhStateId, (currentBatInWh / 1000).toFixed(3), true);
                log(`BatIn addiert. Neue Summe: ${currentBatInWh.toFixed(2)} Wh`);
            }
        
            // Werte für die nächste Iteration speichern
            lastPowerValue = currentPower;
            lastUpdateTime = currentTime;
        });
        
        
        // Reset der Tagessummen um Mitternacht
        schedule('0 0 * * *', async function () {
            log("Mitternacht erreicht, setze Tagessummen zurück.");
            setState(dailyBatOutWhStateId, 0, true);
            setState(dailyBatOutKWhStateId, 0, true);
            setState(dailyBatInWhStateId, 0, true);
            setState(dailyBatInKWhStateId, 0, true);
            // lastUpdateTime muss auch zurückgesetzt werden, um korrekte delta-t Berechnung am neuen Tag zu gewährleisten
            lastUpdateTime = new Date().getTime();
            lastPowerValue = (await getStateAsync(powerStateId)).val || 0; // Aktuellen Power-Wert erneut holen
        
            log("Tagessummen wurden zurückgesetzt.");
        });
        
        // Optional: Für Debugging, wenn das Skript startet
        log("Skript zum Überwachen von BatOut und BatIn gestartet.");
        
        1. Grid Power (schreibt die Werte in DP's (Einspeisung und Netzbezug), einmal Wh und einmal kWh und wird täglich um 00:00 Uhr auf '0' gesetzt)
        // ====== Konfiguration START ======
        // Datenpunkt-ID, die die aktuelle Leistung in Watt liefert.
        // Negativ für Einspeisung, Positiv für Netzbezug.
        // Diesen Wert musst du an deine ioBroker-Installation anpassen!
        const powerStateId = "senec.0.ENERGY.GUI_GRID_POW"; // Beispiel: smartmeter.0.0_1_123_456_789.power_total_current
        
        // Datenpunkt-ID für die tägliche Einspeisung in Wh (wird erstellt, falls nicht vorhanden)
        const dailyFeedInWhStateId = "0_userdata.0.Energie.Senec.Tages_Einspeisung_Wh";
        // Datenpunkt-ID für die tägliche Einspeisung in kWh
        const dailyFeedInKWhStateId = "0_userdata.0.Energie.Senec.Tages_Einspeisung_kWh";
        
        // Datenpunkt-ID für den täglichen Netzbezug in Wh (wird erstellt, falls nicht vorhanden)
        const dailyGridPurchaseWhStateId = "0_userdata.0.Energie.Senec.Tages_Netzbezug_Wh";
        // Datenpunkt-ID für den täglichen Netzbezug in kWh
        const dailyGridPurchaseKWhStateId = "0_userdata.0.Energie.Senec.Tages_Netzbezug_kWh";
        
        // ====== Konfiguration ENDE ======
        
        
        // Globale Variablen für die Berechnung der Tagessummen
        let lastPowerValue = 0; // Letzter bekannter Leistungswert
        let lastUpdateTime = new Date().getTime(); // Zeitpunkt der letzten Aktualisierung in Millisekunden
        
        // Funktion zum Initialisieren der Datenpunkte (falls nicht vorhanden)
        function createStates() {
            createState(dailyFeedInWhStateId, 0, {
                name: 'Tägliche Einspeisung (Wh)',
                type: 'number',
                read: true,
                write: false,
                role: 'value.power.consumption',
                unit: 'Wh'
            });
            createState(dailyFeedInKWhStateId, 0, {
                name: 'Tägliche Einspeisung (kWh)',
                type: 'number',
                read: true,
                write: false,
                role: 'value.power.consumption',
                unit: 'kWh'
            });
            createState(dailyGridPurchaseWhStateId, 0, {
                name: 'Täglicher Netzbezug (Wh)',
                type: 'number',
                read: true,
                write: false,
                role: 'value.power.consumption',
                unit: 'Wh'
            });
            createState(dailyGridPurchaseKWhStateId, 0, {
                name: 'Täglicher Netzbezug (kWh)',
                type: 'number',
                read: true,
                write: false,
                role: 'value.power.consumption',
                unit: 'kWh'
            });
        }
        
        // Beim Start des Skripts Datenpunkte erstellen und initialisieren
        createStates();
        
        // Werte aus den Datenpunkten beim Skriptstart lesen, falls schon vorhanden
        // Prüfen, ob die Datenpunkte bereits einen Wert haben, ansonsten auf 0 setzen
        let initialFeedInState = getState(dailyFeedInWhStateId);
        if (initialFeedInState && initialFeedInState.val !== null) {
            log(`Initialer Wert für Einspeisung Wh: ${initialFeedInState.val}`);
        } else {
            setState(dailyFeedInWhStateId, 0);
            log(`Datenpunkt ${dailyFeedInWhStateId} auf 0 gesetzt.`);
        }
        
        let initialGridPurchaseState = getState(dailyGridPurchaseWhStateId);
        if (initialGridPurchaseState && initialGridPurchaseState.val !== null) {
            log(`Initialer Wert für Netzbezug Wh: ${initialGridPurchaseState.val}`);
        } else {
            setState(dailyGridPurchaseWhStateId, 0);
            log(`Datenpunkt ${dailyGridPurchaseWhStateId} auf 0 gesetzt.`);
        }
        
        // Trigger bei Änderung des Leistungsdatenpunkts
        on({ id: powerStateId, change: 'ne' }, async function (obj) {
            const currentPower = obj.state.val; // Aktueller Leistungswert in Watt
            const currentTime = new Date().getTime(); // Aktueller Zeitstempel in Millisekunden
        
            // Wenn der Wert zum ersten Mal kommt oder Zeitunterschied zu gering ist
            // Hier ist es wichtig, dass lastPowerValue und lastUpdateTime initialisiert werden
            if (lastPowerValue === 0 && lastUpdateTime === new Date().getTime()) { // Initialisierung
                const powerState = getState(powerStateId);
                if (powerState && powerState.val !== null) {
                    lastPowerValue = powerState.val;
                } else {
                    lastPowerValue = 0; // Fallback, falls kein Wert verfügbar
                }
                lastUpdateTime = currentTime;
                log("Initialisierung der Leistungswerte beim ersten Trigger.");
                return;
            }
        
        
            const timeDiffSeconds = (currentTime - lastUpdateTime) / 1000; // Zeitdifferenz in Sekunden
        
            if (timeDiffSeconds <= 0) { // Falls keine Zeit vergangen ist (z.B. zu schnelle Updates)
                log("Zeitdifferenz ist 0 oder negativ, Überspringe Berechnung.");
                return;
            }
        
            // Leistung * Zeit = Energie (Wattsekunden)
            const energyWs = currentPower * timeDiffSeconds;
            const energyWh = energyWs / 3600; // Umrechnung von Wattsekunden in Wattstunden
        
            log(`Aktuelle Leistung: ${currentPower} W, Zeitdiff: ${timeDiffSeconds.toFixed(2)} s, Berechnete Energie: ${energyWh.toFixed(2)} Wh`);
        
            // **KORRIGIERTER BEREICH START**
            // Sicherstellen, dass die gelesenen Werte Zahlen sind (durch parseFloat oder den Unary-Plus-Operator)
            let currentFeedInWh = parseFloat((await getStateAsync(dailyFeedInWhStateId)).val) || 0;
            let currentGridPurchaseWh = parseFloat((await getStateAsync(dailyGridPurchaseWhStateId)).val) || 0;
            // **KORRIGIERTER BEREICH ENDE**
        
            if (currentPower < 0) { // Einspeisung
                currentFeedInWh += Math.abs(energyWh); // Absolutwert, damit die Summe positiv ist
                setState(dailyFeedInWhStateId, currentFeedInWh.toFixed(2), true);
                setState(dailyFeedInKWhStateId, (currentFeedInWh / 1000).toFixed(3), true);
                log(`Einspeisung addiert. Neue Summe: ${currentFeedInWh.toFixed(2)} Wh`);
            } else { // Netzbezug
                currentGridPurchaseWh += energyWh;
                setState(dailyGridPurchaseWhStateId, currentGridPurchaseWh.toFixed(2), true);
                setState(dailyGridPurchaseKWhStateId, (currentGridPurchaseWh / 1000).toFixed(3), true);
                log(`Netzbezug addiert. Neue Summe: ${currentGridPurchaseWh.toFixed(2)} Wh`);
            }
        
            // Werte für die nächste Iteration speichern
            lastPowerValue = currentPower;
            lastUpdateTime = currentTime;
        });
        
        
        // Reset der Tagessummen um Mitternacht
        schedule('0 0 * * *', async function () {
            log("Mitternacht erreicht, setze Tagessummen zurück.");
            setState(dailyFeedInWhStateId, 0, true);
            setState(dailyFeedInKWhStateId, 0, true);
            setState(dailyGridPurchaseWhStateId, 0, true);
            setState(dailyGridPurchaseKWhStateId, 0, true);
            // lastUpdateTime muss auch zurückgesetzt werden, um korrekte delta-t Berechnung am neuen Tag zu gewährleisten
            lastUpdateTime = new Date().getTime();
            lastPowerValue = (await getStateAsync(powerStateId)).val || 0; // Aktuellen Power-Wert erneut holen
        
            log("Tagessummen wurden zurückgesetzt.");
        });
        
        // Optional: Für Debugging, wenn das Skript startet
        log("Skript zum Überwachen von Einspeisung und Netzbezug gestartet.");
        

        wer Lust hat kann ja mit testen ob das so passt.

        M Offline
        M Offline
        musicnrw
        schrieb am zuletzt editiert von
        #368

        @icebear, ich bin was iobroker und Scripte angeht absoluter Anfänger. Kannst Du mir bitte möglichst detailiert beschreiben, was ich an welcher Stelle tun muss? Also, wie und wo richte ich Deine Scripte in meiner Konfiguration ein, damit ich die Werte aus der SENEC-Anlage in meine InfluxDB bekomme. Vielen Dank im Voraus!

        1 Antwort Letzte Antwort
        0
        • icebearI Online
          icebearI Online
          icebear
          schrieb am zuletzt editiert von
          #369

          @musicnrw said in [Neuer Adapter] Senec Home Adapter:

          Also, wie und wo richte ich Deine Scripte in meiner Konfiguration ein,

          Scripte von oben kopieren und dann auf 'Skripte'

          Senec_1.png

          Dann legst 1. Einen neuen Ordner (im Root-Verzeichnis an), dann 2. den Ordner anklicken (das er 'blau' markiert ist) und dann oben auf 3. das '+' für neues Script.

          Senec_2.png

          Dann JS auswählen

          Senec_3.png

          Dann einen Namen vergeben und das kopierte Script einfügen.

          Senec_4.png

          Auf den 'roten Pfeil' Script starten.

          Unter > 0_userdata.0.Energie.Senec sollten dann die DP erstellt und mit den Werten befüllt werden.

          M 1 Antwort Letzte Antwort
          2
          • icebearI icebear

            @dispo112 said in [Neuer Adapter] Senec Home Adapter:

            So, bitte schön. Die Ordnerpfade sind alle standard. Theoretisch könnt ihr das Skript importieren und starten, fertig. Alle neuen Datenpunkte werden im Userdata erzeugt und können dann via Lovelace / VIS abgegriffen werden.

            Nochmal vielen Dank für deine Arbeit. Läuft bei mir seit gestern und die Werte sind soweit völlig O.K.

            Ich hab mich jetzt auch entschieden, nicht mehr darauf zu Warten ob irgendeiner etwas bereitstellt das die API oder die Werte über mein-senec abgegriffen werden. Die Klimzüge über HA sind mir zu aufwendig und keiner weiß wie lange das dann funktioniert bevor Senec da wieder den Riegel vorschiebt.
            Solange wie der Adapter mir die Live-Daten zur Verfügung stellt reicht mir das.

            Ich hab deshalb zusätzlich zu dem Script von @dispo112 noch drei weitere Scripte bei mir implementiert, die laufen seit gestern und liefern meines Erachtens ausreichend genaue Werte, so das sich eine gewisse Statistik aufrechterhalten lässt.

            Hier die drei Scripte die ich zusätzlich noch implementiert hab:

            1. Hausverbrauch (schreibt die Werte in zwei DP's , einmal Wh und einmal kWh und wird täglich um 00:00 Uhr auf '0' gesetzt)
            // ====== Konfiguration START ======
            
            // ID des Datenpunkts, der den aktuellen Hausverbrauch in Watt (W) liefert.
            // Diesen Wert MUSST du an deine ioBroker-Installation anpassen!
            // Beispiel: 'shelly.0.shellypower.power' oder 'modbus.0.holdingRegisters.power_total'
            const currentHouseConsumptionPowerId = "senec.0.ENERGY.GUI_HOUSE_POW";
            
            // ID des Datenpunkts für den täglichen Hausverbrauch in Wh (wird erstellt, falls nicht vorhanden)
            const dailyHouseConsumptionWhId = "0_userdata.0.Energie.Senec.Tages_Hausverbrauch_Wh";
            // ID des Datenpunkts für den täglichen Hausverbrauch in kWh (wird erstellt, falls nicht vorhanden)
            const dailyHouseConsumptionKWhId = "0_userdata.0.Energie.Senec.Tages_Hausverbrauch_kWh";
            
            // ====== Konfiguration ENDE ======
            
            
            // Globale Variablen für die Berechnung
            let lastPowerValue = 0; // Letzter bekannter Leistungswert
            let lastUpdateTime = new Date().getTime(); // Zeitpunkt der letzten Aktualisierung in Millisekunden
            
            // --- Hilfsfunktionen ---
            
            // Funktion zum Erstellen der notwendigen Datenpunkte (States)
            function createMyStates() {
                createState(dailyHouseConsumptionWhId, 0, {
                    name: 'Täglicher Hausverbrauch (Wh)',
                    type: 'number',
                    read: true,
                    write: false,
                    role: 'value.power.consumption',
                    unit: 'Wh',
                    desc: 'Summierter Hausverbrauch für den aktuellen Tag in Wattstunden'
                });
                createState(dailyHouseConsumptionKWhId, 0, {
                    name: 'Täglicher Hausverbrauch (kWh)',
                    type: 'number',
                    read: true,
                    write: false,
                    role: 'value.power.consumption',
                    unit: 'kWh',
                    desc: 'Summierter Hausverbrauch für den aktuellen Tag in Kilowattstunden'
                });
                log("Datenpunkte für den Hausverbrauch überprüft/erstellt.");
            }
            
            // Funktion, um den initialen Wert aus dem Datenpunkt zu lesen und zu loggen
            async function initializeConsumptionValues() {
                const state = await getStateAsync(dailyHouseConsumptionWhId);
                if (state && state.val !== null) {
                    log(`Initialer Wert für täglichen Hausverbrauch Wh: ${state.val} Wh`);
                } else {
                    setState(dailyHouseConsumptionWhId, 0, true);
                    setState(dailyHouseConsumptionKWhId, 0, true);
                    log(`Datenpunkte ${dailyHouseConsumptionWhId} und ${dailyHouseConsumptionKWhId} auf 0 gesetzt.`);
                }
            }
            
            // --- Hauptlogik ---
            
            // 1. Beim Start des Skripts: Datenpunkte erstellen und initialisieren
            createMyStates();
            initializeConsumptionValues();
            
            // 2. Trigger bei Änderung des Hausverbrauchs-Datenpunkts
            on({ id: currentHouseConsumptionPowerId, change: 'ne' }, async function (obj) {
                const currentPower = parseFloat(obj.state.val); // Aktueller Leistungswert in Watt
            
                // Prüfen, ob der Wert gültig ist
                if (isNaN(currentPower)) {
                    log(`Ungültiger Leistungswert erhalten: ${obj.state.val}. Berechnung übersprungen.`, 'warn');
                    return;
                }
            
                const currentTime = new Date().getTime(); // Aktueller Zeitstempel in Millisekunden
            
                // Initialisierung von lastPowerValue und lastUpdateTime beim ersten Trigger
                if (lastPowerValue === 0 && lastUpdateTime === new Date().getTime()) {
                    const powerState = getState(currentHouseConsumptionPowerId);
                    if (powerState && powerState.val !== null) {
                        lastPowerValue = parseFloat(powerState.val);
                    } else {
                        lastPowerValue = 0; // Fallback
                    }
                    lastUpdateTime = currentTime;
                    log("Initialisierung der Leistungswerte beim ersten Trigger.");
                    return;
                }
            
                const timeDiffSeconds = (currentTime - lastUpdateTime) / 1000; // Zeitdifferenz in Sekunden
            
                // Falls keine Zeit vergangen ist (z.B. zu schnelle Updates oder gleicher Zeitstempel)
                if (timeDiffSeconds <= 0) {
                    // log("Zeitdifferenz ist 0 oder negativ, Überspringe Berechnung.", 'debug'); // Kann viele Logs verursachen
                    return;
                }
            
                // Berechnung der Energie: Leistung (W) * Zeit (s) = Wattsekunden (Ws)
                const energyWs = currentPower * timeDiffSeconds;
                // Umrechnung von Wattsekunden in Wattstunden (Ws / 3600 = Wh)
                const energyWh = energyWs / 3600;
            
                log(`Aktuelle Leistung: ${currentPower.toFixed(2)} W, Zeitdiff: ${timeDiffSeconds.toFixed(2)} s, Berechnete Energie: ${energyWh.toFixed(2)} Wh`);
            
                // Aktuellen summierten Verbrauch von ioBroker lesen und sicherstellen, dass es eine Zahl ist
                let currentDailyConsumptionWh = parseFloat((await getStateAsync(dailyHouseConsumptionWhId)).val) || 0;
            
                // Energie zum Tagesverbrauch addieren
                currentDailyConsumptionWh += energyWh;
            
                // Werte in die Datenpunkte schreiben
                setState(dailyHouseConsumptionWhId, currentDailyConsumptionWh.toFixed(2), true); // Wh auf 2 Nachkommastellen
                setState(dailyHouseConsumptionKWhId, (currentDailyConsumptionWh / 1000).toFixed(3), true); // kWh auf 3 Nachkommastellen
            
                log(`Hausverbrauch addiert. Neue Summe: ${currentDailyConsumptionWh.toFixed(2)} Wh (${(currentDailyConsumptionWh / 1000).toFixed(3)} kWh)`);
            
                // Werte für die nächste Iteration speichern
                lastPowerValue = currentPower;
                lastUpdateTime = currentTime;
            });
            
            // 3. Reset der Tagessummen um Mitternacht
            schedule('0 0 * * *', async function () {
                log("Mitternacht erreicht, setze täglichen Hausverbrauch zurück.");
                setState(dailyHouseConsumptionWhId, 0, true);
                setState(dailyHouseConsumptionKWhId, 0, true);
            
                // Wichtig: lastUpdateTime muss auch zurückgesetzt werden für korrekte delta-t Berechnung am neuen Tag
                // und lastPowerValue für den ersten Wert des neuen Tages
                lastUpdateTime = new Date().getTime();
                lastPowerValue = parseFloat((await getStateAsync(currentHouseConsumptionPowerId)).val) || 0; // Aktuellen Power-Wert erneut holen
            
                log("Täglicher Hausverbrauch wurde zurückgesetzt.");
            });
            
            // Optional: Info-Log beim Start des Skripts
            log("Skript zum Überwachen des täglichen Hausverbrauchs gestartet.");
            
            1. Akku Be-und Entladung (schreibt die Werte (batIn und batOut) in DP's , einmal Wh und einmal kWh und wird täglich um 00:00 Uhr auf '0' gesetzt)
            // ====== Konfiguration START ======
            // Datenpunkt-ID, die die aktuelle Leistung in Watt liefert.
            // Negativ für BatOut, Positiv für BatIn.
            // Diesen Wert musst du an deine ioBroker-Installation anpassen!
            const powerStateId = "senec.0.ENERGY.GUI_BAT_DATA_POWER"; // Beispiel: smartmeter.0.0_1_123_456_789.power_total_current
            
            // Datenpunkt-ID für die tägliche BatOut in Wh (wird erstellt, falls nicht vorhanden)
            const dailyBatOutWhStateId = "0_userdata.0.Energie.Senec.Bat_Out_Wh";
            // Datenpunkt-ID für die tägliche BatOut in kWh
            const dailyBatOutKWhStateId = "0_userdata.0.Energie.Senec.Bat_Out_kWh";
            
            // Datenpunkt-ID für den täglichen BatIn in Wh (wird erstellt, falls nicht vorhanden)
            const dailyBatInWhStateId = "0_userdata.0.Energie.Senec.Bat_In_Wh";
            // Datenpunkt-ID für den täglichen BatIn in kWh
            const dailyBatInKWhStateId = "0_userdata.0.Energie.Senec.Bat_In_kWh";
            
            // ====== Konfiguration ENDE ======
            
            
            // Globale Variablen für die Berechnung der Tagessummen
            let lastPowerValue = 0; // Letzter bekannter Leistungswert
            let lastUpdateTime = new Date().getTime(); // Zeitpunkt der letzten Aktualisierung in Millisekunden
            
            // Funktion zum Initialisieren der Datenpunkte (falls nicht vorhanden)
            function createStates() {
                createState(dailyBatOutWhStateId, 0, {
                    name: 'Daily BatOut (Wh)',
                    type: 'number',
                    read: true,
                    write: false,
                    role: 'value.power.consumption',
                    unit: 'Wh'
                });
                createState(dailyBatOutKWhStateId, 0, {
                    name: 'Daily BatOut (kWh)',
                    type: 'number',
                    read: true,
                    write: false,
                    role: 'value.power.consumption',
                    unit: 'kWh'
                });
                createState(dailyBatInWhStateId, 0, {
                    name: 'Daily BatIn (Wh)',
                    type: 'number',
                    read: true,
                    write: false,
                    role: 'value.power.consumption',
                    unit: 'Wh'
                });
                createState(dailyBatInKWhStateId, 0, {
                    name: 'Daily BatIn (kWh)',
                    type: 'number',
                    read: true,
                    write: false,
                    role: 'value.power.consumption',
                    unit: 'kWh'
                });
            }
            
            // Beim Start des Skripts Datenpunkte erstellen und initialisieren
            createStates();
            
            // Werte aus den Datenpunkten beim Skriptstart lesen, falls schon vorhanden
            // Prüfen, ob die Datenpunkte bereits einen Wert haben, ansonsten auf 0 setzen
            let initialBatOutState = getState(dailyBatOutWhStateId);
            if (initialBatOutState && initialBatOutState.val !== null) {
                log(`Initialer Wert für BatOut Wh: ${initialBatOutState.val}`);
            } else {
                setState(dailyBatOutWhStateId, 0);
                log(`Datenpunkt ${dailyBatOutWhStateId} auf 0 gesetzt.`);
            }
            
            let initialBatInState = getState(dailyBatInWhStateId);
            if (initialBatInState && initialBatInState.val !== null) {
                log(`Initialer Wert für BatIn Wh: ${initialBatInState.val}`);
            } else {
                setState(dailyBatInWhStateId, 0);
                log(`Datenpunkt ${dailyBatInWhStateId} auf 0 gesetzt.`);
            }
            
            // Trigger bei Änderung des Leistungsdatenpunkts
            on({ id: powerStateId, change: 'ne' }, async function (obj) {
                const currentPower = obj.state.val; // Aktueller Leistungswert in Watt
                const currentTime = new Date().getTime(); // Aktueller Zeitstempel in Millisekunden
            
                // Wenn der Wert zum ersten Mal kommt oder Zeitunterschied zu gering ist
                if (lastPowerValue === 0 && lastUpdateTime === new Date().getTime()) { // Initialisierung
                    lastPowerValue = currentPower;
                    lastUpdateTime = currentTime;
                    log("Initialisierung der Leistungswerte.");
                    return;
                }
            
                const timeDiffSeconds = (currentTime - lastUpdateTime) / 1000; // Zeitdifferenz in Sekunden
            
                if (timeDiffSeconds <= 0) { // Falls keine Zeit vergangen ist (z.B. zu schnelle Updates)
                    log("Zeitdifferenz ist 0 oder negativ, Überspringe Berechnung.");
                    return;
                }
            
                // Leistung * Zeit = Energie (Wattsekunden)
                const energyWs = currentPower * timeDiffSeconds;
                const energyWh = energyWs / 3600; // Umrechnung von Wattsekunden in Wattstunden
            
                log(`Aktuelle Leistung: ${currentPower} W, Zeitdiff: ${timeDiffSeconds.toFixed(2)} s, Berechnete Energie: ${energyWh.toFixed(2)} Wh`);
            
                // Sicherstellen, dass die gelesenen Werte Zahlen sind (durch parseFloat oder den Unary-Plus-Operator)
                let currentBatInWh = parseFloat((await getStateAsync(dailyBatInWhStateId)).val) || 0;
                let currentBatOutWh = parseFloat((await getStateAsync(dailyBatOutWhStateId)).val) || 0;
            
                if (currentPower < 0) { // BatOut
                    currentBatOutWh += Math.abs(energyWh); // Absolutwert, damit die Summe positiv ist
                    setState(dailyBatOutWhStateId, currentBatOutWh.toFixed(2), true);
                    setState(dailyBatOutKWhStateId, (currentBatOutWh / 1000).toFixed(3), true);
                    log(`BatOut addiert. Neue Summe: ${currentBatOutWh.toFixed(2)} Wh`);
                } else { // BatIn
                    currentBatInWh += energyWh;
                    setState(dailyBatInWhStateId, currentBatInWh.toFixed(2), true);
                    setState(dailyBatInKWhStateId, (currentBatInWh / 1000).toFixed(3), true);
                    log(`BatIn addiert. Neue Summe: ${currentBatInWh.toFixed(2)} Wh`);
                }
            
                // Werte für die nächste Iteration speichern
                lastPowerValue = currentPower;
                lastUpdateTime = currentTime;
            });
            
            
            // Reset der Tagessummen um Mitternacht
            schedule('0 0 * * *', async function () {
                log("Mitternacht erreicht, setze Tagessummen zurück.");
                setState(dailyBatOutWhStateId, 0, true);
                setState(dailyBatOutKWhStateId, 0, true);
                setState(dailyBatInWhStateId, 0, true);
                setState(dailyBatInKWhStateId, 0, true);
                // lastUpdateTime muss auch zurückgesetzt werden, um korrekte delta-t Berechnung am neuen Tag zu gewährleisten
                lastUpdateTime = new Date().getTime();
                lastPowerValue = (await getStateAsync(powerStateId)).val || 0; // Aktuellen Power-Wert erneut holen
            
                log("Tagessummen wurden zurückgesetzt.");
            });
            
            // Optional: Für Debugging, wenn das Skript startet
            log("Skript zum Überwachen von BatOut und BatIn gestartet.");
            
            1. Grid Power (schreibt die Werte in DP's (Einspeisung und Netzbezug), einmal Wh und einmal kWh und wird täglich um 00:00 Uhr auf '0' gesetzt)
            // ====== Konfiguration START ======
            // Datenpunkt-ID, die die aktuelle Leistung in Watt liefert.
            // Negativ für Einspeisung, Positiv für Netzbezug.
            // Diesen Wert musst du an deine ioBroker-Installation anpassen!
            const powerStateId = "senec.0.ENERGY.GUI_GRID_POW"; // Beispiel: smartmeter.0.0_1_123_456_789.power_total_current
            
            // Datenpunkt-ID für die tägliche Einspeisung in Wh (wird erstellt, falls nicht vorhanden)
            const dailyFeedInWhStateId = "0_userdata.0.Energie.Senec.Tages_Einspeisung_Wh";
            // Datenpunkt-ID für die tägliche Einspeisung in kWh
            const dailyFeedInKWhStateId = "0_userdata.0.Energie.Senec.Tages_Einspeisung_kWh";
            
            // Datenpunkt-ID für den täglichen Netzbezug in Wh (wird erstellt, falls nicht vorhanden)
            const dailyGridPurchaseWhStateId = "0_userdata.0.Energie.Senec.Tages_Netzbezug_Wh";
            // Datenpunkt-ID für den täglichen Netzbezug in kWh
            const dailyGridPurchaseKWhStateId = "0_userdata.0.Energie.Senec.Tages_Netzbezug_kWh";
            
            // ====== Konfiguration ENDE ======
            
            
            // Globale Variablen für die Berechnung der Tagessummen
            let lastPowerValue = 0; // Letzter bekannter Leistungswert
            let lastUpdateTime = new Date().getTime(); // Zeitpunkt der letzten Aktualisierung in Millisekunden
            
            // Funktion zum Initialisieren der Datenpunkte (falls nicht vorhanden)
            function createStates() {
                createState(dailyFeedInWhStateId, 0, {
                    name: 'Tägliche Einspeisung (Wh)',
                    type: 'number',
                    read: true,
                    write: false,
                    role: 'value.power.consumption',
                    unit: 'Wh'
                });
                createState(dailyFeedInKWhStateId, 0, {
                    name: 'Tägliche Einspeisung (kWh)',
                    type: 'number',
                    read: true,
                    write: false,
                    role: 'value.power.consumption',
                    unit: 'kWh'
                });
                createState(dailyGridPurchaseWhStateId, 0, {
                    name: 'Täglicher Netzbezug (Wh)',
                    type: 'number',
                    read: true,
                    write: false,
                    role: 'value.power.consumption',
                    unit: 'Wh'
                });
                createState(dailyGridPurchaseKWhStateId, 0, {
                    name: 'Täglicher Netzbezug (kWh)',
                    type: 'number',
                    read: true,
                    write: false,
                    role: 'value.power.consumption',
                    unit: 'kWh'
                });
            }
            
            // Beim Start des Skripts Datenpunkte erstellen und initialisieren
            createStates();
            
            // Werte aus den Datenpunkten beim Skriptstart lesen, falls schon vorhanden
            // Prüfen, ob die Datenpunkte bereits einen Wert haben, ansonsten auf 0 setzen
            let initialFeedInState = getState(dailyFeedInWhStateId);
            if (initialFeedInState && initialFeedInState.val !== null) {
                log(`Initialer Wert für Einspeisung Wh: ${initialFeedInState.val}`);
            } else {
                setState(dailyFeedInWhStateId, 0);
                log(`Datenpunkt ${dailyFeedInWhStateId} auf 0 gesetzt.`);
            }
            
            let initialGridPurchaseState = getState(dailyGridPurchaseWhStateId);
            if (initialGridPurchaseState && initialGridPurchaseState.val !== null) {
                log(`Initialer Wert für Netzbezug Wh: ${initialGridPurchaseState.val}`);
            } else {
                setState(dailyGridPurchaseWhStateId, 0);
                log(`Datenpunkt ${dailyGridPurchaseWhStateId} auf 0 gesetzt.`);
            }
            
            // Trigger bei Änderung des Leistungsdatenpunkts
            on({ id: powerStateId, change: 'ne' }, async function (obj) {
                const currentPower = obj.state.val; // Aktueller Leistungswert in Watt
                const currentTime = new Date().getTime(); // Aktueller Zeitstempel in Millisekunden
            
                // Wenn der Wert zum ersten Mal kommt oder Zeitunterschied zu gering ist
                // Hier ist es wichtig, dass lastPowerValue und lastUpdateTime initialisiert werden
                if (lastPowerValue === 0 && lastUpdateTime === new Date().getTime()) { // Initialisierung
                    const powerState = getState(powerStateId);
                    if (powerState && powerState.val !== null) {
                        lastPowerValue = powerState.val;
                    } else {
                        lastPowerValue = 0; // Fallback, falls kein Wert verfügbar
                    }
                    lastUpdateTime = currentTime;
                    log("Initialisierung der Leistungswerte beim ersten Trigger.");
                    return;
                }
            
            
                const timeDiffSeconds = (currentTime - lastUpdateTime) / 1000; // Zeitdifferenz in Sekunden
            
                if (timeDiffSeconds <= 0) { // Falls keine Zeit vergangen ist (z.B. zu schnelle Updates)
                    log("Zeitdifferenz ist 0 oder negativ, Überspringe Berechnung.");
                    return;
                }
            
                // Leistung * Zeit = Energie (Wattsekunden)
                const energyWs = currentPower * timeDiffSeconds;
                const energyWh = energyWs / 3600; // Umrechnung von Wattsekunden in Wattstunden
            
                log(`Aktuelle Leistung: ${currentPower} W, Zeitdiff: ${timeDiffSeconds.toFixed(2)} s, Berechnete Energie: ${energyWh.toFixed(2)} Wh`);
            
                // **KORRIGIERTER BEREICH START**
                // Sicherstellen, dass die gelesenen Werte Zahlen sind (durch parseFloat oder den Unary-Plus-Operator)
                let currentFeedInWh = parseFloat((await getStateAsync(dailyFeedInWhStateId)).val) || 0;
                let currentGridPurchaseWh = parseFloat((await getStateAsync(dailyGridPurchaseWhStateId)).val) || 0;
                // **KORRIGIERTER BEREICH ENDE**
            
                if (currentPower < 0) { // Einspeisung
                    currentFeedInWh += Math.abs(energyWh); // Absolutwert, damit die Summe positiv ist
                    setState(dailyFeedInWhStateId, currentFeedInWh.toFixed(2), true);
                    setState(dailyFeedInKWhStateId, (currentFeedInWh / 1000).toFixed(3), true);
                    log(`Einspeisung addiert. Neue Summe: ${currentFeedInWh.toFixed(2)} Wh`);
                } else { // Netzbezug
                    currentGridPurchaseWh += energyWh;
                    setState(dailyGridPurchaseWhStateId, currentGridPurchaseWh.toFixed(2), true);
                    setState(dailyGridPurchaseKWhStateId, (currentGridPurchaseWh / 1000).toFixed(3), true);
                    log(`Netzbezug addiert. Neue Summe: ${currentGridPurchaseWh.toFixed(2)} Wh`);
                }
            
                // Werte für die nächste Iteration speichern
                lastPowerValue = currentPower;
                lastUpdateTime = currentTime;
            });
            
            
            // Reset der Tagessummen um Mitternacht
            schedule('0 0 * * *', async function () {
                log("Mitternacht erreicht, setze Tagessummen zurück.");
                setState(dailyFeedInWhStateId, 0, true);
                setState(dailyFeedInKWhStateId, 0, true);
                setState(dailyGridPurchaseWhStateId, 0, true);
                setState(dailyGridPurchaseKWhStateId, 0, true);
                // lastUpdateTime muss auch zurückgesetzt werden, um korrekte delta-t Berechnung am neuen Tag zu gewährleisten
                lastUpdateTime = new Date().getTime();
                lastPowerValue = (await getStateAsync(powerStateId)).val || 0; // Aktuellen Power-Wert erneut holen
            
                log("Tagessummen wurden zurückgesetzt.");
            });
            
            // Optional: Für Debugging, wenn das Skript startet
            log("Skript zum Überwachen von Einspeisung und Netzbezug gestartet.");
            

            wer Lust hat kann ja mit testen ob das so passt.

            R Offline
            R Offline
            RicoStriese
            schrieb am zuletzt editiert von
            #370

            @icebear Danke für die Arbeit, habe alles übernommen. Es funktioniert super. Nochmals besten Danke für die Mühe.:clap: :clap:

            1 Antwort Letzte Antwort
            0
            • icebearI icebear

              @dispo112 said in [Neuer Adapter] Senec Home Adapter:

              So, bitte schön. Die Ordnerpfade sind alle standard. Theoretisch könnt ihr das Skript importieren und starten, fertig. Alle neuen Datenpunkte werden im Userdata erzeugt und können dann via Lovelace / VIS abgegriffen werden.

              Nochmal vielen Dank für deine Arbeit. Läuft bei mir seit gestern und die Werte sind soweit völlig O.K.

              Ich hab mich jetzt auch entschieden, nicht mehr darauf zu Warten ob irgendeiner etwas bereitstellt das die API oder die Werte über mein-senec abgegriffen werden. Die Klimzüge über HA sind mir zu aufwendig und keiner weiß wie lange das dann funktioniert bevor Senec da wieder den Riegel vorschiebt.
              Solange wie der Adapter mir die Live-Daten zur Verfügung stellt reicht mir das.

              Ich hab deshalb zusätzlich zu dem Script von @dispo112 noch drei weitere Scripte bei mir implementiert, die laufen seit gestern und liefern meines Erachtens ausreichend genaue Werte, so das sich eine gewisse Statistik aufrechterhalten lässt.

              Hier die drei Scripte die ich zusätzlich noch implementiert hab:

              1. Hausverbrauch (schreibt die Werte in zwei DP's , einmal Wh und einmal kWh und wird täglich um 00:00 Uhr auf '0' gesetzt)
              // ====== Konfiguration START ======
              
              // ID des Datenpunkts, der den aktuellen Hausverbrauch in Watt (W) liefert.
              // Diesen Wert MUSST du an deine ioBroker-Installation anpassen!
              // Beispiel: 'shelly.0.shellypower.power' oder 'modbus.0.holdingRegisters.power_total'
              const currentHouseConsumptionPowerId = "senec.0.ENERGY.GUI_HOUSE_POW";
              
              // ID des Datenpunkts für den täglichen Hausverbrauch in Wh (wird erstellt, falls nicht vorhanden)
              const dailyHouseConsumptionWhId = "0_userdata.0.Energie.Senec.Tages_Hausverbrauch_Wh";
              // ID des Datenpunkts für den täglichen Hausverbrauch in kWh (wird erstellt, falls nicht vorhanden)
              const dailyHouseConsumptionKWhId = "0_userdata.0.Energie.Senec.Tages_Hausverbrauch_kWh";
              
              // ====== Konfiguration ENDE ======
              
              
              // Globale Variablen für die Berechnung
              let lastPowerValue = 0; // Letzter bekannter Leistungswert
              let lastUpdateTime = new Date().getTime(); // Zeitpunkt der letzten Aktualisierung in Millisekunden
              
              // --- Hilfsfunktionen ---
              
              // Funktion zum Erstellen der notwendigen Datenpunkte (States)
              function createMyStates() {
                  createState(dailyHouseConsumptionWhId, 0, {
                      name: 'Täglicher Hausverbrauch (Wh)',
                      type: 'number',
                      read: true,
                      write: false,
                      role: 'value.power.consumption',
                      unit: 'Wh',
                      desc: 'Summierter Hausverbrauch für den aktuellen Tag in Wattstunden'
                  });
                  createState(dailyHouseConsumptionKWhId, 0, {
                      name: 'Täglicher Hausverbrauch (kWh)',
                      type: 'number',
                      read: true,
                      write: false,
                      role: 'value.power.consumption',
                      unit: 'kWh',
                      desc: 'Summierter Hausverbrauch für den aktuellen Tag in Kilowattstunden'
                  });
                  log("Datenpunkte für den Hausverbrauch überprüft/erstellt.");
              }
              
              // Funktion, um den initialen Wert aus dem Datenpunkt zu lesen und zu loggen
              async function initializeConsumptionValues() {
                  const state = await getStateAsync(dailyHouseConsumptionWhId);
                  if (state && state.val !== null) {
                      log(`Initialer Wert für täglichen Hausverbrauch Wh: ${state.val} Wh`);
                  } else {
                      setState(dailyHouseConsumptionWhId, 0, true);
                      setState(dailyHouseConsumptionKWhId, 0, true);
                      log(`Datenpunkte ${dailyHouseConsumptionWhId} und ${dailyHouseConsumptionKWhId} auf 0 gesetzt.`);
                  }
              }
              
              // --- Hauptlogik ---
              
              // 1. Beim Start des Skripts: Datenpunkte erstellen und initialisieren
              createMyStates();
              initializeConsumptionValues();
              
              // 2. Trigger bei Änderung des Hausverbrauchs-Datenpunkts
              on({ id: currentHouseConsumptionPowerId, change: 'ne' }, async function (obj) {
                  const currentPower = parseFloat(obj.state.val); // Aktueller Leistungswert in Watt
              
                  // Prüfen, ob der Wert gültig ist
                  if (isNaN(currentPower)) {
                      log(`Ungültiger Leistungswert erhalten: ${obj.state.val}. Berechnung übersprungen.`, 'warn');
                      return;
                  }
              
                  const currentTime = new Date().getTime(); // Aktueller Zeitstempel in Millisekunden
              
                  // Initialisierung von lastPowerValue und lastUpdateTime beim ersten Trigger
                  if (lastPowerValue === 0 && lastUpdateTime === new Date().getTime()) {
                      const powerState = getState(currentHouseConsumptionPowerId);
                      if (powerState && powerState.val !== null) {
                          lastPowerValue = parseFloat(powerState.val);
                      } else {
                          lastPowerValue = 0; // Fallback
                      }
                      lastUpdateTime = currentTime;
                      log("Initialisierung der Leistungswerte beim ersten Trigger.");
                      return;
                  }
              
                  const timeDiffSeconds = (currentTime - lastUpdateTime) / 1000; // Zeitdifferenz in Sekunden
              
                  // Falls keine Zeit vergangen ist (z.B. zu schnelle Updates oder gleicher Zeitstempel)
                  if (timeDiffSeconds <= 0) {
                      // log("Zeitdifferenz ist 0 oder negativ, Überspringe Berechnung.", 'debug'); // Kann viele Logs verursachen
                      return;
                  }
              
                  // Berechnung der Energie: Leistung (W) * Zeit (s) = Wattsekunden (Ws)
                  const energyWs = currentPower * timeDiffSeconds;
                  // Umrechnung von Wattsekunden in Wattstunden (Ws / 3600 = Wh)
                  const energyWh = energyWs / 3600;
              
                  log(`Aktuelle Leistung: ${currentPower.toFixed(2)} W, Zeitdiff: ${timeDiffSeconds.toFixed(2)} s, Berechnete Energie: ${energyWh.toFixed(2)} Wh`);
              
                  // Aktuellen summierten Verbrauch von ioBroker lesen und sicherstellen, dass es eine Zahl ist
                  let currentDailyConsumptionWh = parseFloat((await getStateAsync(dailyHouseConsumptionWhId)).val) || 0;
              
                  // Energie zum Tagesverbrauch addieren
                  currentDailyConsumptionWh += energyWh;
              
                  // Werte in die Datenpunkte schreiben
                  setState(dailyHouseConsumptionWhId, currentDailyConsumptionWh.toFixed(2), true); // Wh auf 2 Nachkommastellen
                  setState(dailyHouseConsumptionKWhId, (currentDailyConsumptionWh / 1000).toFixed(3), true); // kWh auf 3 Nachkommastellen
              
                  log(`Hausverbrauch addiert. Neue Summe: ${currentDailyConsumptionWh.toFixed(2)} Wh (${(currentDailyConsumptionWh / 1000).toFixed(3)} kWh)`);
              
                  // Werte für die nächste Iteration speichern
                  lastPowerValue = currentPower;
                  lastUpdateTime = currentTime;
              });
              
              // 3. Reset der Tagessummen um Mitternacht
              schedule('0 0 * * *', async function () {
                  log("Mitternacht erreicht, setze täglichen Hausverbrauch zurück.");
                  setState(dailyHouseConsumptionWhId, 0, true);
                  setState(dailyHouseConsumptionKWhId, 0, true);
              
                  // Wichtig: lastUpdateTime muss auch zurückgesetzt werden für korrekte delta-t Berechnung am neuen Tag
                  // und lastPowerValue für den ersten Wert des neuen Tages
                  lastUpdateTime = new Date().getTime();
                  lastPowerValue = parseFloat((await getStateAsync(currentHouseConsumptionPowerId)).val) || 0; // Aktuellen Power-Wert erneut holen
              
                  log("Täglicher Hausverbrauch wurde zurückgesetzt.");
              });
              
              // Optional: Info-Log beim Start des Skripts
              log("Skript zum Überwachen des täglichen Hausverbrauchs gestartet.");
              
              1. Akku Be-und Entladung (schreibt die Werte (batIn und batOut) in DP's , einmal Wh und einmal kWh und wird täglich um 00:00 Uhr auf '0' gesetzt)
              // ====== Konfiguration START ======
              // Datenpunkt-ID, die die aktuelle Leistung in Watt liefert.
              // Negativ für BatOut, Positiv für BatIn.
              // Diesen Wert musst du an deine ioBroker-Installation anpassen!
              const powerStateId = "senec.0.ENERGY.GUI_BAT_DATA_POWER"; // Beispiel: smartmeter.0.0_1_123_456_789.power_total_current
              
              // Datenpunkt-ID für die tägliche BatOut in Wh (wird erstellt, falls nicht vorhanden)
              const dailyBatOutWhStateId = "0_userdata.0.Energie.Senec.Bat_Out_Wh";
              // Datenpunkt-ID für die tägliche BatOut in kWh
              const dailyBatOutKWhStateId = "0_userdata.0.Energie.Senec.Bat_Out_kWh";
              
              // Datenpunkt-ID für den täglichen BatIn in Wh (wird erstellt, falls nicht vorhanden)
              const dailyBatInWhStateId = "0_userdata.0.Energie.Senec.Bat_In_Wh";
              // Datenpunkt-ID für den täglichen BatIn in kWh
              const dailyBatInKWhStateId = "0_userdata.0.Energie.Senec.Bat_In_kWh";
              
              // ====== Konfiguration ENDE ======
              
              
              // Globale Variablen für die Berechnung der Tagessummen
              let lastPowerValue = 0; // Letzter bekannter Leistungswert
              let lastUpdateTime = new Date().getTime(); // Zeitpunkt der letzten Aktualisierung in Millisekunden
              
              // Funktion zum Initialisieren der Datenpunkte (falls nicht vorhanden)
              function createStates() {
                  createState(dailyBatOutWhStateId, 0, {
                      name: 'Daily BatOut (Wh)',
                      type: 'number',
                      read: true,
                      write: false,
                      role: 'value.power.consumption',
                      unit: 'Wh'
                  });
                  createState(dailyBatOutKWhStateId, 0, {
                      name: 'Daily BatOut (kWh)',
                      type: 'number',
                      read: true,
                      write: false,
                      role: 'value.power.consumption',
                      unit: 'kWh'
                  });
                  createState(dailyBatInWhStateId, 0, {
                      name: 'Daily BatIn (Wh)',
                      type: 'number',
                      read: true,
                      write: false,
                      role: 'value.power.consumption',
                      unit: 'Wh'
                  });
                  createState(dailyBatInKWhStateId, 0, {
                      name: 'Daily BatIn (kWh)',
                      type: 'number',
                      read: true,
                      write: false,
                      role: 'value.power.consumption',
                      unit: 'kWh'
                  });
              }
              
              // Beim Start des Skripts Datenpunkte erstellen und initialisieren
              createStates();
              
              // Werte aus den Datenpunkten beim Skriptstart lesen, falls schon vorhanden
              // Prüfen, ob die Datenpunkte bereits einen Wert haben, ansonsten auf 0 setzen
              let initialBatOutState = getState(dailyBatOutWhStateId);
              if (initialBatOutState && initialBatOutState.val !== null) {
                  log(`Initialer Wert für BatOut Wh: ${initialBatOutState.val}`);
              } else {
                  setState(dailyBatOutWhStateId, 0);
                  log(`Datenpunkt ${dailyBatOutWhStateId} auf 0 gesetzt.`);
              }
              
              let initialBatInState = getState(dailyBatInWhStateId);
              if (initialBatInState && initialBatInState.val !== null) {
                  log(`Initialer Wert für BatIn Wh: ${initialBatInState.val}`);
              } else {
                  setState(dailyBatInWhStateId, 0);
                  log(`Datenpunkt ${dailyBatInWhStateId} auf 0 gesetzt.`);
              }
              
              // Trigger bei Änderung des Leistungsdatenpunkts
              on({ id: powerStateId, change: 'ne' }, async function (obj) {
                  const currentPower = obj.state.val; // Aktueller Leistungswert in Watt
                  const currentTime = new Date().getTime(); // Aktueller Zeitstempel in Millisekunden
              
                  // Wenn der Wert zum ersten Mal kommt oder Zeitunterschied zu gering ist
                  if (lastPowerValue === 0 && lastUpdateTime === new Date().getTime()) { // Initialisierung
                      lastPowerValue = currentPower;
                      lastUpdateTime = currentTime;
                      log("Initialisierung der Leistungswerte.");
                      return;
                  }
              
                  const timeDiffSeconds = (currentTime - lastUpdateTime) / 1000; // Zeitdifferenz in Sekunden
              
                  if (timeDiffSeconds <= 0) { // Falls keine Zeit vergangen ist (z.B. zu schnelle Updates)
                      log("Zeitdifferenz ist 0 oder negativ, Überspringe Berechnung.");
                      return;
                  }
              
                  // Leistung * Zeit = Energie (Wattsekunden)
                  const energyWs = currentPower * timeDiffSeconds;
                  const energyWh = energyWs / 3600; // Umrechnung von Wattsekunden in Wattstunden
              
                  log(`Aktuelle Leistung: ${currentPower} W, Zeitdiff: ${timeDiffSeconds.toFixed(2)} s, Berechnete Energie: ${energyWh.toFixed(2)} Wh`);
              
                  // Sicherstellen, dass die gelesenen Werte Zahlen sind (durch parseFloat oder den Unary-Plus-Operator)
                  let currentBatInWh = parseFloat((await getStateAsync(dailyBatInWhStateId)).val) || 0;
                  let currentBatOutWh = parseFloat((await getStateAsync(dailyBatOutWhStateId)).val) || 0;
              
                  if (currentPower < 0) { // BatOut
                      currentBatOutWh += Math.abs(energyWh); // Absolutwert, damit die Summe positiv ist
                      setState(dailyBatOutWhStateId, currentBatOutWh.toFixed(2), true);
                      setState(dailyBatOutKWhStateId, (currentBatOutWh / 1000).toFixed(3), true);
                      log(`BatOut addiert. Neue Summe: ${currentBatOutWh.toFixed(2)} Wh`);
                  } else { // BatIn
                      currentBatInWh += energyWh;
                      setState(dailyBatInWhStateId, currentBatInWh.toFixed(2), true);
                      setState(dailyBatInKWhStateId, (currentBatInWh / 1000).toFixed(3), true);
                      log(`BatIn addiert. Neue Summe: ${currentBatInWh.toFixed(2)} Wh`);
                  }
              
                  // Werte für die nächste Iteration speichern
                  lastPowerValue = currentPower;
                  lastUpdateTime = currentTime;
              });
              
              
              // Reset der Tagessummen um Mitternacht
              schedule('0 0 * * *', async function () {
                  log("Mitternacht erreicht, setze Tagessummen zurück.");
                  setState(dailyBatOutWhStateId, 0, true);
                  setState(dailyBatOutKWhStateId, 0, true);
                  setState(dailyBatInWhStateId, 0, true);
                  setState(dailyBatInKWhStateId, 0, true);
                  // lastUpdateTime muss auch zurückgesetzt werden, um korrekte delta-t Berechnung am neuen Tag zu gewährleisten
                  lastUpdateTime = new Date().getTime();
                  lastPowerValue = (await getStateAsync(powerStateId)).val || 0; // Aktuellen Power-Wert erneut holen
              
                  log("Tagessummen wurden zurückgesetzt.");
              });
              
              // Optional: Für Debugging, wenn das Skript startet
              log("Skript zum Überwachen von BatOut und BatIn gestartet.");
              
              1. Grid Power (schreibt die Werte in DP's (Einspeisung und Netzbezug), einmal Wh und einmal kWh und wird täglich um 00:00 Uhr auf '0' gesetzt)
              // ====== Konfiguration START ======
              // Datenpunkt-ID, die die aktuelle Leistung in Watt liefert.
              // Negativ für Einspeisung, Positiv für Netzbezug.
              // Diesen Wert musst du an deine ioBroker-Installation anpassen!
              const powerStateId = "senec.0.ENERGY.GUI_GRID_POW"; // Beispiel: smartmeter.0.0_1_123_456_789.power_total_current
              
              // Datenpunkt-ID für die tägliche Einspeisung in Wh (wird erstellt, falls nicht vorhanden)
              const dailyFeedInWhStateId = "0_userdata.0.Energie.Senec.Tages_Einspeisung_Wh";
              // Datenpunkt-ID für die tägliche Einspeisung in kWh
              const dailyFeedInKWhStateId = "0_userdata.0.Energie.Senec.Tages_Einspeisung_kWh";
              
              // Datenpunkt-ID für den täglichen Netzbezug in Wh (wird erstellt, falls nicht vorhanden)
              const dailyGridPurchaseWhStateId = "0_userdata.0.Energie.Senec.Tages_Netzbezug_Wh";
              // Datenpunkt-ID für den täglichen Netzbezug in kWh
              const dailyGridPurchaseKWhStateId = "0_userdata.0.Energie.Senec.Tages_Netzbezug_kWh";
              
              // ====== Konfiguration ENDE ======
              
              
              // Globale Variablen für die Berechnung der Tagessummen
              let lastPowerValue = 0; // Letzter bekannter Leistungswert
              let lastUpdateTime = new Date().getTime(); // Zeitpunkt der letzten Aktualisierung in Millisekunden
              
              // Funktion zum Initialisieren der Datenpunkte (falls nicht vorhanden)
              function createStates() {
                  createState(dailyFeedInWhStateId, 0, {
                      name: 'Tägliche Einspeisung (Wh)',
                      type: 'number',
                      read: true,
                      write: false,
                      role: 'value.power.consumption',
                      unit: 'Wh'
                  });
                  createState(dailyFeedInKWhStateId, 0, {
                      name: 'Tägliche Einspeisung (kWh)',
                      type: 'number',
                      read: true,
                      write: false,
                      role: 'value.power.consumption',
                      unit: 'kWh'
                  });
                  createState(dailyGridPurchaseWhStateId, 0, {
                      name: 'Täglicher Netzbezug (Wh)',
                      type: 'number',
                      read: true,
                      write: false,
                      role: 'value.power.consumption',
                      unit: 'Wh'
                  });
                  createState(dailyGridPurchaseKWhStateId, 0, {
                      name: 'Täglicher Netzbezug (kWh)',
                      type: 'number',
                      read: true,
                      write: false,
                      role: 'value.power.consumption',
                      unit: 'kWh'
                  });
              }
              
              // Beim Start des Skripts Datenpunkte erstellen und initialisieren
              createStates();
              
              // Werte aus den Datenpunkten beim Skriptstart lesen, falls schon vorhanden
              // Prüfen, ob die Datenpunkte bereits einen Wert haben, ansonsten auf 0 setzen
              let initialFeedInState = getState(dailyFeedInWhStateId);
              if (initialFeedInState && initialFeedInState.val !== null) {
                  log(`Initialer Wert für Einspeisung Wh: ${initialFeedInState.val}`);
              } else {
                  setState(dailyFeedInWhStateId, 0);
                  log(`Datenpunkt ${dailyFeedInWhStateId} auf 0 gesetzt.`);
              }
              
              let initialGridPurchaseState = getState(dailyGridPurchaseWhStateId);
              if (initialGridPurchaseState && initialGridPurchaseState.val !== null) {
                  log(`Initialer Wert für Netzbezug Wh: ${initialGridPurchaseState.val}`);
              } else {
                  setState(dailyGridPurchaseWhStateId, 0);
                  log(`Datenpunkt ${dailyGridPurchaseWhStateId} auf 0 gesetzt.`);
              }
              
              // Trigger bei Änderung des Leistungsdatenpunkts
              on({ id: powerStateId, change: 'ne' }, async function (obj) {
                  const currentPower = obj.state.val; // Aktueller Leistungswert in Watt
                  const currentTime = new Date().getTime(); // Aktueller Zeitstempel in Millisekunden
              
                  // Wenn der Wert zum ersten Mal kommt oder Zeitunterschied zu gering ist
                  // Hier ist es wichtig, dass lastPowerValue und lastUpdateTime initialisiert werden
                  if (lastPowerValue === 0 && lastUpdateTime === new Date().getTime()) { // Initialisierung
                      const powerState = getState(powerStateId);
                      if (powerState && powerState.val !== null) {
                          lastPowerValue = powerState.val;
                      } else {
                          lastPowerValue = 0; // Fallback, falls kein Wert verfügbar
                      }
                      lastUpdateTime = currentTime;
                      log("Initialisierung der Leistungswerte beim ersten Trigger.");
                      return;
                  }
              
              
                  const timeDiffSeconds = (currentTime - lastUpdateTime) / 1000; // Zeitdifferenz in Sekunden
              
                  if (timeDiffSeconds <= 0) { // Falls keine Zeit vergangen ist (z.B. zu schnelle Updates)
                      log("Zeitdifferenz ist 0 oder negativ, Überspringe Berechnung.");
                      return;
                  }
              
                  // Leistung * Zeit = Energie (Wattsekunden)
                  const energyWs = currentPower * timeDiffSeconds;
                  const energyWh = energyWs / 3600; // Umrechnung von Wattsekunden in Wattstunden
              
                  log(`Aktuelle Leistung: ${currentPower} W, Zeitdiff: ${timeDiffSeconds.toFixed(2)} s, Berechnete Energie: ${energyWh.toFixed(2)} Wh`);
              
                  // **KORRIGIERTER BEREICH START**
                  // Sicherstellen, dass die gelesenen Werte Zahlen sind (durch parseFloat oder den Unary-Plus-Operator)
                  let currentFeedInWh = parseFloat((await getStateAsync(dailyFeedInWhStateId)).val) || 0;
                  let currentGridPurchaseWh = parseFloat((await getStateAsync(dailyGridPurchaseWhStateId)).val) || 0;
                  // **KORRIGIERTER BEREICH ENDE**
              
                  if (currentPower < 0) { // Einspeisung
                      currentFeedInWh += Math.abs(energyWh); // Absolutwert, damit die Summe positiv ist
                      setState(dailyFeedInWhStateId, currentFeedInWh.toFixed(2), true);
                      setState(dailyFeedInKWhStateId, (currentFeedInWh / 1000).toFixed(3), true);
                      log(`Einspeisung addiert. Neue Summe: ${currentFeedInWh.toFixed(2)} Wh`);
                  } else { // Netzbezug
                      currentGridPurchaseWh += energyWh;
                      setState(dailyGridPurchaseWhStateId, currentGridPurchaseWh.toFixed(2), true);
                      setState(dailyGridPurchaseKWhStateId, (currentGridPurchaseWh / 1000).toFixed(3), true);
                      log(`Netzbezug addiert. Neue Summe: ${currentGridPurchaseWh.toFixed(2)} Wh`);
                  }
              
                  // Werte für die nächste Iteration speichern
                  lastPowerValue = currentPower;
                  lastUpdateTime = currentTime;
              });
              
              
              // Reset der Tagessummen um Mitternacht
              schedule('0 0 * * *', async function () {
                  log("Mitternacht erreicht, setze Tagessummen zurück.");
                  setState(dailyFeedInWhStateId, 0, true);
                  setState(dailyFeedInKWhStateId, 0, true);
                  setState(dailyGridPurchaseWhStateId, 0, true);
                  setState(dailyGridPurchaseKWhStateId, 0, true);
                  // lastUpdateTime muss auch zurückgesetzt werden, um korrekte delta-t Berechnung am neuen Tag zu gewährleisten
                  lastUpdateTime = new Date().getTime();
                  lastPowerValue = (await getStateAsync(powerStateId)).val || 0; // Aktuellen Power-Wert erneut holen
              
                  log("Tagessummen wurden zurückgesetzt.");
              });
              
              // Optional: Für Debugging, wenn das Skript startet
              log("Skript zum Überwachen von Einspeisung und Netzbezug gestartet.");
              

              wer Lust hat kann ja mit testen ob das so passt.

              R Offline
              R Offline
              RicoStriese
              schrieb am zuletzt editiert von
              #371

              @icebear Danke für die Arbeit, habe alles übernommen. Es funktioniert super. Nochmals besten Danke für die Mühe.:clap: :clap:

              1 Antwort Letzte Antwort
              0
              • icebearI icebear

                @musicnrw said in [Neuer Adapter] Senec Home Adapter:

                Also, wie und wo richte ich Deine Scripte in meiner Konfiguration ein,

                Scripte von oben kopieren und dann auf 'Skripte'

                Senec_1.png

                Dann legst 1. Einen neuen Ordner (im Root-Verzeichnis an), dann 2. den Ordner anklicken (das er 'blau' markiert ist) und dann oben auf 3. das '+' für neues Script.

                Senec_2.png

                Dann JS auswählen

                Senec_3.png

                Dann einen Namen vergeben und das kopierte Script einfügen.

                Senec_4.png

                Auf den 'roten Pfeil' Script starten.

                Unter > 0_userdata.0.Energie.Senec sollten dann die DP erstellt und mit den Werten befüllt werden.

                M Offline
                M Offline
                musicnrw
                schrieb am zuletzt editiert von
                #372

                @icebear, super. Vielen Dank für Deine Arbeit. Ich werde es am Wochenende umsetzen.

                1 Antwort Letzte Antwort
                0
                • D Offline
                  D Offline
                  DiMe
                  schrieb am zuletzt editiert von
                  #373

                  Hi Zusammen,
                  habe heute Zeit gehabt und habe mich dem Thema angenommen. War schneller fertig als ich dachte. Es klappt alles wunderbar. Vielen Dank für die sehr wertvollen Beiträge!

                  1 Antwort Letzte Antwort
                  0
                  • H Offline
                    H Offline
                    HansJochen
                    schrieb am zuletzt editiert von
                    #374

                    Guten Morgen,

                    ich bin gerade auf diesen Thread gestoßen und habe das für mich sehr ähnlich gelöst wie Icebear mit dem hier geteilten Script. Ich möchte hier eine Idee teilen, die aber anders als dieses Script nicht einfach für jeden Senec Besitzer möglich ist und dann auch für jeden etwas anders. Vielleicht mag das aber der eine oder andere für sich auch umsetzen.

                    Das Summieren der Speicher Werte funktioniert natürlich nur, solange ioBroker läuft. Das wird meistens der Fall sein. Ich ermittle seit Jahren die summierten Verbrauchswerte meiner Wärmepumpe auf die gleiche Art mit gutem Ergebnis. Durch Updates oder Wartung verlorene Verbrauchswerte spielen dort in meiner Statistik eigentlich keine große Rolle. Allerdings: Wenn der Speicher gerade mal nicht kommunizieren mag (NPU Fehler oder sonstwas), gehen ja auch Werte verloren.

                    Für die Einspeisung und den Netzbezug habe ich Zähler, die ich über einen Tibber Pulse auslese. Die haben geringere Auflösung (die Nachkommastelle wird nicht übermittelt), sind aber sonst natürlich sehr genau und ausfallsicher. Deswegen verwende ich für diese Werte die Zählerwerte und berechne nur Hausverbrauch und Produktion so, wie im in Icebears Skript.

                    Die Möglichkeiten sind hier vielfältig, je nach Installation und Alter der PV Anlage messen die Zähler etwas anderes. Der Weg ist folgender:

                    • Ich habe einen Cronjob, der mir jede Stunde, jeden Tag, jeden Monat und jedes Jahr den Zählerstand in einen Datenpunkt kopiert. Bei Skript Neustart prüfe ich den Zeitstempel dieser Zählerwerte und aktualisiere sie, falls sie älter als erwartet sind (dann natürlich zu spät, aber das ist besser, als einen ganzen Jahreswechsel zu verpassen).
                    • Ein Trigger auf jeden Zähler berechnet die Differenz aus aktuellem Zählerstand und Zählerstand zu Beginn der aktuellen Stunde, Monats, ... und speichert das als Verbrauchswert in diesem Zeitraum.

                    Die beste Lösung wäre vermutlich eine Mischung der beiden Vorgehensweisen: Die dynamische Berechnung wie in Icebears Script (wegen der höheren Auflösung) mit Korrektur auf Basis der Zählerstände, soweit erforderlich.

                    1 Antwort Letzte Antwort
                    1
                    • T Offline
                      T Offline
                      ToniTL
                      schrieb am zuletzt editiert von
                      #375

                      Hallo,
                      ich habe mal eine Frage bezüglich des Projekts an sich. Läuft das noch oder wurde das jetzt auf Eis gelegt?

                      Leider hört es sich teils so an, das SENEC vielleicht den API-Zugang sperren könnte. Für mich zumindest kein gutes Zeichen, wenn dieser Zugang beim V4 gänzlich weggelassen wurde.
                      Ich habe ebenso jetzt eine manuelle Zählung eingerichtet, welche aber auf die Zuverlässigkeit und Beständigkeit von SENEC aufbaut.

                      Hat sich jemand schon einmal darüber Gedanken gemacht, den Enfluri direkt anzuzapfen? Das heißt als read only (sniffer)? Welche Daten werden darüber genau geliefert?

                      Mit diesem Gedanken spiele ich schon länger und es würde unabhängiger von SENECs Laune laufen. Ich verstehe den Sicherheitsgedanken seitens SENEC und den damit verbundenen Onlinezwang. Aber bei jedem Update kann Schluss mit der API sein.
                      Ich mag die Unabhängigkeit und mein persönliches Hausdashboard. Ebenso läuft meine PV-Überschussbeladung meiner Wallbox mit Daten von SENEC. Ich habe es zwar auf das Nötigste reduziert (Einspeisung und Bezug über Wattwächter). Aber Hausverbrauch an sich wäre noch nett.

                      O 1 Antwort Letzte Antwort
                      1
                      • T ToniTL

                        Hallo,
                        ich habe mal eine Frage bezüglich des Projekts an sich. Läuft das noch oder wurde das jetzt auf Eis gelegt?

                        Leider hört es sich teils so an, das SENEC vielleicht den API-Zugang sperren könnte. Für mich zumindest kein gutes Zeichen, wenn dieser Zugang beim V4 gänzlich weggelassen wurde.
                        Ich habe ebenso jetzt eine manuelle Zählung eingerichtet, welche aber auf die Zuverlässigkeit und Beständigkeit von SENEC aufbaut.

                        Hat sich jemand schon einmal darüber Gedanken gemacht, den Enfluri direkt anzuzapfen? Das heißt als read only (sniffer)? Welche Daten werden darüber genau geliefert?

                        Mit diesem Gedanken spiele ich schon länger und es würde unabhängiger von SENECs Laune laufen. Ich verstehe den Sicherheitsgedanken seitens SENEC und den damit verbundenen Onlinezwang. Aber bei jedem Update kann Schluss mit der API sein.
                        Ich mag die Unabhängigkeit und mein persönliches Hausdashboard. Ebenso läuft meine PV-Überschussbeladung meiner Wallbox mit Daten von SENEC. Ich habe es zwar auf das Nötigste reduziert (Einspeisung und Bezug über Wattwächter). Aber Hausverbrauch an sich wäre noch nett.

                        O Online
                        O Online
                        oxident
                        schrieb am zuletzt editiert von
                        #376

                        @tonitl Das wäre schon recht hilfreich und wichtig. Auch für die, die mit den NPU-Abstürzen zu kämpfen haben.

                        Ich glaube, der Bus wird sogar am Senec selber durchgeschliffen, oder?

                        B 1 Antwort Letzte Antwort
                        1
                        • O oxident

                          @tonitl Das wäre schon recht hilfreich und wichtig. Auch für die, die mit den NPU-Abstürzen zu kämpfen haben.

                          Ich glaube, der Bus wird sogar am Senec selber durchgeschliffen, oder?

                          B Offline
                          B Offline
                          Blockmove
                          schrieb am zuletzt editiert von
                          #377

                          @oxident said in [Neuer Adapter] Senec Home Adapter:

                          @tonitl Das wäre schon recht hilfreich und wichtig. Auch für die, die mit den NPU-Abstürzen zu kämpfen haben.

                          Ich glaube, der Bus wird sogar am Senec selber durchgeschliffen, oder?

                          Mit sowas sollte es funktionieren:
                          RS485-Sniffer

                          Seit dem ich mich vom Senec-Adapter verabschiedet hab und die Abfrage nur noch über curl mache, hab ich keinerlei NPU-Abstürze mehr gehabt.
                          Die Kommunikationsfehler sind auch sehr viel weniger geworden. Ich hol mir halt nur noch die Daten, die ich wirklich brauche.

                          The difference beetween Man and Boys:
                          The price of their toys 😀

                          T 1 Antwort Letzte Antwort
                          0
                          • B Blockmove

                            @oxident said in [Neuer Adapter] Senec Home Adapter:

                            @tonitl Das wäre schon recht hilfreich und wichtig. Auch für die, die mit den NPU-Abstürzen zu kämpfen haben.

                            Ich glaube, der Bus wird sogar am Senec selber durchgeschliffen, oder?

                            Mit sowas sollte es funktionieren:
                            RS485-Sniffer

                            Seit dem ich mich vom Senec-Adapter verabschiedet hab und die Abfrage nur noch über curl mache, hab ich keinerlei NPU-Abstürze mehr gehabt.
                            Die Kommunikationsfehler sind auch sehr viel weniger geworden. Ich hol mir halt nur noch die Daten, die ich wirklich brauche.

                            T Offline
                            T Offline
                            ToniTL
                            schrieb am zuletzt editiert von
                            #378

                            @blockmove läuft bei dir das Setup schon über den enfluri oder rufst du die Daten über curl vom senec selbst ab?

                            Solltest du es schon über den enfluri betreiben, könntest du mitteilen wie du es angestellt hast? Schritt für Schritt Anleitung?

                            B 1 Antwort Letzte Antwort
                            0
                            • T ToniTL

                              @blockmove läuft bei dir das Setup schon über den enfluri oder rufst du die Daten über curl vom senec selbst ab?

                              Solltest du es schon über den enfluri betreiben, könntest du mitteilen wie du es angestellt hast? Schritt für Schritt Anleitung?

                              B Offline
                              B Offline
                              Blockmove
                              schrieb am zuletzt editiert von
                              #379

                              @tonitl said in [Neuer Adapter] Senec Home Adapter:

                              @blockmove läuft bei dir das Setup schon über den enfluri oder rufst du die Daten über curl vom senec selbst ab?

                              Solltest du es schon über den enfluri betreiben, könntest du mitteilen wie du es angestellt hast? Schritt für Schritt Anleitung?

                              Ich ruf über curl die Daten vom Senec ab.
                              Es kommt vereinzelt zu Fehlermeldungen, aber die Kommunikation fängt sich immer wieder.
                              Seitdem keinerlei Probleme mehr mit der Steuerung meines Heizstabes und meiner 2 go-e Wallboxen.

                              Ich muss allerdings auch sagen, dass ich das nun alles über Node RED mache und nicht mehr über ioBroker.
                              Aber das ist ein anderes Thema. Der curl-Aufruf wird in ioBroker genauso funktionieren.

                              The difference beetween Man and Boys:
                              The price of their toys 😀

                              1 Antwort Letzte Antwort
                              0
                              • M Offline
                                M Offline
                                musicnrw
                                schrieb am zuletzt editiert von
                                #380

                                Ich habe gerade festgestellt, dass es im Javasript Warnungen gibt, weiß aber nicht, in wieweit die kritisch sind.
                                Und zwar im Gridpower-Script:
                                javascript.0 20:17:16.511 warn at Object.<anonymous> (script.js.common.Senec.GridPower:123:9)
                                javascript.0 20:17:16.512 warn at Object.<anonymous> (script.js.common.Senec.GridPower:124:9)

                                im Hausverbrauch Script:
                                javascript.0 20:18:16.667 warn at Object.<anonymous> (script.js.common.Senec.Hausverbrauch:111:5)
                                javascript.0 20:18:16.668 warn at Object.<anonymous> (script.js.common.Senec.Hausverbrauch:112:5)

                                und im Akku Be- und Entladung Script
                                javascript.0 20:19:20.174 warn at Object.<anonymous> (script.js.common.Senec.AkkuBeEntladung:114:9)
                                javascript.0 20:19:20.175 warn at Object.<anonymous> (script.js.common.Senec.AkkuBeEntladung:115:9)

                                Alle Warnungen wiederholen sich alle 10 Sekunden im Debug-Fenster.
                                Kann mir da jemand etwas zu sagen? Ich habe die Scripte nach der Anleitung von icebear bei mir im ioBroker angelegt.

                                Danke und Gruß
                                Thomas

                                icebearI 1 Antwort Letzte Antwort
                                0
                                • M musicnrw

                                  Ich habe gerade festgestellt, dass es im Javasript Warnungen gibt, weiß aber nicht, in wieweit die kritisch sind.
                                  Und zwar im Gridpower-Script:
                                  javascript.0 20:17:16.511 warn at Object.<anonymous> (script.js.common.Senec.GridPower:123:9)
                                  javascript.0 20:17:16.512 warn at Object.<anonymous> (script.js.common.Senec.GridPower:124:9)

                                  im Hausverbrauch Script:
                                  javascript.0 20:18:16.667 warn at Object.<anonymous> (script.js.common.Senec.Hausverbrauch:111:5)
                                  javascript.0 20:18:16.668 warn at Object.<anonymous> (script.js.common.Senec.Hausverbrauch:112:5)

                                  und im Akku Be- und Entladung Script
                                  javascript.0 20:19:20.174 warn at Object.<anonymous> (script.js.common.Senec.AkkuBeEntladung:114:9)
                                  javascript.0 20:19:20.175 warn at Object.<anonymous> (script.js.common.Senec.AkkuBeEntladung:115:9)

                                  Alle Warnungen wiederholen sich alle 10 Sekunden im Debug-Fenster.
                                  Kann mir da jemand etwas zu sagen? Ich habe die Scripte nach der Anleitung von icebear bei mir im ioBroker angelegt.

                                  Danke und Gruß
                                  Thomas

                                  icebearI Online
                                  icebearI Online
                                  icebear
                                  schrieb am zuletzt editiert von
                                  #381

                                  @musicnrw

                                  Ich benutze diese Scripte nicht mehr, weil ich die in einem Script zusammengefasst habe.

                                  Hier das Script welches ich jetzt verwende, du mußt dort halt die Datenpunkte evtl. auf deine Gegebenheiten anpassen. Beim ersten Start sollten vom Script alle erforderlichen Datenpunkte, wenn sie nicht vorhanden sind, erstellt werden.

                                  Wichtig!!! Das ist ein TypeScript. Also beim erstellen nicht JS nehmen sondern ganz rechts TS

                                  Kannst ja mal schauen.

                                  // -----------------------------------------------------------------------------------
                                  // 1. KONFIGURATION
                                  // -----------------------------------------------------------------------------------
                                  
                                  // Basis-Pfad für eigene Datenpunkte (muss in ioBroker unter 0_userdata.0.Energie.Senec. konsolidiert werden)
                                  const BASE_DP_PATH = '0_userdata.0.Energie.Senec';
                                  
                                  // Senec Quell-Datenpunkte (Leistung in Watt)
                                  // WICHTIG: Prüfen, ob die IDs in Ihrer Installation korrekt sind!
                                  const PowerSourceIds = {
                                      // Skript 1: Negativ = Einspeisung, Positiv = Netzbezug
                                      GRID_POWER: "senec.0.ENERGY.GUI_GRID_POW",
                                      // Skript 2: Hausverbrauch (immer positiv)
                                      HOUSE_CONSUMPTION_POWER: "senec.0.ENERGY.GUI_HOUSE_POW",
                                      // Skript 3: Positiv = Laden (In), Negativ = Entladen (Out)
                                      BAT_POWER: "senec.0.ENERGY.GUI_BAT_DATA_POWER",
                                      // Skript 4: PV-Produktion (immer positiv)
                                      PV_POWER: "senec.0.ENERGY.GUI_INVERTER_POWER",
                                  };
                                  
                                  // Ziel-Datenpunkte (Tagesenergie in Wh und kWh)
                                  const TargetStateIds = {
                                      // Skript 1
                                      DAILY_FEED_IN_WH: `${BASE_DP_PATH}.Tages_Einspeisung_Wh`,
                                      DAILY_FEED_IN_KWH: `${BASE_DP_PATH}.Tages_Einspeisung_kWh`,
                                      DAILY_GRID_PURCHASE_WH: `${BASE_DP_PATH}.Tages_Netzbezug_Wh`,
                                      DAILY_GRID_PURCHASE_KWH: `${BASE_DP_PATH}.Tages_Netzbezug_kWh`,
                                  
                                      // Skript 2
                                      DAILY_HOUSE_CONSUMPTION_WH: `${BASE_DP_PATH}.Tages_Hausverbrauch_Wh`,
                                      DAILY_HOUSE_CONSUMPTION_KWH: `${BASE_DP_PATH}.Tages_Hausverbrauch_kWh`,
                                  
                                      // Skript 3 (Bat_Out = Entladen, Bat_In = Laden)
                                      DAILY_BAT_OUT_WH: `${BASE_DP_PATH}.Bat_Out_Wh`,
                                      DAILY_BAT_OUT_KWH: `${BASE_DP_PATH}.Bat_Out_kWh`,
                                      DAILY_BAT_IN_WH: `${BASE_DP_PATH}.Bat_In_Wh`,
                                      DAILY_BAT_IN_KWH: `${BASE_DP_PATH}.Bat_In_kWh`,
                                  
                                      // Skript 4 (Zusätzliche PV- und Bilanz-Werte)
                                      DAILY_PV_WH: `${BASE_DP_PATH}.Tageswerte.pv_wh`,
                                      DAILY_PV_KWH: `${BASE_DP_PATH}.Tageswerte.pv_kwh`,
                                      DAILY_BILANZ_KWH: `${BASE_DP_PATH}.Tageswerte.bilanz_kwh`,
                                      DAILY_GRID_WH_S4: `${BASE_DP_PATH}.Tageswerte.netz_wh`, // Duplikat von DAILY_GRID_PURCHASE_WH
                                      DAILY_FEED_IN_WH_S4: `${BASE_DP_PATH}.Tageswerte.einspeisung_wh`, // Duplikat von DAILY_FEED_IN_WH
                                  };
                                  
                                  // -----------------------------------------------------------------------------------
                                  // 2. TYPEN, GLOBALE VARIABLEN UND HILFSFUNKTIONEN
                                  // -----------------------------------------------------------------------------------
                                  
                                  // Typsichere Definition der globalen Variablen für die Integration
                                  // Der Schlüssel ist die PowerSourceId, der Wert speichert den letzten Zustand.
                                  interface LastState {
                                      lastPower: number;
                                      lastTime: number; // Zeitstempel in Millisekunden
                                  }
                                  
                                  // Speichert den letzten Zustand für jede Quelle separat
                                  const lastStates: { [key: string]: LastState } = {};
                                  
                                  // Typsichere Definition für die Erstellung der States
                                  interface CommonStateOptions {
                                      name: string;
                                      type: "number";
                                      read: boolean;
                                      write: boolean;
                                      role: string;
                                      unit: "Wh" | "kWh";
                                      desc?: string;
                                  }
                                  
                                  // Definiert alle zu erstellenden Datenpunkte
                                  const stateDefinitions: { id: string; initialValue: number; common: CommonStateOptions }[] = [
                                      // Skript 1
                                      { id: TargetStateIds.DAILY_FEED_IN_WH, initialValue: 0, common: { name: 'Tägliche Einspeisung (Wh)', type: 'number', read: true, write: false, role: 'value.power.consumption', unit: 'Wh' } },
                                      { id: TargetStateIds.DAILY_FEED_IN_KWH, initialValue: 0, common: { name: 'Tägliche Einspeisung (kWh)', type: 'number', read: true, write: false, role: 'value.power.consumption', unit: 'kWh' } },
                                      { id: TargetStateIds.DAILY_GRID_PURCHASE_WH, initialValue: 0, common: { name: 'Täglicher Netzbezug (Wh)', type: 'number', read: true, write: false, role: 'value.power.consumption', unit: 'Wh' } },
                                      { id: TargetStateIds.DAILY_GRID_PURCHASE_KWH, initialValue: 0, common: { name: 'Täglicher Netzbezug (kWh)', type: 'number', read: true, write: false, role: 'value.power.consumption', unit: 'kWh' } },
                                  
                                      // Skript 2
                                      { id: TargetStateIds.DAILY_HOUSE_CONSUMPTION_WH, initialValue: 0, common: { name: 'Täglicher Hausverbrauch (Wh)', type: 'number', read: true, write: false, role: 'value.power.consumption', unit: 'Wh', desc: 'Summierter Hausverbrauch für den aktuellen Tag in Wattstunden' } },
                                      { id: TargetStateIds.DAILY_HOUSE_CONSUMPTION_KWH, initialValue: 0, common: { name: 'Täglicher Hausverbrauch (kWh)', type: 'number', read: true, write: false, role: 'value.power.consumption', unit: 'kWh', desc: 'Summierter Hausverbrauch für den aktuellen Tag in Kilowattstunden' } },
                                  
                                      // Skript 3
                                      { id: TargetStateIds.DAILY_BAT_OUT_WH, initialValue: 0, common: { name: 'Daily BatOut (Wh)', type: 'number', read: true, write: false, role: 'value.power.consumption', unit: 'Wh' } },
                                      { id: TargetStateIds.DAILY_BAT_OUT_KWH, initialValue: 0, common: { name: 'Daily BatOut (kWh)', type: 'number', read: true, write: false, role: 'value.power.consumption', unit: 'kWh' } },
                                      { id: TargetStateIds.DAILY_BAT_IN_WH, initialValue: 0, common: { name: 'Daily BatIn (Wh)', type: 'number', read: true, write: false, role: 'value.power.consumption', unit: 'Wh' } },
                                      { id: TargetStateIds.DAILY_BAT_IN_KWH, initialValue: 0, common: { name: 'Daily BatIn (kWh)', type: 'number', read: true, write: false, role: 'value.power.consumption', unit: 'kWh' } },
                                      
                                      // Skript 4
                                      { id: TargetStateIds.DAILY_PV_WH, initialValue: 0, common: { name: 'Tageswert PV in Wh', type: 'number', read: true, write: false, role: 'value.power', unit: 'Wh' } },
                                      { id: TargetStateIds.DAILY_PV_KWH, initialValue: 0, common: { name: 'Tageswert PV in kWh', type: 'number', read: true, write: false, role: 'value.power', unit: 'kWh' } },
                                      { id: TargetStateIds.DAILY_BILANZ_KWH, initialValue: 0, common: { name: 'Bilanz kWh (PV - Netzbezug)', type: 'number', read: true, write: false, role: 'value.power', unit: 'kWh' } },
                                      // Die folgenden sind Duplikate aus Skript 4, die wir beibehalten, um Skript 4 vollständig abzubilden
                                      { id: TargetStateIds.DAILY_GRID_WH_S4, initialValue: 0, common: { name: 'Tages-Wh Netzbezug (S4)', type: 'number', read: true, write: false, role: 'value.power', unit: 'Wh' } },
                                      { id: TargetStateIds.DAILY_FEED_IN_WH_S4, initialValue: 0, common: { name: 'Tages-Wh Einspeisung (S4)', type: 'number', read: true, write: false, role: 'value.power', unit: 'Wh' } }
                                  ];
                                  
                                  /**
                                   * Erstellt einen Datenpunkt, falls er noch nicht existiert.
                                   * @param id ID des zu erstellenden States.
                                   * @param initialValue Initialer Wert.
                                   * @param common Common-Objekt Definition.
                                   */
                                  function createStateIfNotExists(id: string, initialValue: number, common: CommonStateOptions): void {
                                      if (!existsState(id)) {
                                          createState(id, initialValue, common, (err) => {
                                              if (err) {
                                                  log(`Fehler beim Erstellen des Datenpunkts ${id}: ${err}`, 'error');
                                              } else {
                                                  log(`Datenpunkt ${id} erfolgreich erstellt.`, 'info');
                                              }
                                          });
                                      }
                                  }
                                  
                                  /**
                                   * Liest den Wert eines States sicher und gibt 0 zurück, falls ungültig.
                                   * @param id ID des States.
                                   * @returns Der Wert als Zahl oder 0.
                                   */
                                  async function getStateSafeAsync(id: string): Promise<number> {
                                      const state = await getStateAsync(id);
                                      if (state && typeof state.val === 'number') {
                                          return state.val;
                                      }
                                      // Float-Parsing für den Fall, dass der Wert als String gespeichert wurde (trotz Type 'number')
                                      if (state && state.val !== null && state.val !== undefined) {
                                          const parsed = parseFloat(state.val.toString());
                                          return isNaN(parsed) ? 0 : parsed;
                                      }
                                      return 0;
                                  }
                                  
                                  /**
                                   * Setzt einen Wert in Wh und berechnet und setzt automatisch den zugehörigen kWh-Wert.
                                   * @param whId ID des Wh-Datenpunkts.
                                   * @param kwhId ID des kWh-Datenpunkts.
                                   * @param whValue Wert in Wh.
                                   */
                                  function setEnergyStates(whId: string, kwhId: string, whValue: number): void {
                                      // Wh als echte Zahl speichern (mit 2 Nachkommastellen runden, um Rundungsfehler zu minimieren, aber als NUMBER)
                                      // Wir verwenden Math.round, um Rundungsfehler zu kontrollieren, bevor wir die Zahl speichern.
                                      const roundedWh = Math.round(whValue * 100) / 100; 
                                      setState(whId, roundedWh, true); // **HIER: Übergabe einer Zahl (roundedWh)**
                                  
                                      // kWh auf 3 Nachkommastellen formatieren (als String)
                                      const kwhString = (roundedWh / 1000).toFixed(3);
                                      setState(kwhId, +kwhString, true); // **KORREKTUR: Verwende +kwhString**
                                  }
                                  
                                  // -----------------------------------------------------------------------------------
                                  // 3. INITIALISIERUNG UND HAUPTLOGIK (INTEGRATION)
                                  // -----------------------------------------------------------------------------------
                                  
                                  /**
                                   * Erstellt alle benötigten Datenpunkte und initialisiert die globalen Last-State-Variablen.
                                   */
                                  async function initializeScript(): Promise<void> {
                                      log("Starte Initialisierung des Energie-Bilanz-Skripts.");
                                  
                                      // Erstellen des Basisordners, falls nicht vorhanden (für Skript 4 relevant)
                                      if (!existsObject(`${BASE_DP_PATH}.Tageswerte`)) {
                                          createState(`${BASE_DP_PATH}.Tageswerte`, 'Ordner für zusätzliche Energie-Tagesdatenpunkte', (err) => {
                                              if (err) log(`Fehler beim Erstellen des Ordners: ${err}`, 'error');
                                              else log(`Ordner ${BASE_DP_PATH}.Tageswerte erfolgreich erstellt`, 'info');
                                          });
                                      }
                                  
                                      // Erstellen/Prüfen aller Datenpunkte
                                      for (const definition of stateDefinitions) {
                                          createStateIfNotExists(definition.id, definition.initialValue, definition.common);
                                      }
                                  
                                      // Initialisierung der Last-States für jede Quelle
                                      const allSourceIds = Object.values(PowerSourceIds);
                                      const currentTime = Date.now();
                                  
                                      for (const id of allSourceIds) {
                                          // Versucht, den aktuellen Wert des Power-States zu lesen
                                          const powerState = await getStateSafeAsync(id);
                                          
                                          lastStates[id] = {
                                              lastPower: powerState,
                                              lastTime: currentTime
                                          };
                                          log(`Initialisiere ${id} mit Leistung: ${powerState} W und Zeit: ${currentTime}.`);
                                      }
                                  
                                      log("Initialisierung abgeschlossen.");
                                  }
                                  
                                  /**
                                   * Generische Funktion zur Berechnung der Delta-Energie und Aktualisierung der Ziel-States.
                                   * @param sourceId Die ID des Power-Quell-States (z.B. GRID_POWER).
                                   * @param currentPower Der aktuelle Leistungswert.
                                   * @param currentTime Der aktuelle Zeitstempel.
                                   */
                                  async function processPowerUpdate(sourceId: string, currentPower: number, currentTime: number): Promise<void> {
                                      const lastState = lastStates[sourceId];
                                  
                                      // Sicherstellen der Initialisierung
                                      if (!lastState || lastState.lastTime === currentTime) {
                                          // Sollte nicht passieren, da initializeScript aufgerufen wird, aber als Fallback
                                          lastStates[sourceId] = { lastPower: currentPower, lastTime: currentTime };
                                          return;
                                      }
                                  
                                      const timeDiffSeconds = (currentTime - lastState.lastTime) / 1000;
                                  
                                      if (timeDiffSeconds <= 0) {
                                          return; // Keine Zeit vergangen oder ungültige Messung
                                      }
                                  
                                      // Berechnung der Energie: E [Wh] = P [W] * t [s] / 3600
                                      // WICHTIG: Hier verwenden wir den aktuellen Power-Wert für eine einfache Trapez-Annäherung
                                      // Die meisten Skripte verwenden nur den aktuellen Wert. Um die Komplexität des ursprünglichen Codes beizubehalten, 
                                      // verwenden wir den aktuellen Wert * deltaT, obwohl oft (lastPower + currentPower) / 2 * deltaT genauer ist.
                                      const energyWs = currentPower * timeDiffSeconds; 
                                      const energyWh = energyWs / 3600;
                                  
                                      // Aktuellen summierten Tageswert lesen (Wh)
                                  let currentWhValue = 0;
                                      
                                      switch (sourceId) {
                                          case PowerSourceIds.GRID_POWER:
                                              // Einspeisung (Negativ) vs. Netzbezug (Positiv)
                                              if (currentPower < 0) { // Einspeisung
                                                  const absEnergyWh = Math.abs(energyWh);
                                                  currentWhValue = (await getStateSafeAsync(TargetStateIds.DAILY_FEED_IN_WH)) + absEnergyWh;
                                                  setEnergyStates(TargetStateIds.DAILY_FEED_IN_WH, TargetStateIds.DAILY_FEED_IN_KWH, currentWhValue);
                                                  
                                                  // Skript 4 Duplikat aktualisieren: Verwenden Sie hier auch eine Zahl
                                                  const roundedFeedInWhS4 = Math.round(currentWhValue * 100) / 100;
                                                  setState(TargetStateIds.DAILY_FEED_IN_WH_S4, roundedFeedInWhS4, true); // **HIER: Übergabe einer Zahl**
                                                  
                                              } else { // Netzbezug
                                                  currentWhValue = (await getStateSafeAsync(TargetStateIds.DAILY_GRID_PURCHASE_WH)) + energyWh;
                                                  setEnergyStates(TargetStateIds.DAILY_GRID_PURCHASE_WH, TargetStateIds.DAILY_GRID_PURCHASE_KWH, currentWhValue);
                                                  
                                                  // Skript 4 Duplikat aktualisieren: Verwenden Sie hier auch eine Zahl
                                                  const roundedGridWhS4 = Math.round(currentWhValue * 100) / 100;
                                                  setState(TargetStateIds.DAILY_GRID_WH_S4, roundedGridWhS4, true); // **HIER: Übergabe einer Zahl**
                                              }
                                              break;
                                  
                                          case PowerSourceIds.HOUSE_CONSUMPTION_POWER:
                                              // Hausverbrauch (immer positiv)
                                              currentWhValue = (await getStateSafeAsync(TargetStateIds.DAILY_HOUSE_CONSUMPTION_WH)) + energyWh;
                                              setEnergyStates(TargetStateIds.DAILY_HOUSE_CONSUMPTION_WH, TargetStateIds.DAILY_HOUSE_CONSUMPTION_KWH, currentWhValue);
                                              break;
                                              
                                          case PowerSourceIds.BAT_POWER:
                                              // Batterie: Laden (Positiv) vs. Entladen (Negativ)
                                              if (currentPower < 0) { // Entladen (Out)
                                                  const absEnergyWh = Math.abs(energyWh);
                                                  currentWhValue = (await getStateSafeAsync(TargetStateIds.DAILY_BAT_OUT_WH)) + absEnergyWh;
                                                  setEnergyStates(TargetStateIds.DAILY_BAT_OUT_WH, TargetStateIds.DAILY_BAT_OUT_KWH, currentWhValue);
                                              } else { // Laden (In)
                                                  currentWhValue = (await getStateSafeAsync(TargetStateIds.DAILY_BAT_IN_WH)) + energyWh;
                                                  setEnergyStates(TargetStateIds.DAILY_BAT_IN_WH, TargetStateIds.DAILY_BAT_IN_KWH, currentWhValue);
                                              }
                                              break;
                                  
                                          case PowerSourceIds.PV_POWER:
                                              // PV-Produktion (immer positiv)
                                              currentWhValue = (await getStateSafeAsync(TargetStateIds.DAILY_PV_WH)) + energyWh;
                                              setEnergyStates(TargetStateIds.DAILY_PV_WH, TargetStateIds.DAILY_PV_KWH, currentWhValue);
                                              break;
                                      }
                                  
                                      // Bilanz-KWh (Skript 4 Logik) nur berechnen, wenn einer der relevanten Werte aktualisiert wird
                                      if (sourceId === PowerSourceIds.PV_POWER || sourceId === PowerSourceIds.GRID_POWER) {
                                          const pvWh = await getStateSafeAsync(TargetStateIds.DAILY_PV_WH);
                                          const gridWh = await getStateSafeAsync(TargetStateIds.DAILY_GRID_PURCHASE_WH);
                                          const bilanzKWh = (pvWh - gridWh) / 1000;
                                  
                                          // Der Unary Plus Operator (+) konvertiert den toFixed()-String zurück in eine Zahl.
                                          setState(TargetStateIds.DAILY_BILANZ_KWH, +bilanzKWh.toFixed(3), true); 
                                      }
                                      
                                      // Werte für die nächste Iteration speichern
                                      lastState.lastPower = currentPower;
                                      lastState.lastTime = currentTime;
                                  }
                                  
                                  // -----------------------------------------------------------------------------------
                                  // 4. TRIGGGER UND SCHEDULER
                                  // -----------------------------------------------------------------------------------
                                  
                                  // 1. Initialisierung beim Skriptstart
                                  initializeScript();
                                  
                                  // 2. Trigger bei Änderung eines Leistungsdatenpunkts (Einspeisung/Bezug, Haus, Batterie, PV)
                                  on({ id: Object.values(PowerSourceIds), change: 'ne' }, async function (obj) {
                                      const sourceId = obj.id as string;
                                      const currentPower = parseFloat(obj.state.val);
                                      const currentTime = Date.now();
                                  
                                      if (isNaN(currentPower)) {
                                          log(`Ungültiger Leistungswert für ${sourceId}: ${obj.state.val}. Berechnung übersprungen.`, 'warn');
                                          return;
                                      }
                                  
                                      await processPowerUpdate(sourceId, currentPower, currentTime);
                                  });
                                  
                                  // 3. Reset der Tagessummen um Mitternacht
                                  schedule('0 0 * * *', async function () {
                                      log("Mitternacht erreicht, setze Tagessummen zurück.");
                                      
                                      // Setze alle Ziel-Datenpunkte auf 0
                                      for (const id of Object.values(TargetStateIds)) {
                                          setState(id, 0, true);
                                      }
                                  
                                      // Reset der Last-States für korrekte Berechnung am neuen Tag
                                      const allSourceIds = Object.values(PowerSourceIds);
                                      const currentTime = Date.now();
                                  
                                      for (const id of allSourceIds) {
                                          // Aktuellen Power-Wert erneut holen, um als 'lastPower' für den neuen Tag zu dienen
                                          const currentPower = await getStateSafeAsync(id);
                                          lastStates[id] = { lastPower: currentPower, lastTime: currentTime };
                                      }
                                  
                                      log("Alle Tagessummen und Zähler wurden zurückgesetzt.");
                                  });
                                  
                                  
                                  
                                  M 1 Antwort Letzte Antwort
                                  0
                                  • icebearI icebear

                                    @musicnrw

                                    Ich benutze diese Scripte nicht mehr, weil ich die in einem Script zusammengefasst habe.

                                    Hier das Script welches ich jetzt verwende, du mußt dort halt die Datenpunkte evtl. auf deine Gegebenheiten anpassen. Beim ersten Start sollten vom Script alle erforderlichen Datenpunkte, wenn sie nicht vorhanden sind, erstellt werden.

                                    Wichtig!!! Das ist ein TypeScript. Also beim erstellen nicht JS nehmen sondern ganz rechts TS

                                    Kannst ja mal schauen.

                                    // -----------------------------------------------------------------------------------
                                    // 1. KONFIGURATION
                                    // -----------------------------------------------------------------------------------
                                    
                                    // Basis-Pfad für eigene Datenpunkte (muss in ioBroker unter 0_userdata.0.Energie.Senec. konsolidiert werden)
                                    const BASE_DP_PATH = '0_userdata.0.Energie.Senec';
                                    
                                    // Senec Quell-Datenpunkte (Leistung in Watt)
                                    // WICHTIG: Prüfen, ob die IDs in Ihrer Installation korrekt sind!
                                    const PowerSourceIds = {
                                        // Skript 1: Negativ = Einspeisung, Positiv = Netzbezug
                                        GRID_POWER: "senec.0.ENERGY.GUI_GRID_POW",
                                        // Skript 2: Hausverbrauch (immer positiv)
                                        HOUSE_CONSUMPTION_POWER: "senec.0.ENERGY.GUI_HOUSE_POW",
                                        // Skript 3: Positiv = Laden (In), Negativ = Entladen (Out)
                                        BAT_POWER: "senec.0.ENERGY.GUI_BAT_DATA_POWER",
                                        // Skript 4: PV-Produktion (immer positiv)
                                        PV_POWER: "senec.0.ENERGY.GUI_INVERTER_POWER",
                                    };
                                    
                                    // Ziel-Datenpunkte (Tagesenergie in Wh und kWh)
                                    const TargetStateIds = {
                                        // Skript 1
                                        DAILY_FEED_IN_WH: `${BASE_DP_PATH}.Tages_Einspeisung_Wh`,
                                        DAILY_FEED_IN_KWH: `${BASE_DP_PATH}.Tages_Einspeisung_kWh`,
                                        DAILY_GRID_PURCHASE_WH: `${BASE_DP_PATH}.Tages_Netzbezug_Wh`,
                                        DAILY_GRID_PURCHASE_KWH: `${BASE_DP_PATH}.Tages_Netzbezug_kWh`,
                                    
                                        // Skript 2
                                        DAILY_HOUSE_CONSUMPTION_WH: `${BASE_DP_PATH}.Tages_Hausverbrauch_Wh`,
                                        DAILY_HOUSE_CONSUMPTION_KWH: `${BASE_DP_PATH}.Tages_Hausverbrauch_kWh`,
                                    
                                        // Skript 3 (Bat_Out = Entladen, Bat_In = Laden)
                                        DAILY_BAT_OUT_WH: `${BASE_DP_PATH}.Bat_Out_Wh`,
                                        DAILY_BAT_OUT_KWH: `${BASE_DP_PATH}.Bat_Out_kWh`,
                                        DAILY_BAT_IN_WH: `${BASE_DP_PATH}.Bat_In_Wh`,
                                        DAILY_BAT_IN_KWH: `${BASE_DP_PATH}.Bat_In_kWh`,
                                    
                                        // Skript 4 (Zusätzliche PV- und Bilanz-Werte)
                                        DAILY_PV_WH: `${BASE_DP_PATH}.Tageswerte.pv_wh`,
                                        DAILY_PV_KWH: `${BASE_DP_PATH}.Tageswerte.pv_kwh`,
                                        DAILY_BILANZ_KWH: `${BASE_DP_PATH}.Tageswerte.bilanz_kwh`,
                                        DAILY_GRID_WH_S4: `${BASE_DP_PATH}.Tageswerte.netz_wh`, // Duplikat von DAILY_GRID_PURCHASE_WH
                                        DAILY_FEED_IN_WH_S4: `${BASE_DP_PATH}.Tageswerte.einspeisung_wh`, // Duplikat von DAILY_FEED_IN_WH
                                    };
                                    
                                    // -----------------------------------------------------------------------------------
                                    // 2. TYPEN, GLOBALE VARIABLEN UND HILFSFUNKTIONEN
                                    // -----------------------------------------------------------------------------------
                                    
                                    // Typsichere Definition der globalen Variablen für die Integration
                                    // Der Schlüssel ist die PowerSourceId, der Wert speichert den letzten Zustand.
                                    interface LastState {
                                        lastPower: number;
                                        lastTime: number; // Zeitstempel in Millisekunden
                                    }
                                    
                                    // Speichert den letzten Zustand für jede Quelle separat
                                    const lastStates: { [key: string]: LastState } = {};
                                    
                                    // Typsichere Definition für die Erstellung der States
                                    interface CommonStateOptions {
                                        name: string;
                                        type: "number";
                                        read: boolean;
                                        write: boolean;
                                        role: string;
                                        unit: "Wh" | "kWh";
                                        desc?: string;
                                    }
                                    
                                    // Definiert alle zu erstellenden Datenpunkte
                                    const stateDefinitions: { id: string; initialValue: number; common: CommonStateOptions }[] = [
                                        // Skript 1
                                        { id: TargetStateIds.DAILY_FEED_IN_WH, initialValue: 0, common: { name: 'Tägliche Einspeisung (Wh)', type: 'number', read: true, write: false, role: 'value.power.consumption', unit: 'Wh' } },
                                        { id: TargetStateIds.DAILY_FEED_IN_KWH, initialValue: 0, common: { name: 'Tägliche Einspeisung (kWh)', type: 'number', read: true, write: false, role: 'value.power.consumption', unit: 'kWh' } },
                                        { id: TargetStateIds.DAILY_GRID_PURCHASE_WH, initialValue: 0, common: { name: 'Täglicher Netzbezug (Wh)', type: 'number', read: true, write: false, role: 'value.power.consumption', unit: 'Wh' } },
                                        { id: TargetStateIds.DAILY_GRID_PURCHASE_KWH, initialValue: 0, common: { name: 'Täglicher Netzbezug (kWh)', type: 'number', read: true, write: false, role: 'value.power.consumption', unit: 'kWh' } },
                                    
                                        // Skript 2
                                        { id: TargetStateIds.DAILY_HOUSE_CONSUMPTION_WH, initialValue: 0, common: { name: 'Täglicher Hausverbrauch (Wh)', type: 'number', read: true, write: false, role: 'value.power.consumption', unit: 'Wh', desc: 'Summierter Hausverbrauch für den aktuellen Tag in Wattstunden' } },
                                        { id: TargetStateIds.DAILY_HOUSE_CONSUMPTION_KWH, initialValue: 0, common: { name: 'Täglicher Hausverbrauch (kWh)', type: 'number', read: true, write: false, role: 'value.power.consumption', unit: 'kWh', desc: 'Summierter Hausverbrauch für den aktuellen Tag in Kilowattstunden' } },
                                    
                                        // Skript 3
                                        { id: TargetStateIds.DAILY_BAT_OUT_WH, initialValue: 0, common: { name: 'Daily BatOut (Wh)', type: 'number', read: true, write: false, role: 'value.power.consumption', unit: 'Wh' } },
                                        { id: TargetStateIds.DAILY_BAT_OUT_KWH, initialValue: 0, common: { name: 'Daily BatOut (kWh)', type: 'number', read: true, write: false, role: 'value.power.consumption', unit: 'kWh' } },
                                        { id: TargetStateIds.DAILY_BAT_IN_WH, initialValue: 0, common: { name: 'Daily BatIn (Wh)', type: 'number', read: true, write: false, role: 'value.power.consumption', unit: 'Wh' } },
                                        { id: TargetStateIds.DAILY_BAT_IN_KWH, initialValue: 0, common: { name: 'Daily BatIn (kWh)', type: 'number', read: true, write: false, role: 'value.power.consumption', unit: 'kWh' } },
                                        
                                        // Skript 4
                                        { id: TargetStateIds.DAILY_PV_WH, initialValue: 0, common: { name: 'Tageswert PV in Wh', type: 'number', read: true, write: false, role: 'value.power', unit: 'Wh' } },
                                        { id: TargetStateIds.DAILY_PV_KWH, initialValue: 0, common: { name: 'Tageswert PV in kWh', type: 'number', read: true, write: false, role: 'value.power', unit: 'kWh' } },
                                        { id: TargetStateIds.DAILY_BILANZ_KWH, initialValue: 0, common: { name: 'Bilanz kWh (PV - Netzbezug)', type: 'number', read: true, write: false, role: 'value.power', unit: 'kWh' } },
                                        // Die folgenden sind Duplikate aus Skript 4, die wir beibehalten, um Skript 4 vollständig abzubilden
                                        { id: TargetStateIds.DAILY_GRID_WH_S4, initialValue: 0, common: { name: 'Tages-Wh Netzbezug (S4)', type: 'number', read: true, write: false, role: 'value.power', unit: 'Wh' } },
                                        { id: TargetStateIds.DAILY_FEED_IN_WH_S4, initialValue: 0, common: { name: 'Tages-Wh Einspeisung (S4)', type: 'number', read: true, write: false, role: 'value.power', unit: 'Wh' } }
                                    ];
                                    
                                    /**
                                     * Erstellt einen Datenpunkt, falls er noch nicht existiert.
                                     * @param id ID des zu erstellenden States.
                                     * @param initialValue Initialer Wert.
                                     * @param common Common-Objekt Definition.
                                     */
                                    function createStateIfNotExists(id: string, initialValue: number, common: CommonStateOptions): void {
                                        if (!existsState(id)) {
                                            createState(id, initialValue, common, (err) => {
                                                if (err) {
                                                    log(`Fehler beim Erstellen des Datenpunkts ${id}: ${err}`, 'error');
                                                } else {
                                                    log(`Datenpunkt ${id} erfolgreich erstellt.`, 'info');
                                                }
                                            });
                                        }
                                    }
                                    
                                    /**
                                     * Liest den Wert eines States sicher und gibt 0 zurück, falls ungültig.
                                     * @param id ID des States.
                                     * @returns Der Wert als Zahl oder 0.
                                     */
                                    async function getStateSafeAsync(id: string): Promise<number> {
                                        const state = await getStateAsync(id);
                                        if (state && typeof state.val === 'number') {
                                            return state.val;
                                        }
                                        // Float-Parsing für den Fall, dass der Wert als String gespeichert wurde (trotz Type 'number')
                                        if (state && state.val !== null && state.val !== undefined) {
                                            const parsed = parseFloat(state.val.toString());
                                            return isNaN(parsed) ? 0 : parsed;
                                        }
                                        return 0;
                                    }
                                    
                                    /**
                                     * Setzt einen Wert in Wh und berechnet und setzt automatisch den zugehörigen kWh-Wert.
                                     * @param whId ID des Wh-Datenpunkts.
                                     * @param kwhId ID des kWh-Datenpunkts.
                                     * @param whValue Wert in Wh.
                                     */
                                    function setEnergyStates(whId: string, kwhId: string, whValue: number): void {
                                        // Wh als echte Zahl speichern (mit 2 Nachkommastellen runden, um Rundungsfehler zu minimieren, aber als NUMBER)
                                        // Wir verwenden Math.round, um Rundungsfehler zu kontrollieren, bevor wir die Zahl speichern.
                                        const roundedWh = Math.round(whValue * 100) / 100; 
                                        setState(whId, roundedWh, true); // **HIER: Übergabe einer Zahl (roundedWh)**
                                    
                                        // kWh auf 3 Nachkommastellen formatieren (als String)
                                        const kwhString = (roundedWh / 1000).toFixed(3);
                                        setState(kwhId, +kwhString, true); // **KORREKTUR: Verwende +kwhString**
                                    }
                                    
                                    // -----------------------------------------------------------------------------------
                                    // 3. INITIALISIERUNG UND HAUPTLOGIK (INTEGRATION)
                                    // -----------------------------------------------------------------------------------
                                    
                                    /**
                                     * Erstellt alle benötigten Datenpunkte und initialisiert die globalen Last-State-Variablen.
                                     */
                                    async function initializeScript(): Promise<void> {
                                        log("Starte Initialisierung des Energie-Bilanz-Skripts.");
                                    
                                        // Erstellen des Basisordners, falls nicht vorhanden (für Skript 4 relevant)
                                        if (!existsObject(`${BASE_DP_PATH}.Tageswerte`)) {
                                            createState(`${BASE_DP_PATH}.Tageswerte`, 'Ordner für zusätzliche Energie-Tagesdatenpunkte', (err) => {
                                                if (err) log(`Fehler beim Erstellen des Ordners: ${err}`, 'error');
                                                else log(`Ordner ${BASE_DP_PATH}.Tageswerte erfolgreich erstellt`, 'info');
                                            });
                                        }
                                    
                                        // Erstellen/Prüfen aller Datenpunkte
                                        for (const definition of stateDefinitions) {
                                            createStateIfNotExists(definition.id, definition.initialValue, definition.common);
                                        }
                                    
                                        // Initialisierung der Last-States für jede Quelle
                                        const allSourceIds = Object.values(PowerSourceIds);
                                        const currentTime = Date.now();
                                    
                                        for (const id of allSourceIds) {
                                            // Versucht, den aktuellen Wert des Power-States zu lesen
                                            const powerState = await getStateSafeAsync(id);
                                            
                                            lastStates[id] = {
                                                lastPower: powerState,
                                                lastTime: currentTime
                                            };
                                            log(`Initialisiere ${id} mit Leistung: ${powerState} W und Zeit: ${currentTime}.`);
                                        }
                                    
                                        log("Initialisierung abgeschlossen.");
                                    }
                                    
                                    /**
                                     * Generische Funktion zur Berechnung der Delta-Energie und Aktualisierung der Ziel-States.
                                     * @param sourceId Die ID des Power-Quell-States (z.B. GRID_POWER).
                                     * @param currentPower Der aktuelle Leistungswert.
                                     * @param currentTime Der aktuelle Zeitstempel.
                                     */
                                    async function processPowerUpdate(sourceId: string, currentPower: number, currentTime: number): Promise<void> {
                                        const lastState = lastStates[sourceId];
                                    
                                        // Sicherstellen der Initialisierung
                                        if (!lastState || lastState.lastTime === currentTime) {
                                            // Sollte nicht passieren, da initializeScript aufgerufen wird, aber als Fallback
                                            lastStates[sourceId] = { lastPower: currentPower, lastTime: currentTime };
                                            return;
                                        }
                                    
                                        const timeDiffSeconds = (currentTime - lastState.lastTime) / 1000;
                                    
                                        if (timeDiffSeconds <= 0) {
                                            return; // Keine Zeit vergangen oder ungültige Messung
                                        }
                                    
                                        // Berechnung der Energie: E [Wh] = P [W] * t [s] / 3600
                                        // WICHTIG: Hier verwenden wir den aktuellen Power-Wert für eine einfache Trapez-Annäherung
                                        // Die meisten Skripte verwenden nur den aktuellen Wert. Um die Komplexität des ursprünglichen Codes beizubehalten, 
                                        // verwenden wir den aktuellen Wert * deltaT, obwohl oft (lastPower + currentPower) / 2 * deltaT genauer ist.
                                        const energyWs = currentPower * timeDiffSeconds; 
                                        const energyWh = energyWs / 3600;
                                    
                                        // Aktuellen summierten Tageswert lesen (Wh)
                                    let currentWhValue = 0;
                                        
                                        switch (sourceId) {
                                            case PowerSourceIds.GRID_POWER:
                                                // Einspeisung (Negativ) vs. Netzbezug (Positiv)
                                                if (currentPower < 0) { // Einspeisung
                                                    const absEnergyWh = Math.abs(energyWh);
                                                    currentWhValue = (await getStateSafeAsync(TargetStateIds.DAILY_FEED_IN_WH)) + absEnergyWh;
                                                    setEnergyStates(TargetStateIds.DAILY_FEED_IN_WH, TargetStateIds.DAILY_FEED_IN_KWH, currentWhValue);
                                                    
                                                    // Skript 4 Duplikat aktualisieren: Verwenden Sie hier auch eine Zahl
                                                    const roundedFeedInWhS4 = Math.round(currentWhValue * 100) / 100;
                                                    setState(TargetStateIds.DAILY_FEED_IN_WH_S4, roundedFeedInWhS4, true); // **HIER: Übergabe einer Zahl**
                                                    
                                                } else { // Netzbezug
                                                    currentWhValue = (await getStateSafeAsync(TargetStateIds.DAILY_GRID_PURCHASE_WH)) + energyWh;
                                                    setEnergyStates(TargetStateIds.DAILY_GRID_PURCHASE_WH, TargetStateIds.DAILY_GRID_PURCHASE_KWH, currentWhValue);
                                                    
                                                    // Skript 4 Duplikat aktualisieren: Verwenden Sie hier auch eine Zahl
                                                    const roundedGridWhS4 = Math.round(currentWhValue * 100) / 100;
                                                    setState(TargetStateIds.DAILY_GRID_WH_S4, roundedGridWhS4, true); // **HIER: Übergabe einer Zahl**
                                                }
                                                break;
                                    
                                            case PowerSourceIds.HOUSE_CONSUMPTION_POWER:
                                                // Hausverbrauch (immer positiv)
                                                currentWhValue = (await getStateSafeAsync(TargetStateIds.DAILY_HOUSE_CONSUMPTION_WH)) + energyWh;
                                                setEnergyStates(TargetStateIds.DAILY_HOUSE_CONSUMPTION_WH, TargetStateIds.DAILY_HOUSE_CONSUMPTION_KWH, currentWhValue);
                                                break;
                                                
                                            case PowerSourceIds.BAT_POWER:
                                                // Batterie: Laden (Positiv) vs. Entladen (Negativ)
                                                if (currentPower < 0) { // Entladen (Out)
                                                    const absEnergyWh = Math.abs(energyWh);
                                                    currentWhValue = (await getStateSafeAsync(TargetStateIds.DAILY_BAT_OUT_WH)) + absEnergyWh;
                                                    setEnergyStates(TargetStateIds.DAILY_BAT_OUT_WH, TargetStateIds.DAILY_BAT_OUT_KWH, currentWhValue);
                                                } else { // Laden (In)
                                                    currentWhValue = (await getStateSafeAsync(TargetStateIds.DAILY_BAT_IN_WH)) + energyWh;
                                                    setEnergyStates(TargetStateIds.DAILY_BAT_IN_WH, TargetStateIds.DAILY_BAT_IN_KWH, currentWhValue);
                                                }
                                                break;
                                    
                                            case PowerSourceIds.PV_POWER:
                                                // PV-Produktion (immer positiv)
                                                currentWhValue = (await getStateSafeAsync(TargetStateIds.DAILY_PV_WH)) + energyWh;
                                                setEnergyStates(TargetStateIds.DAILY_PV_WH, TargetStateIds.DAILY_PV_KWH, currentWhValue);
                                                break;
                                        }
                                    
                                        // Bilanz-KWh (Skript 4 Logik) nur berechnen, wenn einer der relevanten Werte aktualisiert wird
                                        if (sourceId === PowerSourceIds.PV_POWER || sourceId === PowerSourceIds.GRID_POWER) {
                                            const pvWh = await getStateSafeAsync(TargetStateIds.DAILY_PV_WH);
                                            const gridWh = await getStateSafeAsync(TargetStateIds.DAILY_GRID_PURCHASE_WH);
                                            const bilanzKWh = (pvWh - gridWh) / 1000;
                                    
                                            // Der Unary Plus Operator (+) konvertiert den toFixed()-String zurück in eine Zahl.
                                            setState(TargetStateIds.DAILY_BILANZ_KWH, +bilanzKWh.toFixed(3), true); 
                                        }
                                        
                                        // Werte für die nächste Iteration speichern
                                        lastState.lastPower = currentPower;
                                        lastState.lastTime = currentTime;
                                    }
                                    
                                    // -----------------------------------------------------------------------------------
                                    // 4. TRIGGGER UND SCHEDULER
                                    // -----------------------------------------------------------------------------------
                                    
                                    // 1. Initialisierung beim Skriptstart
                                    initializeScript();
                                    
                                    // 2. Trigger bei Änderung eines Leistungsdatenpunkts (Einspeisung/Bezug, Haus, Batterie, PV)
                                    on({ id: Object.values(PowerSourceIds), change: 'ne' }, async function (obj) {
                                        const sourceId = obj.id as string;
                                        const currentPower = parseFloat(obj.state.val);
                                        const currentTime = Date.now();
                                    
                                        if (isNaN(currentPower)) {
                                            log(`Ungültiger Leistungswert für ${sourceId}: ${obj.state.val}. Berechnung übersprungen.`, 'warn');
                                            return;
                                        }
                                    
                                        await processPowerUpdate(sourceId, currentPower, currentTime);
                                    });
                                    
                                    // 3. Reset der Tagessummen um Mitternacht
                                    schedule('0 0 * * *', async function () {
                                        log("Mitternacht erreicht, setze Tagessummen zurück.");
                                        
                                        // Setze alle Ziel-Datenpunkte auf 0
                                        for (const id of Object.values(TargetStateIds)) {
                                            setState(id, 0, true);
                                        }
                                    
                                        // Reset der Last-States für korrekte Berechnung am neuen Tag
                                        const allSourceIds = Object.values(PowerSourceIds);
                                        const currentTime = Date.now();
                                    
                                        for (const id of allSourceIds) {
                                            // Aktuellen Power-Wert erneut holen, um als 'lastPower' für den neuen Tag zu dienen
                                            const currentPower = await getStateSafeAsync(id);
                                            lastStates[id] = { lastPower: currentPower, lastTime: currentTime };
                                        }
                                    
                                        log("Alle Tagessummen und Zähler wurden zurückgesetzt.");
                                    });
                                    
                                    
                                    
                                    M Offline
                                    M Offline
                                    musicnrw
                                    schrieb am zuletzt editiert von
                                    #382

                                    @icebear, vielen Dank für Deine schnelle Antwort.
                                    Ich denke, ich kann das neue Gesamtscript, ohne an den alten was zu ändern oder diese zu deaktivieren, parallel installieren und die damit erzeugten Datenpunkte zumindest erstmal testweise für Visulaisierungen mit Grafana verwenden?

                                    icebearI 1 Antwort Letzte Antwort
                                    0
                                    • M musicnrw

                                      @icebear, vielen Dank für Deine schnelle Antwort.
                                      Ich denke, ich kann das neue Gesamtscript, ohne an den alten was zu ändern oder diese zu deaktivieren, parallel installieren und die damit erzeugten Datenpunkte zumindest erstmal testweise für Visulaisierungen mit Grafana verwenden?

                                      icebearI Online
                                      icebearI Online
                                      icebear
                                      schrieb am zuletzt editiert von
                                      #383

                                      @musicnrw

                                      Das wäre ein guter Plan, und wenn alles läuft, dann einfach die alten deaktivieren.

                                      M 1 Antwort Letzte Antwort
                                      0
                                      • icebearI icebear

                                        @musicnrw

                                        Das wäre ein guter Plan, und wenn alles läuft, dann einfach die alten deaktivieren.

                                        M Offline
                                        M Offline
                                        musicnrw
                                        schrieb am zuletzt editiert von
                                        #384

                                        @icebear, Du schreibst ja "...du mußt dort halt die Datenpunkte evtl. auf deine Gegebenheiten anpassen....". Meinst Du mit diesen Datenpunkten die, deren Werte ich später in Grafana zur Visualisierung heranziehe?
                                        Und wie kann ich es erreichen, dass ich für Auswertungen (z.B. PV-Ertrag letzte Woche, letzter Monat, letztes Jahr) sowohl auf die Daten zurückgreifen kann, die mit den alten Scripts generiert und in die InfluxDB geschrieben wurden, wie auch auf die Daten, die ab Tag X mit Deinem neuen Script in die InfluxDB wandern? Reicht dazu die identische Benennung der Datenpunkte bzw. ist es überhaupt möglich, dass Daten vom alten und neuen Script in gleich benannte Datenpunkte geschrieben werden (Konsistenz?)?

                                        icebearI 1 Antwort Letzte Antwort
                                        0
                                        • M musicnrw

                                          @icebear, Du schreibst ja "...du mußt dort halt die Datenpunkte evtl. auf deine Gegebenheiten anpassen....". Meinst Du mit diesen Datenpunkten die, deren Werte ich später in Grafana zur Visualisierung heranziehe?
                                          Und wie kann ich es erreichen, dass ich für Auswertungen (z.B. PV-Ertrag letzte Woche, letzter Monat, letztes Jahr) sowohl auf die Daten zurückgreifen kann, die mit den alten Scripts generiert und in die InfluxDB geschrieben wurden, wie auch auf die Daten, die ab Tag X mit Deinem neuen Script in die InfluxDB wandern? Reicht dazu die identische Benennung der Datenpunkte bzw. ist es überhaupt möglich, dass Daten vom alten und neuen Script in gleich benannte Datenpunkte geschrieben werden (Konsistenz?)?

                                          icebearI Online
                                          icebearI Online
                                          icebear
                                          schrieb am zuletzt editiert von
                                          #385

                                          @musicnrw

                                          Du kannst doch die Tageswerte in der InfluxDB speichern und dann in der Abfrage mit der 'aggregation' (day, week, month) festlegen wie diese in Grafana dargestellt werden. oder du machst es so wie es @haus-automatisierung in diesem Video mal erklärt hat.

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


                                          Support us

                                          ioBroker
                                          Community Adapters
                                          Donate

                                          657

                                          Online

                                          32.4k

                                          Benutzer

                                          81.4k

                                          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