I need a little more flexibility and control of my geyser and some additional heavy loads I am hopefully adding.
So I decided to replace my old Arduino set-up (ModbusThermostat) with Node-Red.
Now I need to bulk read settings from my Deye/Sunsynk inverter every second, and then reliably turn off the geyser within another second if I exceed maximum server load.
I started with SunSynk-NodeRed, and it works, but reads parameters one at a time, so is rather slow and ridiculously clunky for extending.
Now I am an absolute n00b at Node-Red, so I have no idea of best practices, or how well this will scale, and I was hoping for some advice.
Basically, I keep the Modbus-Flex-Getter from the original project, and set bigger quantities to bulk read parameters.
Then I have this function to decode them, and spit out a stream of parameter messages:
// per register
// props = properties of new message object
// conv = conversion function to get payload.value from modbus response data
// scale = (optional) scaling factor to apply to payload.value after conversion
// skip = (optional) do not produce output for this register
//
// conversion algorithms
// s16 = signed 16 bit
// u16 = unsigned 16 bit
// temp = sunsynk temperature value
/*const regs = {
'182': {
props: {
topic: 'battery_temp',
deviceclass: 'battery',
unit: '°C',
},
conv: 'temp',
},
'183': {
props: {
topic: 'battery_volt',
deviceclass: 'battery',
unit: 'V',
},
conv: 'u16',
scale: 0.01,
},
'184': {
props: {
topic: 'battery_soc',
deviceclass: 'battery',
unit: '%',
},
conv: 's16',
},
};
*/
var regs = global.get('sunsynk_regs');
if(!regs) {
node.error('Error getting global sunsynk_regs!');
node.status({ text:'Error getting global sunsynk_regs!'});
}
if (msg.values.length != msg.modbusRequest.quantity) {
node.warn("Read failed - response length does not match request...");
return null;
}
var base = msg.modbusRequest.address;
//node.warn("start: "+base);
var outs = [];
var i = 0;
while(i < msg.values.length) {
var reg = base + i;
var rem = msg.values.length - i;
//node.warn("reg: "+reg);
//node.warn("remain: " + rem);
if(!(reg in regs)) {
//node.warn('key not found:'+reg)
i++;
continue;
}
var config = regs[reg];
//node.warn("config: "+config);
if("skip" in config && config.skip) {
node.warn('key set to skip:' + reg)
i++;
continue;
}
var newmsg = Object.assign({}, config.props);
if(config.conv == 's16') {
var val = (msg.values[i] + 0);
if(val > 32767) {
val = val - 65535;
}
newmsg.payload = val;
} else if (config.conv == 'u16') {
newmsg.payload = (msg.values[i] + 0);
} else if (config.conv == 'temp') {
newmsg.payload = (msg.values[i] - 1000) / 10;
} else {
node.warn('conversion function not found:' + config.conv);
i++;
continue;
}
if("scale" in config) {
newmsg.payload *= config.scale;
}
outs.push(newmsg);
i++;
}
//node.warn("outs: "+outs.length);
return [outs];
The regs object has moved to the global context and is loaded from a JSON file, but I kept the originals in comment form, so you know what the object looks like. (Actually, with the eye to properly modularising it, it is 2 files - a system file with defaults and a user file with local overrides.)
Anyway, before I translate 100s of modbus register definitions into JSON configurations, I would like to try get an idea if I am going down the right path here, or will this approach not scale well?
Thanks,
Justin