Hello,
I currently use node-red for my automations. However, I am in the process of replacing this with pure Home Assistant automations.
However, I have a complex calculation in node-red in a Java Script function. This script calculates the fill level of my water tank.
How can I run the script in a native HA automation?
The script looks like this:
// zistern litering table (got from manufacturer GRAF)
// first column = fillheight in mm
// second column = liter
const literingTable = [
[0, 0],
[100, 135],
[200, 320],
[300, 550],
[400, 825],
[500, 1120],
[600, 1450],
[700, 1800],
[800, 2160],
[900, 2530],
[1000, 2895],
[1100, 3245],
[1200, 3580],
[1300, 3885],
[1400, 4160],
[1500, 4430],
[1600, 4585],
[1700, 4720],
[1800, 4780],
];
//var msg;
var msg2;
var msg3;
var msg4;
// pressure Sensor parameters
const max_height = 5; // 4m
const max_mA = 20; // mA at max_height
const min_mA = 4; // mA at zero height
// index to table for lowest level in zistern where water
// can still be pumped. Below pump cant reach the water
const percent_0_index = 2 // eg, 2 is 976l
//const lowest_level_Tindex = 2;
// Main starts here
var y;
var measured_mA = msg.payload; // get mA value from Sensor as input
var measured_mA_mean;
measured_mA_mean = context.get('measured_mA_mean');
if (context.get('Startup') == 341487) {
context.set("Startup", 0)
measured_mA_mean = measured_mA;
}
measured_mA_mean = (measured_mA_mean * 2 + measured_mA) / 3;
context.set('measured_mA_mean', measured_mA_mean);
if (measured_mA < min_mA) {
// if its below allowed range, then exit
node.warn("Error ! Input Value below 4mA")
return;
}
// both possible: measured_mA_mean or measured_mA
spline(measured_mA_mean)
// calculates based on linear relationship. alternative below
var high_mm = (max_height * 1000 / (max_mA - min_mA) * (measured_mA_mean - min_mA));
//var x = Math.round(high_mm);
var x = high_mm;
//search for fitting table area, getting index into table as i
for (var i = 0; i < literingTable.length; i++) {
if (literingTable[i][0] > x) break; // if "end entry" found, then exit
}
i = i - 1; // correct index to "start entry"
// check if index was inside table and not the last entry
if (i < literingTable.length - 1) {
// was inside table
// get values from table
var x1 = literingTable[i + 0][0];
var y1 = literingTable[i + 0][1];
var x2 = literingTable[i + 1][0];
var y2 = literingTable[i + 1][1];
// and calculate the value in between = lineare interpolation
y = (y1 + ((x - x1) / (x2 - x1) * (y2 - y1)));
}
else {
// was the last entry,
// so return last entry as fixe value from table
// no calcuation needed
y = literingTable[i + 0][1]
}
// calculate percentage from table index 2 as 0% to last table index as 100%
var percent = 100 / (literingTable[literingTable.length - 1][1] - literingTable[percent_0_index][1]);
percent = Math.round(((percent * (y - literingTable[percent_0_index][1])) * 1) * 1e0) / 1e0;
if (percent < 0) {
percent = -1;
}
y = Math.round(y);
msg.payload = [
[ // first value
{ // influxdb2 fields
"menge": y
},
{ // influxdb2 tags
"typ": "inhalt",
"einheit": "liter",
"quelle": "drucksensor",
"ort": "zisterne"
}
],
[ // second value
{ // influxdb2 fields
"menge": percent
},
{ // influxdb2 tags
"typ": "inhalt",
"einheit": "prozent",
"quelle": "drucksensor",
"ort": "zisterne"
}
]
];
return [msg];
function spline(pressure) {
// normalized needed for spline interpolation
const normalized_pressure = (pressure - min_mA) / (max_mA - min_mA);
// spline interpolation for x:
var interval = -1; // Initialize with an invalid value
for (var i = 0; i < literingTable.length - 1; i++) {
if (normalized_pressure >= literingTable[i][0] && normalized_pressure <= literingTable[i + 1][0]) {
interval = i;
break; // if "end entry" found, then exit
}
}
// Errorhandling
if (interval === -1) {
interval = normalized_pressure < literingTable[0][0] ? 0 : literingTable.length - 2;
}
// find value between two entries
const x1 = literingTable[interval][0];
const y1 = literingTable[interval][1];
const x2 = literingTable[interval + 1][0];
const y2 = literingTable[interval + 1][1];
//non-linear interpolation
const t = (normalized_pressure - x1) / (x2 - x1);
const a = 2 * y1 - 2 * y2 + 1;
const b = -3 * y1 + 3 * y2;
const c = y1;
// fill height in mm
var y = a * Math.pow(t, 3) + b * Math.pow(t, 2) + c * t;
// fill volume (1800 based on max_fillheight) in mm
var x = (Math.PI * y ^ 2 * (3 * (1800 / 2) - y)) / 3;
// return what you need.
return y; // or x
}
Thank you very much for your help