Dateien nach „objectmapper“ hochladen

This commit is contained in:
administrator 2026-05-30 09:31:43 +00:00
parent 8c0f201b32
commit 6a71d1eaf9

View file

@ -0,0 +1,532 @@
// ============================================================
// SolarBat Object Mapper
// Version: 2.0
//
// Historie:
//
// V1.0
// - Grundstruktur
// - MQTT Topic Verarbeitung
// - dynamischer Objektaufbau
//
// V1.1
// - rekursives Zahlen-Casting
// - JSON Erkennung
// - stabilere Datenstruktur
//
// V1.2
// - ADS1115 Unterstützung
// - Spannungsumrechnung
// - RAW + Voltage Werte
//
// V1.3
// - UBATT Output ergänzt
// - direkte Übergabe an AC UNIT
//
// V1.4
// - SELF-HEALING integriert
// - Reparatur doppelter Objektstrukturen
//
// V2.0
// - vollständige Dokumentation
// - strukturierte Objektlogik
// - zentrale SolarBat Datenbasis
// - verbesserte Stabilität
// - vorbereitet für Erweiterungen
//
// ============================================================
// ============================================================
// TOPIC PRÜFEN
// ============================================================
if (!msg.topic) {
return null;
}
// ============================================================
// TOPIC ZERLEGEN
// ============================================================
let parts =
msg.topic.split("/");
if (parts.length < 4) {
node.warn(
"Ungültiges Topic: "
+ msg.topic
);
return null;
}
// ============================================================
// ROOT KEY
// ============================================================
let device =
parts[1];
let id =
parts[2];
let rootKey =
`${device}_${id}`;
// ============================================================
// FLOW CONTEXT LADEN
// ============================================================
let root =
flow.get(rootKey)
||
{};
// ============================================================
// SELF HEALING
// repariert doppelte Strukturen
// ============================================================
if (root[rootKey]) {
node.warn(
`Bereinige doppelte Struktur in flow.${rootKey}`
);
root =
root[rootKey];
}
// ============================================================
// REKURSIVES ZAHLEN CASTING
// ============================================================
function castNumbersDeep(input) {
// --------------------------------------------------------
// STRING
// --------------------------------------------------------
if (typeof input === "string") {
if (
input.trim() !== ""
&&
!isNaN(input)
) {
return Number(input);
}
return input;
}
// --------------------------------------------------------
// ARRAY
// --------------------------------------------------------
if (Array.isArray(input)) {
return input.map(
castNumbersDeep
);
}
// --------------------------------------------------------
// OBJECT
// --------------------------------------------------------
if (
typeof input === "object"
&&
input !== null
) {
let result = {};
for (let key in input) {
result[key] =
castNumbersDeep(
input[key]
);
}
return result;
}
// --------------------------------------------------------
// DEFAULT
// --------------------------------------------------------
return input;
}
// ============================================================
// ADS1115
// RAW → VOLT
// ============================================================
function convertADS1115(value) {
if (
typeof value !== "object"
||
value === null
) {
return value;
}
if (!value.ADS1115) {
return value;
}
// --------------------------------------------------------
// SCALE MAP
// --------------------------------------------------------
const SCALE_MAP = {
"A0": 0.004323,
"A1": 0.004334,
"A2": 0.004323,
"A3": 0.004323
};
let ads =
value.ADS1115;
let result = {};
// --------------------------------------------------------
// KANÄLE
// --------------------------------------------------------
for (let ch in ads) {
let raw =
Number(ads[ch]);
if (!isNaN(raw)) {
let scale =
SCALE_MAP[ch]
||
0.004323;
let voltage =
raw * scale;
result[ch + "_raw"] =
raw;
result[ch] =
Number(
voltage.toFixed(2)
);
}
else {
result[ch] =
ads[ch];
}
}
value.ADS1115 =
result;
return value;
}
// ============================================================
// STRUKTUR AUS TOPIC AUFBAUEN
// ============================================================
let obj = root;
for (
let i = 3;
i < parts.length - 1;
i++
) {
let key =
parts[i];
if (
typeof obj[key] !== "object"
||
obj[key] === null
) {
obj[key] = {};
}
obj =
obj[key];
}
// ============================================================
// LETZTER KEY
// ============================================================
let lastKey =
parts[parts.length - 1];
// ============================================================
// PAYLOAD HOLEN
// ============================================================
let value =
msg.payload;
// ============================================================
// JSON ERKENNEN
// ============================================================
if (typeof value === "string") {
try {
value =
JSON.parse(value);
}
catch (e) {
// kein JSON
}
}
// ============================================================
// ZAHLEN CASTEN
// ============================================================
value =
castNumbersDeep(value);
// ============================================================
// ADS1115 VERARBEITEN
// ============================================================
value =
convertADS1115(value);
// ============================================================
// SPEICHERN
// ============================================================
if (
typeof value === "object"
&&
value !== null
&&
!Array.isArray(value)
) {
// --------------------------------------------------------
// OBJEKT ERWEITERN
// --------------------------------------------------------
if (
typeof obj[lastKey]
!== "object"
||
obj[lastKey] === null
) {
obj[lastKey] = {};
}
Object.assign(
obj[lastKey],
value
);
}
else {
// --------------------------------------------------------
// EINFACHER WERT
// --------------------------------------------------------
obj[lastKey] =
value;
}
// ============================================================
// TIMESTAMP
// ============================================================
root._lastUpdate =
Date.now();
// ============================================================
// FLOW CONTEXT SPEICHERN
// ============================================================
flow.set(
rootKey,
root
);
// ============================================================
// OUTPUT 1
// KOMPLETTES OBJEKT
// ============================================================
let out1 = {
payload: {
[rootKey]: root
},
topic:
msg.topic
};
// ============================================================
// OUTPUT 2
// UBATT
// für AC UNIT / WR CONTROL
// ============================================================
let ubatt =
root?.DC?.SENSOR?.INA219?.Voltage;
let out2 = null;
if (typeof ubatt === "number") {
out2 = {
payload:
ubatt,
topic:
"ubatt"
};
}
// ============================================================
// RETURN
// ============================================================
return [
out1,
out2
];