I was pretty unhappy with the rudimentary calculations for vertical tanks provided above, so I threw together this customizable template (These are numbers for a 20lb tank, but can be adjusted for any dimension tank you want) for vertical tanks:
{# User supplied variables. Use the same units that the mopeka sensor is configured to report.
I use mm by default (better resolution from the sensor #}
{% set side_length = 203.0 %}
{% set tank_diameter = 304.0 %}
{% set wall_thickness = 3.175 %}
{% set measured_fill_depth = states('sensor.propane_tank_level') | float - wall_thickness%}
{% set R_tank = tank_diameter / 2 %}
{% set H_tank = side_length + R_tank %}
{# A 2:1 Ellipsoid has a c value that is half of its a/b values. A and B are the radius of the tank #}
{% set E_a = R_tank %}
{% set E_b = R_tank %}
{% set E_c = R_tank / 2 %}
{# Vertical tanks are a cylinder capped on either end with half of a 2:1 ellipsoid https://www.vcalc.com/wiki/vCalc/Ellipsoid-Volume #}
{# Calculate max tank volume in mm^3 #}
{% set hemi_ellipsoid_volume = 2 / 3 * pi * E_a * E_b * E_c %}
{% set cylinder_volume = side_length * pi * R_tank**2 %}
{% set max_volume = 2 * hemi_ellipsoid_volume + cylinder_volume %}
{# Fill volume is dependent on which region of the tank the level is in. #}
{# Create a piecewise function which regions for bottom ellipsoid, middle cylinder, or top ellipsoid #}
{% if 0 <= measured_fill_depth and measured_fill_depth <= E_c %}
{# The region where the fill level is in the bottom ellipsoid can be calculated using the volume
for an elipsoidal cap. https://www.vcalc.com/wiki/ellipsoid-cap-volume #}
{% set measured_fill_volume = pi * E_a * E_b * ((2/3 * E_c) - E_c + measured_fill_depth + ((E_c - measured_fill_depth)**3/(3 * E_c**2))) %}
{% elif E_c <= measured_fill_depth and measured_fill_depth <= E_c + side_length %}
{# The region where the fill level is in the middle cylinder can be calculated using the volume
of the bottom ellipsoid plus the filled volume of the cylinder #}
{% set measured_fill_volume = hemi_ellipsoid_volume + (measured_fill_depth - E_c) * pi * R_tank**2 %}
{% elif E_c + side_length <= measured_fill_depth and measured_fill_depth <= H_tank %}
{# The region where the fill level is in the top ellipsoid can be calculated by subtracting the volume
of an elipsoidal cap that has a height of the height of the tank minus the fill level.
https://www.vcalc.com/wiki/ellipsoid-cap-volume #}
{% set measured_fill_volume = max_volume - (pi * E_a * E_b * ((2/3 * E_c) - E_c + H_tank - measured_fill_depth + ((E_c - H_tank + measured_fill_depth)**3/(3 * E_c**2)))) %}
{% else %}
{# All other ranges are invalid, set measured_fill_volume to a value that will result in the template returning -1 #}
{% set measured_fill_volume = -max_volume / 100 %}
{% endif %}
{{ (100 * measured_fill_volume / max_volume) | round(1) }}
I also created a similar configurable template for horizontal tanks.These are numbers for a 500 gal tank, but can be adjusted for any dimension tank you want:
{# User supplied variables. Use the same units that the mopeka sensor is configured to report.
I use mm by default (better resolution from the sensor #}
{% set overall_length = 3022.6 %}
{% set overall_tank_diameter = 952.5 %}
{% set wall_thickness = 6.35 %}
{% set internal_tank_diameter = overall_tank_diameter - 2 * wall_thickness %}
{% set side_length = overall_length - overall_tank_diameter %}
{% set measured_fill_depth = states('sensor.propane_tank_level') | float - wall_thickness %}
{% set R_tank = internal_tank_diameter / 2 %}
{# Horizontal tanks are a cylinder capped on either end with hemispheres #}
{# calculate max tank volume in mm^3 #}
{% set spherical_volume = 4 / 3 * pi * R_tank**3 %}
{% set cylinder_volume = side_length * pi * R_tank**2 %}
{% set max_volume = spherical_volume + cylinder_volume %}
{# Calculate the volume of a spherical cap of height equal to the fill level: https://www.vcalc.com/wiki/Volume-of-a-Sphere-Cap #}
{% set fill_spherical_volume = pi / 3 * measured_fill_depth**2 * (3 * R_tank - measured_fill_depth) %}
{# Calculate the volume of a partial horizontal cylinder: https://www.vcalc.com/wiki/volume-of-horizontal-cylinder #}
{% set fill_cylinder_volume = side_length * (R_tank**2 * acos((R_tank - measured_fill_depth) / R_tank) - (R_tank - measured_fill_depth) * sqrt(2 * R_tank * measured_fill_depth - measured_fill_depth**2)) %}
{% set measured_fill_volume = fill_spherical_volume + fill_cylinder_volume %}
{{ (100 * measured_fill_volume / max_volume) | round(2) }}
Just make sure you enter your user dimensions in the same unit as your sensor is configured or it will not work correctly.