Dateien nach „objectmapper“ hochladen
This commit is contained in:
parent
8c0f201b32
commit
6a71d1eaf9
1 changed files with 532 additions and 0 deletions
532
objectmapper/solarbat2-object-mapper-V2.0.js
Normal file
532
objectmapper/solarbat2-object-mapper-V2.0.js
Normal 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
|
||||
];
|
||||
Loading…
Add table
Reference in a new issue