# Wood-oven w/ water heater: calculate energy transfer

Hi guys, I am currently working on calculating the Energy (Watt-hors) my wood oven transfers to the H/P reservoir.

Basic system:

• Heat Pumpe, 3.6kW el. / 10kW heat power, ground-source, fed by PV
• Wood oven with water jacket, generates 15kW of heat of which 5kW go into the room’s air, 10kW go into the water
• Heat reservoir, 1.000l of water, feeding my entire home’s heating and hot water
• Pump (1800lpm) and 3-way mixer, PID-controlled oven influx temperature of min. 60°C (to avoid soot) - three temp readings done by a Shelly UNI (oven return, oven feed - mixed, reservoir feed pre-mixer)
• Heat reservoir feed temperature from oven: 64-80°C

Scope:

• Calculate the transfer ratio from the total oven water throughput into the heat reservoir in percent, based on three temperatures (oven input, oven output, heat reservoir cold water input)
• Calculate the energy transfer (in kWh), based on water temperature, pump throughput and transfer ration
• Display both results in HASS

Problem:

• Ratio calculated but, cannot calc the energy transfer

Function Code snippets:

• Transfer Ratio
``````// Assuming msg.payload contains the temperature sensor data

// Calculate the heat transfer ratio
var heatTransferRatio = 100 - ((temperature2 - temperature3) / (temperature1 - temperature3)) * 100;

// Round the result to the nearest whole number
heatTransferRatio = Math.round(heatTransferRatio);

// Set the heat transfer ratio as a global variable
global.set("heatTransferRatio", heatTransferRatio);

// Set the heat transfer ratio as the payload
return msg;
``````
• Energy Transfer:
``````// Assuming msg.payload contains the temperature sensor data

// Pump flow rate in liters per minute
var pumpFlowRateLitersPerMinute = 1800;

// Specific heat capacity of water in J/(kg·°C)
var specificHeatCapacityOfWater = 4186; // This is an approximate value, you may need to adjust it

// Density of water in kg/L
var densityOfWater = 1; // This is an approximate value for room temperature

// Access the heat transfer ratio from the global variable
var heatTransferRatio = global.get(heatTransferRatio) || 0;

// Calculate the mass of water in kg
var massOfWater = (heatTransferRatio / 100) * pumpFlowRateLitersPerMinute * densityOfWater;

// Calculate the energy transferred in joules
var energyJoules = massOfWater * specificHeatCapacityOfWater * temperature1;

// Convert joules to watts
var energyWatts = energyJoules / 60; // Assuming 60 seconds in a minute

// Set the result as the payload
return msg;
``````

Won’t work. “Error: Invalid context key”
I also tried to join the percent and oven-temperature into one message to feed the function node. No success.

Any idea?

Update:
I did join all three parameters into an array and now the delivered heat in kiloWatts works:

``````// Assuming msg.payload contains the temperature sensor data

// Assuming msg.payload is an array of numbers

// Calculate the Temperature differenceputput-input
var deltaTemperature = numbersArray[0] - numbersArray[1];

// Pump flow rate in liters per minute (based on 1800 litres per hour)
var pumpFlowRateLitersPerMinute = 30;

// Specific heat capacity of water in J/(kg·°C)
var specificHeatCapacityOfWater = 4186; // This is an approximate value, you may need to adjust it

// Density of water in kg/L
var densityOfWater = 1; // This is an approximate value for room temperature

// Access the heat transfer ratio from the global variable
var heatTransferRatio = numbersArray[2];

// Calculate the mass of water in kg
var massOfWater = (heatTransferRatio / 100) * pumpFlowRateLitersPerMinute * densityOfWater;

// Calculate the energy transferred in joules
var energyJoules = massOfWater * specificHeatCapacityOfWater * deltaTemperature;

// Convert joules to watts
var powerWatts = energyJoules / 60; // Assuming 60 seconds in a minute

// Convert watts to kilowatts
var powerkiloWatts = powerWatts / 1000;

// Set the result as the payload
msg.topic = 'OvenPowerkiloWatts'

return msg;
``````

Next challenge is on, how to display this in the HASSio dashboard as a gauge.
I’m still fiddling around with the “call service” node without success.

Now,

here’s the entire setup for Node-Red, based on a 3-way mixer with temperature adaptation to have the oven water inlet to be always above 55°C to prevent soot.
For the ease of calculation, all variables were converted to global ones plus, sensors were set to display core values in HASSio:

