Files
EOS/docs/akkudoktoreos/nodered_pv_measurement_function.js
T

69 lines
2.7 KiB
JavaScript
Raw Normal View History

// Node-RED function: cumulative PV production meter readings -> EOS measurement API.
//
// Strang:
// inject (repeat 3600 s, msg.topic = die SQL aus dem Tutorial)
// -> mysql "MariaDB" (DB `sensor`)
// -> DIESE function
// -> http request (Method: "- set by msg.method -")
//
// EOS speichert unter `pv_production_emr_keys` Zaehlerstaende (EMR) in kWh und
// bildet die Differenzen selbst. Aus den Momentanleistungen in `data.solarallpower`
// muss also erst ein monoton steigender Zaehler werden - das macht die SQL.
//
// Wichtig: das Startdatum in der SQL bleibt FEST. Ein rollendes
// `NOW() - INTERVAL n DAY` verschiebt den Nullpunkt der kumulativen Summe bei
// jedem Lauf, und EOS liest den Sprung als Produktion.
const BASE_URL = "http://192.168.1.151:8503";
const KEY = "pv_produktion_emr";
const TZ = "Europe/Berlin";
function toIsoWithOffset(value) {
// DATE_FORMAT() kommt als String in lokaler Zeit zurueck. Den Offset aus dem
// Datum selbst bilden, damit CEST und CET beide stimmen.
const date = value instanceof Date ? value : new Date(String(value).replace(" ", "T"));
const pad = n => String(Math.trunc(Math.abs(n))).padStart(2, "0");
const offsetMin = -date.getTimezoneOffset();
const sign = offsetMin >= 0 ? "+" : "-";
return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}` +
`T${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}` +
`${sign}${pad(offsetMin / 60)}:${pad(offsetMin % 60)}`;
}
const rows = Array.isArray(msg.payload) ? msg.payload : [];
const data = {};
let last = null;
let skipped = 0;
for (const row of rows) {
const value = Number(row.emr);
if (!Number.isFinite(value)) {
skipped += 1;
continue;
}
// Ein Zaehler laeuft nur vorwaerts. Ein Rueckschritt bedeutet eine Luecke oder
// einen kaputten Messwert - EOS wuerde daraus eine negative Produktionsstunde
// machen.
if (last !== null && value < last) {
skipped += 1;
continue;
}
last = value;
data[toIsoWithOffset(row.ts)] = Number(value.toFixed(6));
}
const count = Object.keys(data).length;
if (count === 0) {
node.warn("Keine PV-Messwerte gefunden - topic und Zeitfenster in der SQL pruefen.");
return null;
}
if (skipped > 0) {
node.warn(`${skipped} Zeilen uebersprungen (nicht-numerisch oder Zaehler rueckwaerts).`);
}
node.status({ text: `${count} EMR-Werte, letzter ${last.toFixed(1)} kWh` });
msg.method = "PUT";
msg.url = `${BASE_URL}/v1/measurement/series?key=${encodeURIComponent(KEY)}`;
msg.headers = { "Content-Type": "application/json" };
msg.payload = { data: data, dtype: "float64", tz: TZ };
return msg;