Estimated Heating Oil Remaining in Tank

Goal: Estimate the amount of home heating oil remaining in my tank, based on burner runtime.

My home is heated by an oil-fired boiler. The burner on this boiler has a fixed fuel flow rate. If I can accumulate the total runtime of the burner, I can estimate how much fuel oil I’ve burned. Knowing the capacity of my fuel tank, I can estimate how much fuel I have left, and when I need to think about getting a refill.

The first step was to monitor the on/off state of the burner. The burner itself runs on regular house current (mains power.) While it’s possible to sense the 24VAC signals from the thermostats or zone controller, the burner fires based on its own internal high/low temperature settings. It can shut off while the thermostats are still calling for heat (if it’s already hot enough) and it can come on when no heat is called for (to maintain the minimum set temperature.)

For this monitoring, I took the reed switch out of a small Visonic battery-powered Zigbee door sensor and wired it to the normally open pins on a relay (contactor.) The coil on the relay is powered off the power connections on the boiler burner. When the burner is running, the relay closes the connection on the sensor, making it think the door is “closed.”

In customize.yaml I’ve defined the door sensor as device_class: power so that open/closed become on/off, and given it an icon that seems better than the default door icon:

  device_class: power
  icon: mdi:fire

Next I created two history_stats entities in configuration.yaml to store daily burner activity. The first one accumulates today’s run-time, while the second one generates one total for yesterday’s run-time.

  - platform: history_stats
    name: Burner on today
    entity_id: binary_sensor.visonic_mct_340_e_0b1243b6_1_1280
    state: 'on'
    type: time
    start: '{{ now().replace(hour=0).replace(minute=0).replace(second=0) }}'
    end: '{{ now() }}'

  - platform: history_stats
    name: Burner on yesterday
    entity_id: binary_sensor.visonic_mct_340_e_0b1243b6_1_1280
    state: 'on'
    type: time
    end: '{{ now().replace(hour=0).replace(minute=0).replace(second=0) }}'
      hours: 24

Of course, both of those values are transient. I created an input_number entry in configuration.yaml to store the cumulative amount of fuel remaining in the tank. Input_numbers are retained indefinitely and not lost between HA restarts, as long as you don’t give them an initial: value.

    name: "Oil Remaining"
    min: 0
    max: 275
    step: 0.1
    mode: box
    unit_of_measurement: 'Gallons'

For clarity, I used Configuration / Customizations to edit customize.yaml and give my new entity an icon. The best I found in the MDI list was a barrel.There are also some fuel-gage icons which you might like better.

  icon: mdi:barrel

I like to extract the burner run-time data for use in spreadsheets or other off-line analysis, so I added a file platform definition to configuration.yaml to store a daily burner firing summary:

  - platform: file
    name: burner_summary
    filename: burner_summary.txt
    timestamp: False

I created an automation in automations.yaml to
(1) Update the daily summary file with yesterday’s total burner time, and
(2) Subtract yesterday’s oil used from the cumulative input_number value.

You’ll notice that this automation is set to run at four distinct times. The first one is at 1 AM, and is really all we need. But in case HA isn’t running then, it will try again three more times during the day. There’s a condition template which prevents it from running again if it’s already run today.

The first action uses the notify service to create one new line in my burner_summary.txt file.

The second action uses the input_number.set_value service to subtract yesterday’s oil use from the oil_tank_remaining value.

In my case, I’ve estimated the burner to use about .98 gallons per hour of run time. This value is set at boiler installation time by selecting the burner nozzle. Over time these nozzles can become clogged, and should be regularly replaced. You can start with the defined value for your nozzle, and calibrate from there over time as you monitor run times and gallons delivered.

- id: id_burner_summary
  alias: Burner Summary
  - platform: time
    at: 01:00:00
  - platform: time
    at: 02:00:00
  - platform: time
    at: '14:00:00'
  - platform: time
    at: '22:00:00'
  - condition: template
    value_template: '{{ now().strftime(''%x'') != as_timestamp(states.automation.burner_summary.attributes.last_triggered)
      | timestamp_custom(''%D'') }}'
  - service: notify.burner_summary
      message: '{{ now().strftime(''%x'') }},{{ states.sensor.burner_on_yesterday.state
  - service: input_number.set_value
      entity_id: input_number.oil_tank_remaining
      value: '{{ (states("input_number.oil_tank_remaining") | float) - (states("sensor.burner_on_yesterday")
        | float * 0.98) }}'
  mode: single

Since my Lovelace skills are as weak as the rest of my HA skills, for now I’ve just created a new view in my HA dashboard with a gauge card and an entities card, both showing the input_number.oil_tank_remaining entity. The first one gives a clear visual, while the second one gives me the ability to change the value when I get a fuel delivery.

  - title: Oil Tank
    path: oil
    icon: 'mdi:barrel'
    badges: []
      - type: gauge
        entity: input_number.oil_tank_remaining
        min: 0
        max: 275
          green: 35
          yellow: 25
          red: 15
      - type: entities
          - input_number.oil_tank_remaining

My last step was to copy my burner_summary.txt file to a spreadsheet, total up the hours of run time since my last fuel delivery, subtract that from the total capacity (less air space) of my tank and plug that value into the entities card I just created.

I should point out here that the oil company won’t fill the tank to it’s rated capacity. They always leave an air space at the top for expansion, and to prevent a spill of foam caused by their high-speed pump. For my 275 gallon tank, I estimate they never fill it beyond about 230 gallons. It’s hard to estimate this amount. Sometimes the temperature difference between the new fuel and ambient air leaves a visible condensation line on the outside of the tank. There are tables you can consult to convert the height of this line to gallons or liters for your tank.


