Uneven cistern volume calculation based on sensor

Hello,
i´m quite new to HA migrating from openHab so please bear with me.
I have a cistern with uneven walls. In the cistern there is a KNX sensor where i get readings of the pressure in mA. I already calculate the height of the water level with this template

    {% set max_height_waterlevel = 1200 %}
    {% set mA_at_min_waterlevel = 4 %}
    {% set mA_at_max_waterlevel = 11.90 %}
    {% set measured_mA = states('sensor.fullstand_zisterne') | float %}
    {% set height= (max_height_waterlevel / (mA_at_max_waterlevel - mA_at_min_waterlevel) * (measured_mA - mA_at_min_waterlevel))%}
    {{ height | round(0, default = 0)}}

Now my next step would be the volume in liter. For that i have a literingtable of the cistern from the manufacturer

    {% set literingTable = [
            { "height": 100, "liter": 417},
            { "height": 200, "liter": 946},
            { "height": 300, "liter": 1517},
            { "height": 400, "liter": 2145},
            { "height": 500, "liter": 2786},
            { "height": 600, "liter": 3413},
            { "height": 700, "liter": 3972},
            { "height": 800, "liter": 4524},
            { "height": 900, "liter": 4850},
            { "height": 1000, "liter": 4893},
            { "height": 1100, "liter": 4958},
            { "height": 1200, "liter": 5004} 
        ] %}

Now im missing the step how to calculate the volume. I found a lot of examples with ESPHome where i can define datapoints etc. but now im stuck, maybe you can give me a hint how i can solve this issue.

thanks
Markus

How accurate do you need it?
You could just round it to the closest 100 and get the value according to the table.

{% set literingTable = {
            "0": 0,
            "100": 417,
            "200": 946,
            "300": 1517,
            "400": 2145,
            "500": 2786,
            "600": 3413,
            "700": 3972,
            "800": 4524,
            "900": 4850,
            "1000": 4893,
            "1100": 4958,
            "1200": 5004,
            "1300": "max"} 
         %}

{% set n = 435 %}
{% set num = ((n/100)|round(0)*100) %}
{{ literingTable[num|string]}}-{{ literingTable[(num+100)|string]}}
# 2147-2786

Is this good enough?

Thanks for the idea, but especially at lower heights the steps in volume are pretty big which is not accurate enough. i know the calculation is not that easy.

Ok…

What about this?
It’s linear and that is probably as good as we can do.

{% set literingTable = {
            "0": 0,
            "100": 417,
            "200": 946,
            "300": 1517,
            "400": 2145,
            "500": 2786,
            "600": 3413,
            "700": 3972,
            "800": 4524,
            "900": 4850,
            "1000": 4893,
            "1100": 4958,
            "1200": 5004} 
         %}

{% set n = 435 %}
{% set num = ((n/100)|int*100) %}
{% if n < 1200 %}
  {{ literingTable[num|string] + (literingTable[(num+100)|string] - literingTable[num|string]) * ((n/100)- (n/100)|int) }}
{% else %}
  5004
{% endif %}
# 2369.35

It would be a lot easier to use this:

The only issue with this is this part:

This will probably skew the results a bit since the data isn’t linear between 0 and 1200.

Does not preclude you from choosing higher orders (as long as you have enough points):

You need at least n+1 points where n is the degree/order of the polynomial.

But even with that you still don’t get a good result.
Perhaps I’m doing something wrong?

But using this:

compensation:
  tank:
    source: input_number.tank
    degree: 2
    data_points:
      - [0, 0,]
      - [100, 417]
      - [200, 946]
      - [300, 1517]
      - [400, 2145]
      - [500, 2786]
      - [600, 3413]
      - [700, 3972]
      - [800, 4524]
      - [900, 4850]
      - [1000, 4893]
      - [1100, 4958]
      - [1200, 5004]

I get pretty darn close at 100

But higher up it drifts off

The issue is that you need to restart each time you make a change in the compensation, it’s not part of what you can quick reload it seems.

You have an extra comma there.

Excel says that a degree 5 polynomial is an almost exact fit.

Degree 3 is ok

Degree 2 is not good:

It seems to work fine.
The “worst” offset seems to be at 1000. It’s almost at the 1100 value.

And it never gets full

But then again when the tank is more than half full then I guess the accuracy isn’t that important.

It’s odd that it worked with the extra comma there. But it also seems it didn’t make any difference.
I guess that’s the only place in yaml where it doesn’t matter if you make an error :smiley:

Yeah it would show up as a list of 3 elements with the third element being null. petro’s code would only need to look at the first two elements, so any further elements in the list would be ignored.

Petro’s code? What have I missed?

He wrote the compensation integration.

1 Like

Wow, thank you all. I created the compensation and it works.

1 Like

I did a test to see what the values are at a few heights.

height	calculated	compensation   From table
0       0            -14.62        0
1	    4.17	     -9.76	       
5	    20.85	     9.62	
10	    41.7	     33.68	
20	    83.4	     81.32	
50	    208.5	     221.53	
75	    312.75	     336.92	
100	    417.0	     452.63	        417
150	    681.5	     689.83	
200	    946.0	     940.6	        946
250	    1231.5	     1209.14	
300	    1517.0	     1496.79	    1517
350	    1831.0	     1802.44	
400	    2145.0	     2122.94	    2145
450	    2465.5	     2453.46	
500	    2786.0	     2787.95	    2786
600	    3413.0	     3440.81	    3413
700	    3972.0	     4023.5	        3972
800	    4524.0	     4483.94	    4524
900	    4850.0	     4788.91	    4850
1000	4893.0	     4936.98	    4893
1100	4958.0	     4971.43	    4958
1200	5004	     4993.15	    5004

What degree polynomial did you use?

5 as you suggested