`

`> [{"id":"49ac2d3a2c8cd6dc","type":"group","z":"e3f30e09f01c979f","name":"Ofen Sensorgruppe","style":{"label":true,"stroke":"#ff0000","color":"#000000"},"nodes":["f7101a878906ff13","f548f031284cf8e4","8d68417fa0d402de","08ae38f779cf334d","99819d57d53cae5d","5d35d585a01c8fbd","0f4bc48f32920fa2","30a68a60b4b5a624","fdc068a15fd7aeac","49c447c109d24801","7e35282b10bace08","87559157a30aa38d","49e1e0eaae5987a5"],"x":34,"y":699,"w":1352,"h":262},{"id":"f7101a878906ff13","type":"poll-state","z":"e3f30e09f01c979f","g":"49ac2d3a2c8cd6dc","name":"Pump-State","server":"61c89149.6b842","version":3,"exposeAsEntityConfig":"","updateInterval":"10","updateIntervalType":"num","updateIntervalUnits":"seconds","outputInitially":false,"outputOnChanged":false,"entityId":"switch.shellypro4pm_0_5_3_e_switch_3","stateType":"habool","ifState":"","ifStateType":"str","ifStateOperator":"is","outputs":1,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"entityState"},{"property":"data","propertyType":"msg","value":"","valueType":"entity"},{"property":"topic","propertyType":"msg","value":"","valueType":"triggerId"}],"x":130,"y":740,"wires":[["f548f031284cf8e4"]]},{"id":"f548f031284cf8e4","type":"function","z":"e3f30e09f01c979f","g":"49ac2d3a2c8cd6dc","name":"Set.global PumpSwitch","func":"// Assuming msg.payload contains a boolean value\nvar boolValue = msg.payload; // Replace this with your boolean value\n\n// Convert boolean to 1 or 0 using a ternary operator\nvar PumpSwitch = boolValue ? 1 : 0;\n\n// Update the global variable with the new value\nglobal.set(\"PumpSwitch\", PumpSwitch);\n\n// Pass the updated value to the next node (optional)\nmsg.payload = global.get(\"PumpSwitch\");\n\n\nreturn msg;\n","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":390,"y":740,"wires":[[]]},{"id":"8d68417fa0d402de","type":"function","z":"e3f30e09f01c979f","g":"49ac2d3a2c8cd6dc","name":"Oven Power transfer","func":"// Access individual temperature and pump status from global variables\nvar temperatureOutput = global.get(\"temperatureOutput\") || 0;\nvar temperatureInput = global.get(\"temperatureInput\") || 0;\nvar heatTransferRatio = global.get(\"heatTransferRatio\") || 0;\nvar PumpSwitch = global.get(\"PumpSwitch\") || 0;\n\n// Calculate the Temperature difference output-input\nvar deltaTemperature = temperatureOutput - temperatureInput;\n\n// Pump flow rate in liters per minute (based on 1800 liters per hour)\nvar pumpFlowRateLitersPerMinute = 30;\n\n// Specific heat capacity of water in J/(kg·°C)\nvar specificHeatCapacityOfWater = 4186; // This is an approximate value, you may need to adjust it\n\n// Density of water in kg/L\nvar densityOfWater = 1; // This is an approximate value for room temperature\n\n// Calculate the mass of water in kg\nvar massOfWater = (heatTransferRatio / 100) * pumpFlowRateLitersPerMinute * densityOfWater;\n\n// Calculate the energy transferred in joules\nvar energyJoules = massOfWater * specificHeatCapacityOfWater * deltaTemperature;\n\n// Convert joules to watts\nvar powerWatts = energyJoules / 60; // Assuming 60 seconds in a minute\n\n// round the output to 3 decimals\npowerWatts = powerWatts.toFixed(0);\n\n// convert to number\npowerWatts = parseFloat(powerWatts);\n\n// multiply output with PumpSwitch Status\npowerWatts = powerWatts * PumpSwitch;\n\n// limit to only positive values including zero\npowerWatts = Math.max(0, powerWatts);\n\n// Set the result as the payload\nmsg.payload = powerWatts;\nmsg.topic = 'OvenPowerWatts';\n\nreturn msg;\n","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":920,"y":860,"wires":[["49e1e0eaae5987a5"]]},{"id":"08ae38f779cf334d","type":"poll-state","z":"e3f30e09f01c979f","g":"49ac2d3a2c8cd6dc","name":"Ofen Einspeisung","server":"61c89149.6b842","version":3,"exposeAsEntityConfig":"","updateInterval":"10","updateIntervalType":"num","updateIntervalUnits":"seconds","outputInitially":true,"outputOnChanged":false,"entityId":"sensor.shelly_uni_0_6_6_c_ladepumpe_temperature_1","stateType":"num","ifState":"","ifStateType":"str","ifStateOperator":"is","outputs":1,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"entityState"},{"property":"data","propertyType":"msg","value":"","valueType":"entity"},{"property":"topic","propertyType":"msg","value":"","valueType":"triggerId"}],"x":140,"y":800,"wires":[["5d35d585a01c8fbd"]]},{"id":"99819d57d53cae5d","type":"poll-state","z":"e3f30e09f01c979f","g":"49ac2d3a2c8cd6dc","name":"Speicher Vorlauf","server":"61c89149.6b842","version":3,"exposeAsEntityConfig":"","updateInterval":"10","updateIntervalType":"num","updateIntervalUnits":"seconds","outputInitially":true,"outputOnChanged":false,"entityId":"sensor.shelly_uni_0_6_6_c_ladepumpe_temperature_3","stateType":"num","ifState":"","ifStateType":"str","ifStateOperator":"is","outputs":1,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"entityState"},{"property":"data","propertyType":"msg","value":"","valueType":"entity"},{"property":"topic","propertyType":"msg","value":"","valueType":"triggerId"},{"property":"parts","propertyType":"msg","value":"3","valueType":"num"}],"x":140,"y":860,"wires":[["0f4bc48f32920fa2"]]},{"id":"5d35d585a01c8fbd","type":"function","z":"e3f30e09f01c979f","g":"49ac2d3a2c8cd6dc","name":"Set.global temperatureOutput","func":"// Assuming msg.payload contains the sensor output\nvar sensorValue = msg.payload;\n\n// Update the global variable with the new value\nglobal.set(\"temperatureOutput\", sensorValue);\n\n// Pass the updated value to the next node (optional)\nmsg.payload = global.get(\"temperatureOutput\");\n\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":410,"y":800,"wires":[[]]},{"id":"0f4bc48f32920fa2","type":"function","z":"e3f30e09f01c979f","g":"49ac2d3a2c8cd6dc","name":"Set.global temperatureInput","func":"// Assuming msg.payload contains the sensor output\nvar sensorValue = msg.payload;\n\n// Update the global variable with the new value\nglobal.set(\"temperatureInput\", sensorValue);\n\n// Pass the updated value to the next node (optional)\nmsg.payload = global.get(\"temperatureInput\");\n\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":400,"y":860,"wires":[[]]},{"id":"30a68a60b4b5a624","type":"inject","z":"e3f30e09f01c979f","g":"49ac2d3a2c8cd6dc","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"10","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":670,"y":840,"wires":[["8d68417fa0d402de","7e35282b10bace08"]]},{"id":"fdc068a15fd7aeac","type":"poll-state","z":"e3f30e09f01c979f","g":"49ac2d3a2c8cd6dc","name":"Ofen Vorlauf","server":"61c89149.6b842","version":3,"exposeAsEntityConfig":"","updateInterval":"10","updateIntervalType":"num","updateIntervalUnits":"seconds","outputInitially":true,"outputOnChanged":false,"entityId":"sensor.shelly_uni_0_6_6_c_ladepumpe_temperature_2","stateType":"num","ifState":"","ifStateType":"str","ifStateOperator":"is","outputs":1,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"entityState"},{"property":"data","propertyType":"msg","value":"","valueType":"entity"},{"property":"topic","propertyType":"msg","value":"","valueType":"triggerId"},{"property":"parts","propertyType":"msg","value":"2","valueType":"num"}],"x":130,"y":920,"wires":[["49c447c109d24801"]]},{"id":"49c447c109d24801","type":"function","z":"e3f30e09f01c979f","g":"49ac2d3a2c8cd6dc","name":"Set.global ovenInput","func":"// Assuming msg.payload contains the sensor output\nvar sensorValue = msg.payload;\n\n// Update the global variable with the new value\nglobal.set(\"ovenInput\", sensorValue);\n\n// Pass the updated value to the next node (optional)\nmsg.payload = global.get(\"ovenInput\");\n\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":380,"y":920,"wires":[[]]},{"id":"7e35282b10bace08","type":"function","z":"e3f30e09f01c979f","g":"49ac2d3a2c8cd6dc","name":"Set.global heatTransferRatio","func":"// Assuming msg.payload contains the temperature sensor data\n// Access temperature values from global variables\nvar temperature1 = global.get(\"temperatureOutput\") || 0;\nvar temperature2 = global.get(\"ovenInput\") || 0;\nvar temperature3 = global.get(\"temperatureInput\") || 0;\nvar PumpSwitch = global.get(\"PumpSwitch\") || 0;\n\n// var temperature1 = msg.payload[\"sensor.shelly_uni_0_6_6_c_ladepumpe_temperature_1\"] || 0; // Temperature from sensor\n// var temperature2 = msg.payload[\"sensor.shelly_uni_0_6_6_c_ladepumpe_temperature_2\"] || 0;\n// var temperature3 = msg.payload[\"sensor.shelly_uni_0_6_6_c_ladepumpe_temperature_3\"] || 0;\n\n// Calculate the heat transfer ratio\nvar heatTransferRatio = 100 - ((temperature2 - temperature3) / (temperature1 - temperature3)) * 100;\n\n// Round the result to the nearest whole number\n// heatTransferRatio = Math.round(heatTransferRatio);\nheatTransferRatio = heatTransferRatio.toFixed(1);\n\n// Clamp the value between 0 and 100\nheatTransferRatio = Math.min(100, Math.max(0, heatTransferRatio));\n\n// Only set value when oven is burning\nheatTransferRatio = PumpSwitch * heatTransferRatio;\n\n// Set the heat transfer ratio as a global variable\nglobal.set(\"heatTransferRatio\", heatTransferRatio);\n\n// Set the heat transfer ratio as the payload\nmsg.payload = heatTransferRatio;\nmsg.topic = \"heatTransferRatio\"\n\nreturn msg;\n","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":940,"y":800,"wires":[["87559157a30aa38d"]]},{"id":"87559157a30aa38d","type":"ha-sensor","z":"e3f30e09f01c979f","g":"49ac2d3a2c8cd6dc","name":"sensor.heatTransferRatio","entityConfig":"e5aa6fef707c2d73","version":0,"state":"payload","stateType":"msg","attributes":[],"inputOverride":"allow","outputProperties":[],"x":1230,"y":800,"wires":[[]]},{"id":"49e1e0eaae5987a5","type":"ha-sensor","z":"e3f30e09f01c979f","g":"49ac2d3a2c8cd6dc","name":"sensor.OvenPowerTransfer","entityConfig":"ce391c35b6eb8d0d","version":0,"state":"payload","stateType":"msg","attributes":[],"inputOverride":"allow","outputProperties":[],"x":1240,"y":860,"wires":[[]]},{"id":"61c89149.6b842","type":"server","name":"Home Assistant","addon":true},{"id":"e5aa6fef707c2d73","type":"ha-entity-config","server":"61c89149.6b842","deviceConfig":"","name":"sensor.HeatTransferRatio","version":"6","entityType":"sensor","haConfig":[{"property":"name","value":"sensor.HeatTransferRatio"},{"property":"icon","value":"mdi:label-percent-outline"},{"property":"entity_picture","value":""},{"property":"entity_category","value":""},{"property":"device_class","value":"power_factor"},{"property":"unit_of_measurement","value":"%"},{"property":"state_class","value":"measurement"}],"resend":true,"debugEnabled":false},{"id":"ce391c35b6eb8d0d","type":"ha-entity-config","server":"61c89149.6b842","deviceConfig":"","name":"sensor.OvenPowerTransfer","version":"6","entityType":"sensor","haConfig":[{"property":"name","value":"sensor.OvenPowerTransfer"},{"property":"icon","value":"mdi:gas-burner"},{"property":"entity_picture","value":""},{"property":"entity_category","value":""},{"property":"device_class","value":"power"},{"property":"unit_of_measurement","value":"W"},{"property":"state_class","value":"measurement"}],"resend":false,"debugEnabled":false}]`

`

Now, using the Riemann-integral the energy my wood-fired oven transfers to the house is easily calculated.

Nevertheless, I still cannot have this “oven” being integrated into the ENERGY-Dashborad - it simply won’t show up.
Any idea what needs to be configured?