I use mdi:fireplace for the burner. It has a little flame that appears when it’s on and mdi:propane-tank-outline for the oil tank.


I looked at the propane tank but didn’t see the fireplace, thanks. There never seems to be an exact fit in the MDI library. But there’s always something you can use. Here’s what I ended up using on my home page:

The heating zones use mdi:radiator and the burner mdi:flame.

1 Like

And I completely missed the radiator. That works for my thermostat card.

Hi Tom
My HA skills aren’t that good. Can I ask where you put (which files) the 2 scripts - the one with the Triggers and the other to create the Lovelace cards?

The automation (code with the triggers) is in automations.yaml.

For the Lovelace cards I clicked the three vertical dots on the upper-right and selected Edit Dashboard. From there I clicked the “+” to create a new “view” (page.) Clicked + Add Card and added a “Gauge” card and an “Entities” card. Find the desired entities and that’s it.

The code I posted is auto-generated by the Lovelace UI editor.

I know it’s hard to find where everything goes at first, which is why I tried to be as specific as I could in the OP. Hopefully this will get you a little more comfortable with the jargon and the navigation. So much to learn!

I’m beginning to understand a bit more about the Lovelace stuff. I was struggling to tie the 2 cards together but now have found the vertical-stack. And I’ve also discovered that in edit mode, you can click on the three dots top right again and edit the whole UI structure in raw YAML speak.
Re the Automations, I can see I’ve already got some stuff in the file from some I created using a Template some while back. I’ll stick your code in there now and see where it gets me!

1 Like

Sorry to be a pain.

I already got the input_number entity oil_tank_remaining and created the vertical stacked card with the gauge and entities cards displaying on my Home screen as your description above.

I’ve now put the automation code in mostly unaltered. Created a sensor type burner_on_yesterday entity. Rebooted and ran the automation manually. It did not change the input_number.oil_tank_remaining!

I found this entry in a log. I don’t understand the syntax nor functions being used in the Condition, Action and Service so am struggling to see what is wrong. Wondering if it’s anything to do with your file output, which I have not included. I’ll continue to research it but if you have any clues…

2022-02-02 15:21:30 ERROR (MainThread) [homeassistant.components.automation.burner_summary] Burner Summary: Error executing script. Service not found for call_service at pos 1: Unable to find service notify.burner_summary
2022-02-02 15:21:30 ERROR (MainThread) [homeassistant.components.automation.burner_summary] Error while executing automation automation.burner_summary: Unable to find service notify.burner_summary


The automation is trying to send a notification to the above and failing. You can change this to notify one of your devices

That step in the automation saves a value to a text file using the notify platform. Perhaps you haven’t defined it the same way I did in the example, or there’s a typo or other error in the definition. See the notify: section in the OP.

You can do without that step in the automation, if you’re not interested in saving data to a text file.

Yes, I’ve taken out the 3 lines trying to write to the file I had not created. I don’t need it currently.

Because I’ve already run the automation, I had to change the condition to 1 == 1 temporarily to get it to work.

But nothing happened and I can see the following warning in the log which tells me why

2022-02-02 16:56:38 WARNING (MainThread) [homeassistant.helpers.template] Template warning: 'float' got invalid input 'unknown' when rendering template '{{ (states("input_number.oil_tank_remaining") | float) - (states("sensor.burner_time_yesterday") | float * 2.28) }}' but no default was specified. Currently 'float' will return '0', however this template will fail to render in Home Assistant core 2022.1

But not having learned this syntax (nor even knowing where I can find it explained) I’m not sure what is needed. Must be simple.

The 2.28 is the hourly burn rate in litres for my particular boiler according to the manual.

I’m getting a delivery of oil tomorrow, so would be good to get this working.


Sorted it now. I had the name for the burn_time_yesterday sensor wrong.

I was thrown for a while thinking it was the float or pipe that was the problem.

Thanks all.

1 Like

I’m trying to set up something similar. What are you using the “Burner on today” sensor for? And I’m thinking there should also be a way to trigger the automation based on state change, rather than time, no?

I’m using it for counting up how long the burner is running each day, which in turn tells me how much fuel it has burned each day. Or did you mean what am I using to collect the burner on/off data?

When I posted this, I was using a Zigbee Visonic MCT 340E door and temperature sensor. I de-soldered the reed switch which opens and closes when the door is opened or closed, and replaced it with a pair of wires to the contacts of a relay. The relay opened or closed the contacts when the burner fired and stopped.

Later, I wired the relay contacts directly to the GPIO pins of the RPi running HA. That works great now, although there’s been a move away from GPIO pin support which I’ll have to consider going forward.

Doesn’t the “burner on yesterday” sensor automatically tally up how much fuel was used in a day though, without the need for the “burner on today” sensor?

I like to see both; yesterday’s total, and today’s so far. Just a personal preference. This time of year I tend to look at both numbers a lot.

I started with CaptTom’s automation but quickly changed it to update the Burn Time each time the burn stopped. That way I can see the time going up during the day.
I’ve now added the cost of the oil as an input_number so it can tell me how much today’s and yesterday’s oil has cost. Done the same for my Electricity now too. Frightening! Got some nice gauges and graphs in Grafana now.


Nice! I’ve actually just set up my own git now that I’ve got mine up and running:


Nice! I’m not sure I want to know how much I’m spending on fuel though. :wink:

Cool, you’ve got some more going on than me there so I’ll have to digest that a bit and see what I can learn and copy.