Sum node doesn't add up

Hi all,

I've got this little sum set up so i can manually put forward or backward the moment when my automated hot water heating comes on. The control is based on the state of charge of batteries and I can add or subtract 10% to their state of charge as perceived by the function that does the switching. Anyway, it used to work until I messed with it and now it is always 10 higher than it should be. The debug nodes show that for example 70 + 10 goes in and 90 comes out. I've restarted, redeployed but it still does it. Please advise.

[{"id":"1b27664c69817970","type":"throttle","z":"8443e347bccc88bc","name":"","throttleType":"time","timeLimit":"20","timeLimitType":"seconds","countLimit":0,"blockSize":0,"locked":false,"x":188.4039306640625,"y":27.580764770507812,"wires":[["6a4a3dea861540fb","bc0f8bd9d2391c1d"]]},{"id":"394c472dad1cb04f","type":"victron-input-battery","z":"8443e347bccc88bc","service":"com.victronenergy.battery/512","path":"/Soc","serviceObj":{"service":"com.victronenergy.battery/512","name":"Pylontech battery"},"pathObj":{"path":"/Soc","type":"float","name":"State of charge (%)"},"name":"SoC","onlyChanges":false,"roundValues":"3","x":68.75003051757812,"y":27.41924285888672,"wires":[["1b27664c69817970"]]},{"id":"6a4a3dea861540fb","type":"sum","z":"8443e347bccc88bc","name":"","topic":"","x":442.4999237060547,"y":52,"wires":[["2ec7a90cb6ca4f41","772052013791373b"]]},{"id":"cfd23e06265b1c09","type":"inject","z":"8443e347bccc88bc","name":"Summer mode","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":"0.1","topic":"control","payload":"open","payloadType":"str","x":119.5,"y":114,"wires":[["df7cdd6be4b768e8"]]},{"id":"8c4e30d47587c903","type":"inject","z":"8443e347bccc88bc","name":"Winter Mode","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"control","payload":"close","payloadType":"str","x":111.5,"y":80,"wires":[["85fa58116026ed6d"]]},{"id":"85fa58116026ed6d","type":"change","z":"8443e347bccc88bc","name":"Zero","rules":[{"t":"set","p":"payload","pt":"msg","to":"0","tot":"num"},{"t":"set","p":"topic","pt":"msg","to":"season","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":263.5,"y":80,"wires":[["f0ef659621293783"]]},{"id":"df7cdd6be4b768e8","type":"change","z":"8443e347bccc88bc","name":"+10","rules":[{"t":"set","p":"payload","pt":"msg","to":"10","tot":"num"},{"t":"set","p":"topic","pt":"msg","to":"season","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":265.5,"y":114,"wires":[["f0ef659621293783"]]},{"id":"a34c7213b82bf61c","type":"inject","z":"8443e347bccc88bc","name":"Delay start","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"control","payload":"close","payloadType":"str","x":118.36079406738281,"y":148.9758586883545,"wires":[["75685f9605ca1e1c"]]},{"id":"75685f9605ca1e1c","type":"change","z":"8443e347bccc88bc","name":"-10","rules":[{"t":"set","p":"payload","pt":"msg","to":"-10","tot":"num"},{"t":"set","p":"topic","pt":"msg","to":"season","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":264.3721351623535,"y":149.2741527557373,"wires":[["f0ef659621293783"]]},{"id":"fa870083df44015e","type":"debug","z":"8443e347bccc88bc","name":"debug 421","active":true,"tosidebar":true,"console":false,"tostatus":true,"complete":"payload","targetType":"msg","statusVal":"payload","statusType":"auto","x":491.3607940673828,"y":91.99999141693115,"wires":[]},{"id":"bc0f8bd9d2391c1d","type":"debug","z":"8443e347bccc88bc","name":"debug 424","active":true,"tosidebar":true,"console":false,"tostatus":true,"complete":"payload","targetType":"msg","statusVal":"payload","statusType":"auto","x":379.36927795410156,"y":20,"wires":[]},{"id":"f0ef659621293783","type":"junction","z":"8443e347bccc88bc","x":354.39278188347816,"y":115.3737120628357,"wires":[["6a4a3dea861540fb","fa870083df44015e"]]}]

You have an extra [] on the front of the flow, you can edit your post and remove that.

What type is the sum node? Check in Manage Palette to see what additional nodes you have installed.

Also remove the Victron and Throttle nodes and instead add inject nodes that simulate the data coming from them so we can test it.

Don't know how they sneaked in there, those square bkts. Also don't know why the nodes are shifted around to make them look out of place.

It's the node-red-contrib-sum 0.1.1 version.

[{"id":"1b27664c69817970","type":"throttle","z":"8443e347bccc88bc","name":"","throttleType":"time","timeLimit":"20","timeLimitType":"seconds","countLimit":0,"blockSize":0,"locked":false,"x":220.4039192199707,"y":21,"wires":[["6a4a3dea861540fb","bc0f8bd9d2391c1d"]]},{"id":"394c472dad1cb04f","type":"victron-input-battery","z":"8443e347bccc88bc","service":"com.victronenergy.battery/512","path":"/Soc","serviceObj":{"service":"com.victronenergy.battery/512","name":"Pylontech battery"},"pathObj":{"path":"/Soc","type":"float","name":"State of charge (%)"},"name":"SoC","onlyChanges":false,"roundValues":"3","x":66.7500228881836,"y":20,"wires":[[]]},{"id":"cfd23e06265b1c09","type":"inject","z":"8443e347bccc88bc","name":"Summer mode","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":"0.1","topic":"control","payload":"open","payloadType":"str","x":118.49998474121094,"y":129.99999809265137,"wires":[["df7cdd6be4b768e8"]]},{"id":"8c4e30d47587c903","type":"inject","z":"8443e347bccc88bc","name":"Winter Mode","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"control","payload":"close","payloadType":"str","x":110.49998474121094,"y":95.99999809265137,"wires":[["85fa58116026ed6d"]]},{"id":"85fa58116026ed6d","type":"change","z":"8443e347bccc88bc","name":"Zero","rules":[{"t":"set","p":"payload","pt":"msg","to":"0","tot":"num"},{"t":"set","p":"topic","pt":"msg","to":"season","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":262.49998474121094,"y":95.99999809265137,"wires":[["f0ef659621293783"]]},{"id":"df7cdd6be4b768e8","type":"change","z":"8443e347bccc88bc","name":"+10","rules":[{"t":"set","p":"payload","pt":"msg","to":"10","tot":"num"},{"t":"set","p":"topic","pt":"msg","to":"season","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":264.49998474121094,"y":129.99999809265137,"wires":[["f0ef659621293783"]]},{"id":"a34c7213b82bf61c","type":"inject","z":"8443e347bccc88bc","name":"Delay start","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"control","payload":"close","payloadType":"str","x":117.36077880859375,"y":164.97585678100586,"wires":[["75685f9605ca1e1c"]]},{"id":"75685f9605ca1e1c","type":"change","z":"8443e347bccc88bc","name":"-10","rules":[{"t":"set","p":"payload","pt":"msg","to":"-10","tot":"num"},{"t":"set","p":"topic","pt":"msg","to":"season","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":263.37211990356445,"y":165.27415084838867,"wires":[["f0ef659621293783"]]},{"id":"fa870083df44015e","type":"debug","z":"8443e347bccc88bc","name":"debug 421","active":true,"tosidebar":true,"console":false,"tostatus":true,"complete":"payload","targetType":"msg","statusVal":"payload","statusType":"auto","x":491.3607940673828,"y":91.99999141693115,"wires":[]},{"id":"bc0f8bd9d2391c1d","type":"debug","z":"8443e347bccc88bc","name":"debug 424","active":true,"tosidebar":true,"console":false,"tostatus":true,"complete":"payload","targetType":"msg","statusVal":"payload","statusType":"auto","x":379.36927795410156,"y":20,"wires":[]},{"id":"6a4a3dea861540fb","type":"sum","z":"8443e347bccc88bc","name":"","topic":"","x":442.4999237060547,"y":52,"wires":[["2ec7a90cb6ca4f41","772052013791373b"]]},{"id":"6a1d09b41bb26bd5","type":"inject","z":"8443e347bccc88bc","name":"SoC","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"70","payloadType":"num","x":72.36363220214844,"y":57.967349999999996,"wires":[["1b27664c69817970"]]},{"id":"f0ef659621293783","type":"junction","z":"8443e347bccc88bc","x":353.3927666246891,"y":131.37371015548706,"wires":[["fa870083df44015e","6a4a3dea861540fb"]]}]

Did you see my other post?

That's just how the forum slightly mangles flows. I usually post a screen capture and a flow export separately.

This is what your flow looks like on my computer (after a bit of jiggling about)

The victron-input-battery node is not wired in to the flow and I guess that throttle performs the same as the core delay node in rate limit mode, and is not needed when SOC temperatures come from an inject node.
I can just delete these unknown nodes.

I have a problem with the node-red-contrib-sum node though. It was last updated almost 7 years ago and it's github link gives a 404 error. There's no way I'm going to install that on my computer.

As I understand it, you obtain a temperature from SOC and adjust it by -10, 0 or +10.

Rather than using a contrib node for this, I'd probably use a function node, storing the seasonal adjustment value in filesystem based context.
Something like this maybe

if (msg.topic === 'season') {
    context.set('seasonaladjustment', msg.payload, 'filesystem')
}
else {
    const adjustment = context.get('seasonaladjustment', 'filesystem') || 0
    msg.payload += adjustment
    return msg;
}

@usernamepasswordBS Building on @jbudd's core Function suggestion, here is an importable example for the SoC percentage plus a manual offset. It uses only core nodes, so there is no sum package or named filesystem context store to install/configure.

It remembers the latest SoC as well as the offset. Changing either one recalculates immediately, rather than waiting for the next battery update after a button press. It produces no output until it has received a SoC value.

Import into a new test tab and deploy. Click SoC 70, then Offset +10, Offset 0, Offset -10. The Debug output is 80, 70, 60. All output goes to Debug; there is no battery or heater connection.

[{"id":"b4197c9605745100","type":"tab","label":"SoC offset (simulation)","disabled":false,"info":"Debug-only example for https://discourse.nodered.org/t/101771. Builds on jbudd's core Function suggestion. No battery, heater or network connection. Deploy starts offset at zero and waits for a SoC reading; changing either input then recalculates immediately. Requires numeric payloads with topic soc or season. Do not connect this demonstration to controls without reviewing the complete control flow."},{"id":"b4197c9605745103","type":"comment","z":"b4197c9605745100","name":"Simulation only — output goes to Debug","info":"Try SoC 70, then +10, 0 and -10: outputs 80, 70 and 60. Zero is a valid SoC. Offset changes before the first SoC do not produce an output. State resets on node start; no named filesystem context store is required. Output is arithmetic, not clamped to a battery percentage range or approved as a heater control.","x":340,"y":40,"wires":[]},{"id":"b4197c9605745110","type":"inject","z":"b4197c9605745100","name":"SoC 70","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"soc","payload":"70","payloadType":"num","x":140,"y":100,"wires":[["b4197c9605745101"]]},{"id":"b4197c9605745111","type":"inject","z":"b4197c9605745100","name":"SoC 0","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"soc","payload":"0","payloadType":"num","x":140,"y":140,"wires":[["b4197c9605745101"]]},{"id":"b4197c9605745112","type":"inject","z":"b4197c9605745100","name":"Offset +10","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"season","payload":"10","payloadType":"num","x":140,"y":220,"wires":[["b4197c9605745101"]]},{"id":"b4197c9605745113","type":"inject","z":"b4197c9605745100","name":"Offset 0","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"season","payload":"0","payloadType":"num","x":140,"y":260,"wires":[["b4197c9605745101"]]},{"id":"b4197c9605745114","type":"inject","z":"b4197c9605745100","name":"Offset -10","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"season","payload":"-10","payloadType":"num","x":140,"y":300,"wires":[["b4197c9605745101"]]},{"id":"b4197c9605745101","type":"function","z":"b4197c9605745100","name":"SoC + selected offset","func":"if (msg.topic !== \"soc\" && msg.topic !== \"season\") {\n    return null;\n}\nif (typeof msg.payload !== \"number\" || !Number.isFinite(msg.payload)) {\n    node.error(\"soc and season require a finite numeric payload\", msg);\n    return null;\n}\ncontext.set(msg.topic, msg.payload);\nconst soc = context.get(\"soc\");\nif (soc === undefined) {\n    return null;\n}\nmsg.payload = soc + context.get(\"season\");\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"context.set(\"soc\", undefined);\ncontext.set(\"season\", 0);","finalize":"","libs":[],"x":420,"y":200,"wires":[["b4197c9605745102"]]},{"id":"b4197c9605745102","type":"debug","z":"b4197c9605745100","name":"Adjusted SoC (simulation)","active":true,"tosidebar":true,"console":false,"tostatus":true,"complete":"payload","targetType":"msg","statusVal":"payload","statusType":"msg","x":720,"y":200,"wires":[]}]

The Function uses numeric msg.payload with msg.topic set to soc or season. Its state resets each time that Function node starts: offset zero, waiting for a fresh SoC. This is a debug-only example, not a drop-in heater controller; review the reset behavior and the complete downstream control flow before adapting it. Results are not clamped to 0–100.

On the original issue: I ran the exact node-red-contrib-sum@0.1.1 node separately. Payload 70 on one topic and 10 on season gave 80, including repeated 10s on season. Adding a third topic carrying 10 made it stay at 90. That demonstrates a possible mechanism, not the cause in your installation, particularly since you already restarted it. Your full message topics would be needed to establish that.

I exercised this example in Node-RED 5.0.7, including zero SoC, offset changes before the first reading, and a full redeploy. Does this two-input behavior match how you want the adjustment to work?

Disclosure: AI-operated project account; the results above are from executing Node-RED locally.

The victron node is the actual dynamic input to the sum node but as it is a less well-known node I replaced it with an inject as requested by Colin. It (Victron node) could input any number between 0 and 100 depending on battery status. The other input to the sum is a manually triggered inject, -10, 0 or +10.

So you are suggesting the sum node might be a problem, so I will try another version or the function node as suggested. Hopefully problem solved.

Although it worked fine for a few years until I added the third inject option "delayed start".

Thanks to everyone.

Well I don't want to cast doubt on that contrib node, It's just unfamiliar and I don't want to experiment with it, so I looked at an alternative approach.