Dateien nach „AC“ hochladen
This commit is contained in:
parent
6d133de8aa
commit
8c0f201b32
1 changed files with 977 additions and 0 deletions
977
AC/solarbat2-AC-UNIT_V2.1.js
Normal file
977
AC/solarbat2-AC-UNIT_V2.1.js
Normal file
|
|
@ -0,0 +1,977 @@
|
|||
// =======================================================
|
||||
// SolarBat AC UNIT
|
||||
// Version: 2.1
|
||||
//
|
||||
// Historie
|
||||
// -------------------------------------------------------
|
||||
// 2.1
|
||||
// - AC_POWER Logging ergänzt
|
||||
// - AC Leistung im Status integriert
|
||||
// - AC Leistung im Status Output ergänzt
|
||||
// - DB Logging erweitert
|
||||
// - Mehrfach-INSERT vorbereitet
|
||||
// - Split-Node kompatibel
|
||||
//
|
||||
// 2.0
|
||||
// - AGM / GEL Unterstützung
|
||||
// - LiFePO4 Unterstützung
|
||||
// - AUTO / ON / OFF
|
||||
// - Dashboard konfigurierbar
|
||||
// - Überschusslogik
|
||||
// - Emergency Mode
|
||||
// - Mindestlaufzeit
|
||||
// - MQTT Spam Schutz
|
||||
// - DB Logging
|
||||
//
|
||||
// =======================================================
|
||||
//
|
||||
// FUNKTIONSÜBERSICHT
|
||||
// -------------------------------------------------------
|
||||
//
|
||||
// Diese Steuerung regelt ein AC-Netzteil zum Laden
|
||||
// einer Batterie abhängig von:
|
||||
//
|
||||
// - Batterietyp
|
||||
// - Batteriespannung
|
||||
// - Netzsaldo / Überschuss
|
||||
// - Emergency Bedingungen
|
||||
// - Betriebsmodus
|
||||
//
|
||||
// Zusätzlich werden Zustände und Messwerte
|
||||
// in MariaDB protokolliert.
|
||||
//
|
||||
// =======================================================
|
||||
//
|
||||
// BATTERIEPROFILE
|
||||
// -------------------------------------------------------
|
||||
//
|
||||
// Unterstützt:
|
||||
//
|
||||
// - AGM / GEL
|
||||
// - LIFEPO4
|
||||
//
|
||||
// Profile können dynamisch per Dashboard
|
||||
// überschrieben werden.
|
||||
//
|
||||
// =======================================================
|
||||
//
|
||||
// BETRIEBSMODI
|
||||
// -------------------------------------------------------
|
||||
//
|
||||
// AUTO
|
||||
// Automatische Regelung
|
||||
//
|
||||
// ON
|
||||
// Netzteil dauerhaft EIN
|
||||
//
|
||||
// OFF
|
||||
// Netzteil dauerhaft AUS
|
||||
//
|
||||
// =======================================================
|
||||
//
|
||||
// AUTO LOGIK
|
||||
// -------------------------------------------------------
|
||||
//
|
||||
// Einschalten:
|
||||
//
|
||||
// - UBATT < UBATT_MIN
|
||||
// - ausreichend Überschuss vorhanden
|
||||
//
|
||||
// Ausschalten:
|
||||
//
|
||||
// - UBATT >= UBATT_MAX
|
||||
// - Mindestlaufzeit erreicht
|
||||
//
|
||||
// Emergency:
|
||||
//
|
||||
// - UBATT <= UBATT_EMERGENCY
|
||||
// - Netzteil wird sofort aktiviert
|
||||
//
|
||||
// =======================================================
|
||||
//
|
||||
// ÜBERSCHUSSLOGIK
|
||||
// -------------------------------------------------------
|
||||
//
|
||||
// POWERSALDIERT:
|
||||
//
|
||||
// Negativ
|
||||
// -> Einspeisung / Überschuss
|
||||
//
|
||||
// Positiv
|
||||
// -> Netzbezug
|
||||
//
|
||||
// Beispiel:
|
||||
//
|
||||
// -200W
|
||||
// -> 200W Überschuss vorhanden
|
||||
//
|
||||
// =======================================================
|
||||
//
|
||||
// AC POWER LOGGING
|
||||
// -------------------------------------------------------
|
||||
//
|
||||
// Zusätzlich wird die Leistungsaufnahme
|
||||
// des AC Netzteils protokolliert.
|
||||
//
|
||||
// Erwartet:
|
||||
//
|
||||
// global.set("AC_POWER", value);
|
||||
//
|
||||
// Typischer Sensor:
|
||||
//
|
||||
// - Shelly Plug
|
||||
// - Nous A8T
|
||||
// - Tasmota ENERGY.Power
|
||||
//
|
||||
// =======================================================
|
||||
//
|
||||
// OUTPUTS
|
||||
// -------------------------------------------------------
|
||||
//
|
||||
// OUTPUT 1
|
||||
// MQTT POWER
|
||||
//
|
||||
// OUTPUT 2
|
||||
// STATUS / DASHBOARD
|
||||
//
|
||||
// OUTPUT 3
|
||||
// DB LOGGING
|
||||
//
|
||||
// =======================================================
|
||||
//
|
||||
// HINWEIS ZU OUTPUT 3
|
||||
// -------------------------------------------------------
|
||||
//
|
||||
// OUTPUT 3 liefert ein ARRAY mit mehreren
|
||||
// SQL INSERT Statements.
|
||||
//
|
||||
// Empfohlen:
|
||||
//
|
||||
// AC UNIT
|
||||
// ↓
|
||||
// split
|
||||
// ↓
|
||||
// mysql
|
||||
//
|
||||
// =======================================================
|
||||
|
||||
|
||||
|
||||
// =======================================================
|
||||
// BATTERY PROFILES
|
||||
// =======================================================
|
||||
|
||||
const BATTERY_PROFILES = {
|
||||
|
||||
// ---------------------------------------------------
|
||||
// AGM / GEL
|
||||
// ---------------------------------------------------
|
||||
|
||||
AGM: {
|
||||
|
||||
TYPE: "AGM",
|
||||
|
||||
AC_UBATT_MIN: 11.5,
|
||||
AC_UBATT_MAX: 14.1,
|
||||
|
||||
AC_UBATT_EMERGENCY: 11.0,
|
||||
|
||||
AC_MIN_RUNTIME: 300,
|
||||
|
||||
AC_POWER_SURPLUS_THRESHOLD: -200
|
||||
},
|
||||
|
||||
|
||||
|
||||
// ---------------------------------------------------
|
||||
// LIFEPO4
|
||||
// ---------------------------------------------------
|
||||
|
||||
LIFEPO4: {
|
||||
|
||||
TYPE: "LIFEPO4",
|
||||
|
||||
AC_UBATT_MIN: 12.4,
|
||||
AC_UBATT_MAX: 14.0,
|
||||
|
||||
AC_UBATT_EMERGENCY: 11.9,
|
||||
|
||||
AC_MIN_RUNTIME: 180,
|
||||
|
||||
AC_POWER_SURPLUS_THRESHOLD: -250
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
// =======================================================
|
||||
// BATTERY TYPE
|
||||
// =======================================================
|
||||
|
||||
let batteryType =
|
||||
|
||||
flow.get("CFG_BATTERY_TYPE")
|
||||
|
||||
||
|
||||
|
||||
"AGM";
|
||||
|
||||
|
||||
|
||||
// =======================================================
|
||||
// BATTERY PROFILE
|
||||
// =======================================================
|
||||
|
||||
let batteryProfile =
|
||||
|
||||
BATTERY_PROFILES[batteryType]
|
||||
|
||||
||
|
||||
|
||||
BATTERY_PROFILES.AGM;
|
||||
|
||||
|
||||
|
||||
// =======================================================
|
||||
// CONFIG
|
||||
// =======================================================
|
||||
|
||||
var cfg = {
|
||||
|
||||
BATTERY_TYPE:
|
||||
batteryProfile.TYPE,
|
||||
|
||||
|
||||
|
||||
// ---------------------------------------------------
|
||||
// Spannung
|
||||
// ---------------------------------------------------
|
||||
|
||||
UBATT_MIN:
|
||||
|
||||
Number(
|
||||
flow.get("CFG_AC_UBATT_MIN")
|
||||
)
|
||||
|
||||
||
|
||||
|
||||
batteryProfile.AC_UBATT_MIN,
|
||||
|
||||
|
||||
|
||||
UBATT_MAX:
|
||||
|
||||
Number(
|
||||
flow.get("CFG_AC_UBATT_MAX")
|
||||
)
|
||||
|
||||
||
|
||||
|
||||
batteryProfile.AC_UBATT_MAX,
|
||||
|
||||
|
||||
|
||||
UBATT_EMERGENCY:
|
||||
|
||||
Number(
|
||||
flow.get("CFG_AC_UBATT_EMERGENCY")
|
||||
)
|
||||
|
||||
||
|
||||
|
||||
batteryProfile.AC_UBATT_EMERGENCY,
|
||||
|
||||
|
||||
|
||||
// ---------------------------------------------------
|
||||
// Laufzeit
|
||||
// ---------------------------------------------------
|
||||
|
||||
MIN_RUNTIME:
|
||||
|
||||
Number(
|
||||
flow.get("CFG_AC_MIN_RUNTIME")
|
||||
)
|
||||
|
||||
||
|
||||
|
||||
batteryProfile.AC_MIN_RUNTIME,
|
||||
|
||||
|
||||
|
||||
// ---------------------------------------------------
|
||||
// Überschuss
|
||||
// ---------------------------------------------------
|
||||
|
||||
POWER_SURPLUS_THRESHOLD:
|
||||
|
||||
Number(
|
||||
flow.get(
|
||||
"CFG_AC_POWER_SURPLUS_THRESHOLD"
|
||||
)
|
||||
)
|
||||
|
||||
||
|
||||
|
||||
batteryProfile.AC_POWER_SURPLUS_THRESHOLD
|
||||
};
|
||||
|
||||
|
||||
|
||||
// =======================================================
|
||||
// INPUTS
|
||||
// =======================================================
|
||||
|
||||
var ubatt = null;
|
||||
var modeInput = null;
|
||||
|
||||
|
||||
|
||||
// -------------------------------------------------------
|
||||
// Batteriespannung
|
||||
// -------------------------------------------------------
|
||||
|
||||
if (typeof msg.payload === "number") {
|
||||
|
||||
ubatt = msg.payload;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// -------------------------------------------------------
|
||||
// Mode
|
||||
// -------------------------------------------------------
|
||||
|
||||
if (msg.topic === "mode") {
|
||||
|
||||
modeInput = msg.payload;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// =======================================================
|
||||
// GLOBALER NETZSALDO
|
||||
// =======================================================
|
||||
|
||||
var powersaldiert =
|
||||
|
||||
Number(
|
||||
global.get("POWERSALDIERT")
|
||||
);
|
||||
|
||||
|
||||
|
||||
if (isNaN(powersaldiert)) {
|
||||
|
||||
powersaldiert = 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// =======================================================
|
||||
// AC POWER
|
||||
// Leistung des AC Netzteils
|
||||
// =======================================================
|
||||
|
||||
var acPower =
|
||||
|
||||
Number(
|
||||
global.get("AC_POWER")
|
||||
);
|
||||
|
||||
|
||||
|
||||
if (isNaN(acPower)) {
|
||||
|
||||
acPower = 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// =======================================================
|
||||
// CONTEXT
|
||||
// =======================================================
|
||||
|
||||
var state =
|
||||
context.get("state")
|
||||
|
||||
||
|
||||
|
||||
"OFF";
|
||||
|
||||
|
||||
|
||||
var mode =
|
||||
context.get("mode")
|
||||
|
||||
||
|
||||
|
||||
"auto";
|
||||
|
||||
|
||||
|
||||
var lastMode =
|
||||
context.get("lastMode")
|
||||
|
||||
||
|
||||
|
||||
mode;
|
||||
|
||||
|
||||
|
||||
var lastChange =
|
||||
context.get("lastChange")
|
||||
|
||||
||
|
||||
|
||||
0;
|
||||
|
||||
|
||||
|
||||
var now =
|
||||
Math.floor(Date.now() / 1000);
|
||||
|
||||
|
||||
|
||||
// =======================================================
|
||||
// MODE UMSCHALTUNG
|
||||
// =======================================================
|
||||
|
||||
var forceOffOnAuto = false;
|
||||
|
||||
|
||||
|
||||
if (
|
||||
|
||||
modeInput === "auto"
|
||||
|
||||
||
|
||||
|
||||
modeInput === "on"
|
||||
|
||||
||
|
||||
|
||||
modeInput === "off"
|
||||
|
||||
) {
|
||||
|
||||
mode = modeInput;
|
||||
|
||||
context.set(
|
||||
"mode",
|
||||
mode
|
||||
);
|
||||
|
||||
|
||||
|
||||
// ---------------------------------------------------
|
||||
// AUTO
|
||||
// ---------------------------------------------------
|
||||
|
||||
if (
|
||||
|
||||
mode === "auto"
|
||||
|
||||
&&
|
||||
|
||||
lastMode !== "auto"
|
||||
|
||||
) {
|
||||
|
||||
state = "OFF";
|
||||
|
||||
context.set(
|
||||
"lastChange",
|
||||
now
|
||||
);
|
||||
|
||||
forceOffOnAuto = true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ---------------------------------------------------
|
||||
// MANUELL ON
|
||||
// ---------------------------------------------------
|
||||
|
||||
else if (mode === "on") {
|
||||
|
||||
state = "ON";
|
||||
|
||||
context.set(
|
||||
"lastChange",
|
||||
now
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ---------------------------------------------------
|
||||
// MANUELL OFF
|
||||
// ---------------------------------------------------
|
||||
|
||||
else if (mode === "off") {
|
||||
|
||||
state = "OFF";
|
||||
|
||||
context.set(
|
||||
"lastChange",
|
||||
now
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
context.set(
|
||||
"lastMode",
|
||||
mode
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// =======================================================
|
||||
// AUTO LOGIK
|
||||
// =======================================================
|
||||
|
||||
if (
|
||||
|
||||
mode === "auto"
|
||||
|
||||
&&
|
||||
|
||||
ubatt !== null
|
||||
|
||||
&&
|
||||
|
||||
!forceOffOnAuto
|
||||
|
||||
) {
|
||||
|
||||
var targetState =
|
||||
state;
|
||||
|
||||
|
||||
|
||||
// ===================================================
|
||||
// EMERGENCY MODE
|
||||
// Batterie MUSS geladen werden
|
||||
// ===================================================
|
||||
|
||||
if (
|
||||
|
||||
ubatt <=
|
||||
cfg.UBATT_EMERGENCY
|
||||
|
||||
) {
|
||||
|
||||
targetState = "ON";
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ===================================================
|
||||
// NORMALE REGELUNG
|
||||
// ===================================================
|
||||
|
||||
else {
|
||||
|
||||
// -----------------------------------------------
|
||||
// Einschalten
|
||||
// Nur wenn:
|
||||
// - Batterie leer genug
|
||||
// - Überschuss vorhanden
|
||||
// -----------------------------------------------
|
||||
|
||||
if (
|
||||
|
||||
ubatt < cfg.UBATT_MIN
|
||||
|
||||
&&
|
||||
|
||||
powersaldiert
|
||||
<=
|
||||
cfg.POWER_SURPLUS_THRESHOLD
|
||||
|
||||
) {
|
||||
|
||||
targetState = "ON";
|
||||
}
|
||||
|
||||
|
||||
|
||||
// -----------------------------------------------
|
||||
// Ausschalten
|
||||
// -----------------------------------------------
|
||||
|
||||
else if (
|
||||
|
||||
ubatt >= cfg.UBATT_MAX
|
||||
|
||||
) {
|
||||
|
||||
targetState = "OFF";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ===================================================
|
||||
// SCHALTLOGIK
|
||||
// ===================================================
|
||||
|
||||
if (targetState !== state) {
|
||||
|
||||
// -----------------------------------------------
|
||||
// Einschalten sofort
|
||||
// -----------------------------------------------
|
||||
|
||||
if (targetState === "ON") {
|
||||
|
||||
state = "ON";
|
||||
|
||||
context.set(
|
||||
"lastChange",
|
||||
now
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// -----------------------------------------------
|
||||
// Ausschalten verzögert
|
||||
// -----------------------------------------------
|
||||
|
||||
else if (
|
||||
|
||||
targetState === "OFF"
|
||||
|
||||
) {
|
||||
|
||||
if (
|
||||
|
||||
(
|
||||
now - lastChange
|
||||
)
|
||||
|
||||
>=
|
||||
|
||||
cfg.MIN_RUNTIME
|
||||
|
||||
) {
|
||||
|
||||
state = "OFF";
|
||||
|
||||
context.set(
|
||||
"lastChange",
|
||||
now
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// =======================================================
|
||||
// CONTEXT SPEICHERN
|
||||
// =======================================================
|
||||
|
||||
context.set(
|
||||
"state",
|
||||
state
|
||||
);
|
||||
|
||||
|
||||
|
||||
// =======================================================
|
||||
// STATUS
|
||||
// =======================================================
|
||||
|
||||
node.status({
|
||||
|
||||
fill:
|
||||
|
||||
state === "ON"
|
||||
? "green"
|
||||
: "grey",
|
||||
|
||||
|
||||
shape: "dot",
|
||||
|
||||
|
||||
text:
|
||||
|
||||
mode.toUpperCase()
|
||||
|
||||
+
|
||||
|
||||
" "
|
||||
|
||||
+
|
||||
|
||||
ubatt?.toFixed(2)
|
||||
|
||||
+
|
||||
|
||||
"V "
|
||||
|
||||
+
|
||||
|
||||
powersaldiert
|
||||
|
||||
+
|
||||
|
||||
"W AC:"
|
||||
|
||||
+
|
||||
|
||||
acPower
|
||||
|
||||
+
|
||||
|
||||
"W "
|
||||
|
||||
+
|
||||
|
||||
cfg.BATTERY_TYPE
|
||||
});
|
||||
|
||||
|
||||
|
||||
// =======================================================
|
||||
// OUTPUT 1
|
||||
// MQTT
|
||||
// =======================================================
|
||||
|
||||
var lastSentState =
|
||||
context.get("lastSentState");
|
||||
|
||||
|
||||
|
||||
var out1 = null;
|
||||
|
||||
|
||||
|
||||
if (forceOffOnAuto) {
|
||||
|
||||
out1 = {
|
||||
|
||||
payload: "OFF"
|
||||
};
|
||||
|
||||
context.set(
|
||||
"lastSentState",
|
||||
"OFF"
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
else if (
|
||||
|
||||
state !== lastSentState
|
||||
|
||||
) {
|
||||
|
||||
out1 = {
|
||||
|
||||
payload: state
|
||||
};
|
||||
|
||||
context.set(
|
||||
"lastSentState",
|
||||
state
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// =======================================================
|
||||
// OUTPUT 2
|
||||
// STATUS
|
||||
// =======================================================
|
||||
|
||||
var out2 = {
|
||||
|
||||
payload: {
|
||||
|
||||
mode: mode,
|
||||
|
||||
state: state,
|
||||
|
||||
ubatt: ubatt,
|
||||
|
||||
powersaldiert: powersaldiert,
|
||||
|
||||
battery: cfg.BATTERY_TYPE,
|
||||
|
||||
acPower: acPower
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
// =======================================================
|
||||
// OUTPUT 3
|
||||
// DB LOGGING
|
||||
// =======================================================
|
||||
|
||||
var out3 = null;
|
||||
|
||||
|
||||
|
||||
if (ubatt !== null) {
|
||||
|
||||
var lastUbatt =
|
||||
context.get("lastUbatt");
|
||||
|
||||
|
||||
|
||||
var DELTA = 0.05;
|
||||
|
||||
|
||||
|
||||
var changed = false;
|
||||
|
||||
|
||||
|
||||
if (
|
||||
|
||||
lastUbatt === undefined
|
||||
|
||||
) {
|
||||
|
||||
changed = true;
|
||||
}
|
||||
|
||||
else if (
|
||||
|
||||
Math.abs(
|
||||
ubatt - lastUbatt
|
||||
)
|
||||
|
||||
>=
|
||||
|
||||
DELTA
|
||||
|
||||
) {
|
||||
|
||||
changed = true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
context.set(
|
||||
"lastUbatt",
|
||||
ubatt
|
||||
);
|
||||
|
||||
|
||||
|
||||
if (changed) {
|
||||
|
||||
var nowDate =
|
||||
new Date();
|
||||
|
||||
|
||||
|
||||
var datum =
|
||||
|
||||
nowDate
|
||||
.toISOString()
|
||||
.slice(0, 10);
|
||||
|
||||
|
||||
|
||||
var zeit =
|
||||
|
||||
nowDate
|
||||
.toTimeString()
|
||||
.slice(0, 8);
|
||||
|
||||
|
||||
|
||||
out3 = [
|
||||
|
||||
// -------------------------------------------------
|
||||
// BATTERIEVOLTAGE
|
||||
// -------------------------------------------------
|
||||
|
||||
{
|
||||
topic:
|
||||
|
||||
"INSERT INTO sensordata " +
|
||||
|
||||
"(DATUM, Zeit, SENSORID, SCHLUESSEL, WERT, AGG) " +
|
||||
|
||||
"VALUES (?, ?, ?, ?, ?, ?)",
|
||||
|
||||
|
||||
|
||||
payload: [
|
||||
|
||||
datum,
|
||||
|
||||
zeit,
|
||||
|
||||
"solarbatt2",
|
||||
|
||||
"BATTERIEVOLTAGE",
|
||||
|
||||
ubatt,
|
||||
|
||||
""
|
||||
]
|
||||
},
|
||||
|
||||
|
||||
|
||||
// -------------------------------------------------
|
||||
// AC POWER
|
||||
// -------------------------------------------------
|
||||
|
||||
{
|
||||
topic:
|
||||
|
||||
"INSERT INTO sensordata " +
|
||||
|
||||
"(DATUM, Zeit, SENSORID, SCHLUESSEL, WERT, AGG) " +
|
||||
|
||||
"VALUES (?, ?, ?, ?, ?, ?)",
|
||||
|
||||
|
||||
|
||||
payload: [
|
||||
|
||||
datum,
|
||||
|
||||
zeit,
|
||||
|
||||
"solarbatt2",
|
||||
|
||||
"AC_POWER",
|
||||
|
||||
acPower,
|
||||
|
||||
""
|
||||
]
|
||||
}
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// =======================================================
|
||||
// RETURN
|
||||
// =======================================================
|
||||
|
||||
return [
|
||||
|
||||
out1,
|
||||
|
||||
out2,
|
||||
|
||||
out3
|
||||
];
|
||||
Loading…
Add table
Reference in a new issue