/**
* Skript: PV-Tagesproduktion aus Senec-Datenpunkten
* Erstellt und summiert Wh- und kWh-Werte aus Live-Daten.
* Berücksichtigt PV-Leistung, Netzbezug/-einspeisung und Batterie-Laden/Entladen.
*
* Erstellt nötige Datenpunkte automatisch, um Messwerte persistent zu speichern.
*
* Letzte Anpassung: 23.07.2025
*/
// --- Konfiguration ---
// Basis-Pfad für eigene Datenpunkte
const BASE_DP_PATH = '0_userdata.0.energy';
// Datenpunkte vom Senec Adapter
const SENEC_PV_POWER = 'senec.0.ENERGY.GUI_INVERTER_POWER'; // Aktuelle PV-Leistung in Watt (alle 10s)
const SENEC_BAT_POWER = 'senec.0.ENERGY.GUI_BAT_DATA_POWER'; // Batterie Lade-/Entladeleistung in Watt (alle 10s), positiv = Laden, negativ = Entladen
const SENEC_GRID_POWER = 'senec.0.ENERGY.GUI_GRID_POW'; // Netzleistung in Watt (alle 10s), positiv = Netzbezug, negativ = Einspeisung
// Datenpunkte zur Speicherung
const DP_PV_WH = `${BASE_DP_PATH}.pv_wh`; // Tages-Wh PV
const DP_BAT_WH = `${BASE_DP_PATH}.bat_wh`; // Tages-Wh Batterie (Netto Laden)
const DP_GRID_WH = `${BASE_DP_PATH}.netz_wh`; // Tages-Wh Netzbezug
const DP_EINSPEISUNG_WH = `${BASE_DP_PATH}.einspeisung_wh`; // Tages-Wh Einspeisung
const DP_PV_KWH = `${BASE_DP_PATH}.pv_kwh`; // Tages-kWh PV
const DP_BILANZ_KWH = `${BASE_DP_PATH}.bilanz_kwh`; // Bilanz kWh (PV - Netzbezug)
// --- Variablen ---
let lastTimestamp = null; // Timestamp letzte Messung
let lastPVPower = 0; // Letzte gemessene PV-Leistung in Watt
let lastBatPower = 0; // Letzte gemessene Batterieleistung in Watt
let lastGridPower = 0; // Letzte gemessene Netzleistung in Watt
// --- Hilfsfunktion: Sicherer State-Lesezugriff ---
function getStateSafe(id, defaultValue = 0) {
const state = getState(id);
if (state && state.val !== null && state.val !== undefined) {
return state.val;
}
return defaultValue;
}
// --- Ordner (Channel) anlegen, falls nicht vorhanden ---
if (!existsObject(BASE_DP_PATH)) {
createChannel(BASE_DP_PATH, 'Ordner für Energie-Datenpunkte', (err) => {
if (err) {
log(`Fehler beim Erstellen des Ordners ${BASE_DP_PATH}: ${err}`, 'error');
} else {
log(`Ordner ${BASE_DP_PATH} erfolgreich erstellt`, 'info');
}
});
}
// --- Datenpunkte anlegen, falls nicht vorhanden ---
function createStateIfNotExists(id, common, initialValue = 0) {
if (!existsState(id)) {
createState(id, initialValue, common, (err) => {
if (err) {
log(`Fehler beim Erstellen des Datenpunkts ${id}: ${err}`, 'error');
} else {
log(`Datenpunkt ${id} erstellt mit Initialwert ${initialValue}`, 'info');
}
});
}
}
// Definieren der Common-Objekte (Typ, Rolle usw.)
const commonWH = { name: 'Tageswert in Wattstunden (Wh)', type: 'number', role: 'value.power', unit: 'Wh', read: true, write: false };
const commonKWH = { name: 'Tageswert in Kilowattstunden (kWh)', type: 'number', role: 'value.power', unit: 'kWh', read: true, write: false };
// Erstellen aller nötigen Datenpunkte
createStateIfNotExists(DP_PV_WH, commonWH);
createStateIfNotExists(DP_BAT_WH, commonWH);
createStateIfNotExists(DP_GRID_WH, commonWH);
createStateIfNotExists(DP_EINSPEISUNG_WH, commonWH);
createStateIfNotExists(DP_PV_KWH, commonKWH);
createStateIfNotExists(DP_BILANZ_KWH, commonKWH);
// --- Hauptlogik: Verarbeitung der Messwerte und Integration in Wh ---
// Funktion zum Verarbeiten neuer Messwerte
function processNewData(pvPower, batPower, gridPower) {
const jetzt = Date.now();
if (lastTimestamp !== null) {
const deltaT = (jetzt - lastTimestamp) / 1000; // Zeitdifferenz in Sekunden
// Energie (Wh) = Leistung (W) * Zeit (h)
// deltaWh = W * (s / 3600)
const deltaWhPV = lastPVPower * (deltaT / 3600);
const deltaWhBat = lastBatPower * (deltaT / 3600);
const deltaWhGrid = lastGridPower * (deltaT / 3600);
// Update der Tageswerte
let pvWh = getStateSafe(DP_PV_WH);
let batWh = getStateSafe(DP_BAT_WH);
let gridWh = getStateSafe(DP_GRID_WH);
let einspeisungWh = getStateSafe(DP_EINSPEISUNG_WH);
pvWh += deltaWhPV;
// Batterie: Nur Netto-Ladung (positive Leistung)
if (deltaWhBat > 0) {
batWh += deltaWhBat;
} else {
// Entladen wird nicht addiert, könnte separat behandelt werden
}
// Netzbezug vs Einspeisung trennen:
if (deltaWhGrid > 0) {
gridWh += deltaWhGrid; // Netzbezug
} else {
einspeisungWh += Math.abs(deltaWhGrid); // Einspeisung
}
// Datenpunkte aktualisieren
setState(DP_PV_WH, pvWh, true);
setState(DP_BAT_WH, batWh, true);
setState(DP_GRID_WH, gridWh, true);
setState(DP_EINSPEISUNG_WH, einspeisungWh, true);
// kWh berechnen und speichern
setState(DP_PV_KWH, +(pvWh / 1000).toFixed(3), true);
// Bilanz: PV-Produktion minus Netzbezug (kWh)
const bilanzKWh = (pvWh - gridWh) / 1000;
setState(DP_BILANZ_KWH, +bilanzKWh.toFixed(3), true);
}
// Update letzte Werte und Timestamp für nächste Berechnung
lastPVPower = pvPower;
lastBatPower = batPower;
lastGridPower = gridPower;
lastTimestamp = jetzt;
}
// --- Event-Listener: Trigger bei Änderung eines relevanten Datenpunkts ---
// Abonnieren aller 3 relevanten Datenpunkte, um schnell reagieren zu können
on({ id: [SENEC_PV_POWER, SENEC_BAT_POWER, SENEC_GRID_POWER], change: 'ne' }, () => {
const pvPower = getStateSafe(SENEC_PV_POWER);
const batPower = getStateSafe(SENEC_BAT_POWER);
const gridPower = getStateSafe(SENEC_GRID_POWER);
processNewData(pvPower, batPower, gridPower);
});
// --- Täglicher Reset der Tageswerte um Mitternacht ---
schedule('0 0 * * *', () => {
log('Tagesreset: Tageswerte auf 0 setzen.');
setState(DP_PV_WH, 0, true);
setState(DP_BAT_WH, 0, true);
setState(DP_GRID_WH, 0, true);
setState(DP_EINSPEISUNG_WH, 0, true);
setState(DP_PV_KWH, 0, true);
setState(DP_BILANZ_KWH, 0, true);
// Reset lokale Variablen ebenfalls
lastTimestamp = null;
lastPVPower = 0;
lastBatPower = 0;
lastGridPower = 0;
});
// --- Skriptstart ---
log('PV-Tagesproduktion mit Senec Datenpunkten gestartet.');