@ralf-2 said in PV Überwachung hoymiles:
@martinschm
Wäre zwar schön aber bestimmt nicht zwingend nötig.
Ich würde einfach die Erträge der einzelnen Module Module für einen Tag aufsummieren und dann bei Abweichung von x Prozent eine Meldung geben.
Bedenke dabei, das vielleicht auch etwas ein Modul mal verschatten kann (Handwerkeraute, oder so etwas). Ich würde das einfach mal eine Woche ohne Alarm laufen lassen und dann den Grenzwert in Prozent daraus ableiten.
Das bekommen wir schon als Teamarbeit hier im Forum hin. Daten solltest du mal im Vorfeld sammeln.
Ich hab mal mit perplexity.ai ein paar Runden gedreht und nutze aktuell dieses Skript
const wrThreshold = 0.7;
const moduleThreshold = 0.7;
const minProduction = 5;
const telegramInstance = 'telegram.0';
const warnDelay = 15 * 60 * 1000; // 15 Minuten in ms
// Gruppen-Konfiguration wie gehabt
const groups = [
{
name: "Zaun",
wrs: [
{ id: "xx", name: "WR1", moduleCount: 6 },
{ id: "xx", name: "WR2", moduleCount: 6 },
{ id: "xx", name: "WR3", moduleCount: 6 }
]
},
{
name: "Gartenhütte",
wrs: [
{ id: "xx", name: "WR4", moduleCount: 4 },
{ id: "xx", name: "WR5", moduleCount: 4 }
]
}
];
// Statusspeicher
if (!globalThis.wrStatus) globalThis.wrStatus = {};
function checkWRs() {
let now = Date.now();
groups.forEach(group => {
let wrPowers = [];
let activeWRs = [];
// 1. Prüfe, welche WR produzieren
group.wrs.forEach((wr, i) => {
const producingState = getState(`opendtu.0.${wr.id}.producing`).val;
const isProducing = producingState === true || producingState === 'true';
if (isProducing) {
let wrPower = getState(`opendtu.0.${wr.id}.power_dc`).val;
wrPowers[i] = wrPower;
activeWRs.push({ index: i, ...wr, wrPower });
} else {
wrPowers[i] = 0;
}
});
// 2. WR-Vergleich (nur aktive WRs und erst nach 15 Minuten)
if (activeWRs.length > 1) {
let avgWR = activeWRs.reduce((sum, wr) => sum + wr.wrPower, 0) / activeWRs.length;
activeWRs.forEach(wr => {
let status = globalThis.wrStatus[wr.name] || { warnSince: null, warned: false };
if (wr.wrPower < avgWR * wrThreshold) {
if (!status.warnSince) status.warnSince = now;
if (!status.warned && (now - status.warnSince) >= warnDelay) {
sendTo(telegramInstance, 'send', {
text: `⚠️ *WR-Warnung (${group.name})*: ${wr.name} liefert seit 15 Minuten nur ${wr.wrPower} W (Durchschnitt: ${avgWR.toFixed(1)} W)`
});
status.warned = true;
}
} else {
if (status.warned) {
sendTo(telegramInstance, 'send', {
text: `✅ *Entwarnung (${group.name})*: ${wr.name} liefert wieder ausreichend Leistung.`
});
}
status.warnSince = null;
status.warned = false;
}
globalThis.wrStatus[wr.name] = status;
});
}
// 3. Modulvergleich je WR (nur bei aktiven WRs, erst nach 15 Minuten)
activeWRs.forEach(wr => {
let modulePowers = [];
for (let m = 1; m <= wr.moduleCount; m++) {
let modPower = getState(`opendtu.0.${wr.id}.dc.input_${m}.power`).val;
modulePowers[m - 1] = modPower;
}
let avgModule = modulePowers.reduce((a, b) => a + b, 0) / wr.moduleCount;
modulePowers.forEach((mp, idx) => {
let key = `${wr.name}_modul${idx+1}`;
let status = globalThis.wrStatus[key] || { warnSince: null, warned: false };
if (mp < avgModule * moduleThreshold) {
if (!status.warnSince) status.warnSince = now;
if (!status.warned && (now - status.warnSince) >= warnDelay) {
sendTo(telegramInstance, 'send', {
text: `⚠️ *Modul-Warnung (${wr.name})*: Modul ${idx + 1} liefert seit 15 Minuten nur ${mp} W (Durchschnitt: ${avgModule.toFixed(1)} W)`
});
status.warned = true;
}
} else {
if (status.warned) {
sendTo(telegramInstance, 'send', {
text: `✅ *Entwarnung (${wr.name})*: Modul ${idx + 1} liefert wieder ausreichend Leistung.`
});
}
status.warnSince = null;
status.warned = false;
}
globalThis.wrStatus[key] = status;
});
});
// 4. Überwachung nicht-produzierender WRs (wie gehabt, ggf. auch mit 15min-Verzögerung anpassen)
group.wrs.forEach((wr, i) => {
const producingState = getState(`opendtu.0.${wr.id}.producing`).val;
const isProducing = producingState === true || producingState === 'true';
let status = globalThis.wrStatus[wr.name + "_prod"] || { warnSince: null, warned: false };
if (!isProducing) {
let anyOtherProducing = group.wrs.some((otherWr, otherIdx) => {
if (otherIdx !== i) {
const otherProducingState = getState(`opendtu.0.${otherWr.id}.producing`).val;
return otherProducingState === true || otherProducingState === 'true';
}
return false;
});
if (anyOtherProducing) {
if (!status.warnSince) status.warnSince = now;
if (!status.warned && (now - status.warnSince) >= warnDelay) {
sendTo(telegramInstance, 'send', {
text: `🔴 *Ausfall (${group.name})*: ${wr.name} produziert seit 15 Minuten nichts, obwohl andere laufen!`
});
status.warned = true;
}
} else {
status.warnSince = null;
status.warned = false;
}
} else {
if (status.warned) {
sendTo(telegramInstance, 'send', {
text: `✅ *Entwarnung (${group.name})*: ${wr.name} produziert wieder!`
});
}
status.warnSince = null;
status.warned = false;
}
globalThis.wrStatus[wr.name + "_prod"] = status;
});
});
}
schedule('*/2 * * * *', checkWRs);
Das ist die zweite Version, die erste hat zu viele Meldungen in den Morgen- und Abendstunden gebracht wenn einzelne Module kurz verschattet sind.
Das Skript vergleicht die Module eines WRs miteinander und warnt wenn ein Module nach 15 min immer noch hinter den anderen herhinkt. Außerdem vergleicht es die WRs innerhalb von zwei Gruppen (wo die Module jeweils gleich ausgerichtet sind) miteinander.