Ok, here’s my yaml for my ‘Hot water litres remaining’ sensor:
- sensor:
- name: "Hot water remaining l"
unit_of_measurement: "l"
state: "{% with
traw = [float(states('sensor.temperature1'),0.0), float(states('sensor.temperature2'),0.0), float(states('sensor.temperature3'),0.0), float(states('sensor.temperature4'),0.0), float(states('sensor.temperature5'),0.0), float(states('sensor.temperature6'),0.0), float(states('sensor.temperature'),0.0)] %}
{% with
tt = 40,
tc = 20,
tlist = [(traw[0]+traw[1])/2, (traw[1]+traw[2])/2, (traw[2]+traw[3])/2, (traw[3]+traw[4])/2, (traw[4]+traw[5])/2, (traw[5]+traw[6])/2],
vlist = [32.7, 34.4, 34.4, 34.4, 34.4, 32.7],
vt=0
%}
{% if tlist[0] > tt -%}
{%- set i=0 -%}
{%- set v=(tlist[i]*vlist[i]-tt*vlist[i])/(tt-tc) + vlist[i] -%}
{%- set vt=vt+v -%}
{%- set i=1 %}
{%- if tlist[i] > tt -%}
{%- set v=(tlist[i]*vlist[i]-tt*vlist[i])/(tt-tc) + vlist[i] -%}
{%- set vt=vt+v -%}
{##,v{{i}}={{v}}##}
{%- set i=2 %}
{%- if tlist[i] > tt -%}
{%- set v=(tlist[i]*vlist[i]-tt*vlist[i])/(tt-tc) + vlist[i] -%}
{%- set vt=vt+v -%}
{##,v{{i}}={{v}}##}
{%- set i=3 %}
{%- if tlist[i] > tt -%}
{%- set v=(tlist[i]*vlist[i]-tt*vlist[i])/(tt-tc) + vlist[i] -%}
{%- set vt=vt+v -%}
{##,v{{i}}={{v}}##}
{%- set i=4 %}
{%- if tlist[i] > tt -%}
{%- set v=(tlist[i]*vlist[i]-tt*vlist[i])/(tt-tc) + vlist[i] -%}
{%- set vt=vt+v -%}
{##,v{{i}}={{v}}##}
{%- set i=5 %}
{%- if tlist[i] > tt %}
{%- set v=(tlist[i]*vlist[i]-tt*vlist[i])/(tt-tc) + vlist[i] -%}
{%- set vt=vt+v -%}
{##,v{{i}}={{v}}##}
{%- endif %}
{%- endif %}
{%- endif %}
{%- endif %}
{%- endif %}
{%- endif %}
{{vt|round(1)}}
{#
traw={{traw}}
tlist = {{tlist}}
vlist = {{vlist}}
#}
{% endwith %}
{% endwith %}"
Might look kind of scary. Not intended to be a code obfuscation competition. Sorry that there aren’t any comments, and there are a few bits of debugging helpers in there.
Basic principle is this:
Define the target temperature, in my case tt=40
(degC)
Define the cold water temperature, in my case tc=20
(degC)
Break up the tank into a series of volumes, with a sensor at the top and bottom of each of them. In my case with 7 sensors, that’s 6 sections, hemispheres at top and bottom, then 4 cylinders in between.
Estimate the geometric volume of each of these in litres (independently using a spreadsheet) and put those into a list (vlist
)
Read off the 7 sensor values, and estimate the average temperature of each section using the mean of the top and bottom sensor of that section (tlist
).
By this point you have a list of 6 volumes and a list of 6 temperatures.
Now the aim is to calculate section by section (counting down since the tank empties from the top), how much water at could be made at tt degC by mixing that section’s volume of water at that section’s temperature with tc degC water. To do this you use Richmann’s calorimetric mixing formula, to get the amount of cold water you need to add, then add the volume of that section. Adding to a running total vt
Keep doing this section by section, until you find a section that is a lower temperature than tt
. At that point you assume that this water is useless (since it’s impossible to mix it with cold water to make it reach tt
), then stop.
Then the total volume vt
is the accumulated hot water you could make from the tank.
Example: There was some sun (free solar electricity) today but not much. An automation triggered the electric heater a bit but not enough to get any ‘section’ of the tank above 40C. Then later in the day we boosted the water using the gas boiler, which got us up to approx 190L of virtual 40C water.
Displaying number of baths/showers is not shown here (I have that on a separate ESPHome display panel in the house)
My advice: overestimate on your bath/shower volume for the calculation to maximise the WAF.