Dateien nach „wr“ hochladen

This commit is contained in:
administrator 2026-05-30 09:30:00 +00:00
parent db82353769
commit 6d133de8aa

933
wr/solarbat2-wr-unitV4.1.js Normal file
View file

@ -0,0 +1,933 @@
// ============================================================
// SolarBat WR Control
// Version: 4.1
//
// HM400 Wechselrichtersteuerung
//
// Unterstützt:
//
// - AGM / GEL
// - LiFePO4
//
// Eigenschaften:
//
// - FLOW basierte Architektur
// - zentrale UBATT Nutzung
// - Dashboard konfigurierbar
// - WR SN dynamisch
// - Emergency Mode
// - Überschusslogik
// - MQTT Spam Schutz
// - sanfte Leistungsreduktion
// - AC Netzteil Priorisierung
//
// ============================================================
//
// ARCHITEKTUR
// ------------------------------------------------------------
//
// Verwendete zentrale Variablen:
//
// flow.UBATT
// global.POWERSALDIERT
//
// Dadurch:
//
// - unabhängig von MQTT Reihenfolge
// - unabhängig von Trigger Topics
// - robust gegen Sensor Delays
// - professioneller Aufbau
//
// EMPFOHLEN:
//
// Inject Node alle 10 Sekunden
// triggert diese Function.
//
// ============================================================
//
// OUTPUT 1
// HM400 LIMIT %
//
// OUTPUT 2
// WR STATUS 0..2
//
// OUTPUT 3
// WR POWER 0/1
//
// OUTPUT 4
// CURRENT LIMIT
//
// ============================================================
// ============================================================
// BATTERY PROFILES
// ============================================================
const BATTERY_PROFILES = {
// --------------------------------------------------------
// AGM / GEL
// --------------------------------------------------------
AGM: {
TYPE: "AGM",
UBATT_ON: 12.0,
UBATT_OFF: 13.4,
UBATT_OVERVOLTAGE: 14.4,
UBATT_EMERGENCY: 11.0,
LIMIT_MIN_ACTIVE: 5,
LIMIT_MAX: 15,
LIMIT_EMERGENCY: 1,
LIMIT_STEP: 5,
REDUCE_DELAY_SEC: 90
},
// --------------------------------------------------------
// LIFEPO4
// --------------------------------------------------------
LIFEPO4: {
TYPE: "LIFEPO4",
UBATT_ON: 12.8,
UBATT_OFF: 13.6,
UBATT_OVERVOLTAGE: 14.3,
UBATT_EMERGENCY: 11.9,
LIMIT_MIN_ACTIVE: 5,
LIMIT_MAX: 35,
LIMIT_EMERGENCY: 0,
LIMIT_STEP: 5,
REDUCE_DELAY_SEC: 30
}
};
// ============================================================
// RESET
// ============================================================
if (!flow.get("WR_INIT_DONE")) {
flow.set("WR_LIMIT", 5);
flow.set("WR_POWER", 1);
flow.set("WR_LAST_REDUCE", 0);
flow.set("LAST_SENT_LIMIT", null);
flow.set("LAST_SENT_POWER", null);
flow.set("WR_INIT_DONE", true);
node.warn("WR Context reset");
}
// ============================================================
// BATTERY TYPE
// ============================================================
let batteryType =
flow.get("CFG_BATTERY_TYPE")
||
"AGM";
// ============================================================
// BATTERY PROFILE
// ============================================================
let batteryProfile =
BATTERY_PROFILES[batteryType]
||
BATTERY_PROFILES.AGM;
// ============================================================
// CONFIG
// ============================================================
const cfg = {
// --------------------------------------------------------
// WR SERIAL
// --------------------------------------------------------
WR_SN:
flow.get("CFG_WR_SN")
??
"112185029661",
// --------------------------------------------------------
// BATTERY TYPE
// --------------------------------------------------------
BATTERY_TYPE:
batteryProfile.TYPE,
// --------------------------------------------------------
// Batterie
// --------------------------------------------------------
UBATT_ON:
Number(
flow.get("CFG_UBATT_ON")
)
||
batteryProfile.UBATT_ON,
UBATT_OFF:
Number(
flow.get("CFG_UBATT_OFF")
)
||
batteryProfile.UBATT_OFF,
UBATT_OVERVOLTAGE:
Number(
flow.get("CFG_UBATT_OVERVOLTAGE")
)
||
batteryProfile.UBATT_OVERVOLTAGE,
UBATT_EMERGENCY:
Number(
flow.get("CFG_UBATT_EMERGENCY")
)
||
batteryProfile.UBATT_EMERGENCY,
// --------------------------------------------------------
// Leistung
// --------------------------------------------------------
LIMIT_MIN_ACTIVE:
Number(
flow.get("CFG_LIMIT_MIN_ACTIVE")
)
||
batteryProfile.LIMIT_MIN_ACTIVE,
LIMIT_MAX:
Number(
flow.get("CFG_LIMIT_MAX")
)
||
batteryProfile.LIMIT_MAX,
LIMIT_STEP:
Number(
flow.get("CFG_LIMIT_STEP")
)
||
batteryProfile.LIMIT_STEP,
LIMIT_EMERGENCY:
Number(
flow.get("CFG_LIMIT_EMERGENCY")
)
||
batteryProfile.LIMIT_EMERGENCY,
REDUCE_DELAY_SEC:
Number(
flow.get("CFG_REDUCE_DELAY_SEC")
)
||
batteryProfile.REDUCE_DELAY_SEC
};
// ============================================================
// UBATT
// FLOW BASIERT
// ============================================================
let ubatt =
Number(
flow.get("UBATT")
);
if (isNaN(ubatt)) {
node.warn("WR: UBATT invalid");
return null;
}
// ============================================================
// GLOBALER NETZSALDO
// ============================================================
let powersaldiert =
Number(
global.get("POWERSALDIERT")
);
if (isNaN(powersaldiert)) {
powersaldiert = 0;
}
// ============================================================
// STATES
// ============================================================
let wrPower =
Number(flow.get("WR_POWER"));
if (isNaN(wrPower)) {
wrPower = 1;
}
let currentLimit =
Number(flow.get("WR_LIMIT"));
if (isNaN(currentLimit)) {
currentLimit =
cfg.LIMIT_MIN_ACTIVE;
}
let lastReduce =
Number(flow.get("WR_LAST_REDUCE"));
if (isNaN(lastReduce)) {
lastReduce = 0;
}
let now =
Date.now();
// ============================================================
// EMERGENCY MODE
// ============================================================
let emergencyMode = false;
if (ubatt <= cfg.UBATT_EMERGENCY) {
emergencyMode = true;
}
// ============================================================
// WR POWER
// ============================================================
wrPower = 0;
// ============================================================
// ÜBERSPANNUNG
// Batterie aktiv entlasten
// ============================================================
if (ubatt >= cfg.UBATT_OVERVOLTAGE) {
currentLimit =
cfg.LIMIT_MAX;
}
// ============================================================
// NETZEINSPEISUNG
// Keine zusätzliche Einspeisung
// ============================================================
else if (powersaldiert < 0) {
currentLimit =
cfg.LIMIT_MIN_ACTIVE;
}
// ============================================================
// REGELBEREICH
// ============================================================
else {
let span =
cfg.UBATT_OFF
-
cfg.UBATT_ON;
if (span <= 0) {
span = 1;
}
// --------------------------------------------------------
// FAKTOR
// --------------------------------------------------------
let factor =
(
ubatt
-
cfg.UBATT_ON
)
/
span;
factor = Math.max(
0,
Math.min(1, factor)
);
// --------------------------------------------------------
// exponentielle Kennlinie
// --------------------------------------------------------
factor =
Math.pow(factor, 0.7);
// --------------------------------------------------------
// Zielwert
// --------------------------------------------------------
let targetLimit =
cfg.LIMIT_MIN_ACTIVE +
(
factor *
(
cfg.LIMIT_MAX
-
cfg.LIMIT_MIN_ACTIVE
)
);
// --------------------------------------------------------
// Schrittweite
// --------------------------------------------------------
targetLimit =
Math.round(
targetLimit
/
cfg.LIMIT_STEP
)
*
cfg.LIMIT_STEP;
// --------------------------------------------------------
// Clamp
// --------------------------------------------------------
targetLimit = Math.max(
cfg.LIMIT_MIN_ACTIVE,
Math.min(
cfg.LIMIT_MAX,
targetLimit
)
);
// --------------------------------------------------------
// sofort erhöhen
// --------------------------------------------------------
if (targetLimit > currentLimit) {
currentLimit =
targetLimit;
}
// --------------------------------------------------------
// verzögert reduzieren
// --------------------------------------------------------
if (targetLimit < currentLimit) {
if (
(
now - lastReduce
)
>=
(
cfg.REDUCE_DELAY_SEC
* 1000
)
) {
currentLimit =
targetLimit;
flow.set(
"WR_LAST_REDUCE",
now
);
}
}
}
// ============================================================
// EMERGENCY MODE
// ============================================================
if (emergencyMode) {
currentLimit =
cfg.LIMIT_EMERGENCY;
}
// ============================================================
// AC STATUS
// ============================================================
let acOn = false;
try {
let root =
flow.get("solarbat_002");
let p = null;
if (root?.AC?.POWER !== undefined) {
p = root.AC.POWER;
}
else if (
root?.AC?.RESULT?.POWER
!== undefined
) {
p =
root.AC.RESULT.POWER;
}
acOn = (p === "ON");
}
catch (e) {
acOn = false;
}
// ============================================================
// AC aktiv
// WR Minimum
// ============================================================
if (acOn && !emergencyMode) {
currentLimit =
cfg.LIMIT_MIN_ACTIVE;
}
// ============================================================
// ABSOLUTES MINIMUM
// ============================================================
if (
currentLimit
<
cfg.LIMIT_EMERGENCY
) {
currentLimit =
cfg.LIMIT_EMERGENCY;
}
// ============================================================
// WR STATUS
// ============================================================
let wrStatus = 0;
try {
let dtu =
flow.get("solarbat_002")
?.DTU?.[cfg.WR_SN];
let reachable =
Number(
dtu?.status?.reachable
)
||
0;
let producing =
Number(
dtu?.status?.producing
)
||
0;
wrStatus =
reachable + producing;
}
catch (e) {
wrStatus = 0;
}
// ============================================================
// SPEICHERN
// ============================================================
flow.set(
"WR_LIMIT",
currentLimit
);
flow.set(
"WR_POWER",
wrPower
);
// ============================================================
// STATUS
// ============================================================
node.status({
fill:
emergencyMode
? "red"
:
wrPower
? "green"
: "grey",
shape: "dot",
text:
ubatt.toFixed(2)
+
"V "
+
cfg.BATTERY_TYPE
+
" WR="
+
wrStatus
+
" "
+
currentLimit
+
"%"
+
" "
+
powersaldiert
+
"W"
});
// ============================================================
// MQTT SPAM SCHUTZ
// ============================================================
let lastSentLimit =
flow.get("LAST_SENT_LIMIT");
let lastSentPower =
flow.get("LAST_SENT_POWER");
// ============================================================
// OUTPUT 1
// LIMIT
// ============================================================
let outLimit = null;
if (lastSentLimit !== currentLimit) {
outLimit = {
payload:
currentLimit
};
flow.set(
"LAST_SENT_LIMIT",
currentLimit
);
}
// ============================================================
// OUTPUT 2
// WR STATUS
// ============================================================
let outStatus = {
payload:
wrStatus
};
// ============================================================
// OUTPUT 3
// WR POWER
// ============================================================
let outPower = null;
if (lastSentPower !== wrPower) {
outPower = {
payload:
wrPower
};
flow.set(
"LAST_SENT_POWER",
wrPower
);
}
// ============================================================
// OUTPUT 4
// CURRENT LIMIT
// ============================================================
let outCurrentLimit = {
payload:
currentLimit
};
// ============================================================
// RETURN
// ============================================================
return [
outLimit,
outStatus,
outPower,
outCurrentLimit
];