Calculating Oil Consumption

I am looking for direction on how to best implement the following;

I want to be able to calculate the consumption of oil I use for heating from a known value (my oil tank). I have a device that is turned “on” when my boiler runs and in another automation system I would take the daily runtime (in hours) of this device and multiple it by the burn rate of my burner which is .85 gallons an hour. I would then take this “gallon-used” value and subtract it from the “tank-gallons” value. This would then give me the remaining “new-tank-gallons” value which would be saved as the new value of “tank-gallons” to be subtracted from the new day.

So as an example, if starting from a full tanks, the logic would look like this.

275 “tank-gallons” - “gallons-used” which is (runtime of device y for the past 24 hours x .85)

this would be the new value for “new-tank-gallons”

“new-tank gallons” would be saved as the new value of “tank-gallons” until the next days usage.

When I get an oil delivery, I would set the value of “tank-gallons” back to 275.

Thanks in advance for any help in coming up with a solution for this.

I use the following vb script to perform this currently.

Sub Main(parm as object)

dim tank = 824 'device reference for virtual tank level
dim oilburned = 924 'device reference for Gallon Used Prio Day
dim heating = 735 'Device reference for thermostat operating state
dim rate = .832 'Gallons per hour burned original .85
dim zwave = 1 'Multiplier for fine tunning
dim runtimer = “Heating” 'name of hs timer
dim heatcycles = “Heat Cycles” 'name of hs counter tracking 3 minute MOT
dim gallonsold = hs.DeviceValueEx(tank) 'Read in current gallons

dim gallons
dim gallonsUsed
dim gallonsUsedToday
dim gallonsnew

gallonsUsed = (hs.TimerValue(runtimer).totalhours * zwave) * rate
gallonsUsedToday = math.round(gallonsUsed,2)

gallons = gallonsold - gallonsUsedToday
gallonsnew = math.round(gallons,2)

hs.WriteLog (“Info”, “Number of gallons used today " + gallonsUsedToday.ToString + " Gallons.”)
hs.WriteLog (“Info”, “Old Gallons Value " + gallonsold.ToString + " Gallons.")

hs.TriggerEvent(“Stop heat timer”)

hs.SetDeviceValueByRef(tank, gallons, True)
hs.SetDeviceString(tank, gallons.ToString + " Gallons", True)

hs.SetDeviceValueByRef(oilburned, gallonsUsedToday, True)
hs.SetDeviceString(oilburned, gallonsUsedToday.ToString + " Gallons", True)

hs.WriteLog (“Info”, “Update heating oil to " + gallons.ToString + " Gallons.”)
End Sub

1 Like

How is this device represented in Home Assistant? Is it a sensor or binary_sensor?

Thanks for posting this! I’ve been toying with doing the exact same thing.

I also have a 275G oil tank, and a binary sensor on my boiler which gives me an exact on and off indication. I get the cumulative daily run time from that with a history_stats entity.

Beyond the basic calculation, I’d also like to hear more on how to zero it out when I get an oil delivery. Ideally, it would be a button in Lovelace, but I haven’t really dug into how yet.

We also need to resolve the issue of physical vs. usable capacity. The delivery driver stops the flow when the whistle stops, which in theory should leave about 10% air space in the top of the tank, but in my case is closer to 3/4 tank. I suppose I could figure out the average delivered against the calculated amount burned, and come up with a “usable” figure to input instead of 275.

It is a binary sensor. I’m using a Insteon TriggerLinc to sense when the burner is running.

Perfect. In that case you should implement what CaptTom described (because it’s exactly what I intended to suggest).

To get you started, here’s a sample configuration for a History Stats sensor:

- platform: history_stats
  name: Boiler Operation Today
  entity_id: binary_sensor.boiler
  state: 'on'
  type: time
  start: '{{ now().replace(hour=0).replace(minute=0).replace(second=0) }}'
  end: '{{ now() }}'

You can create a Template Sensor that performs the actual calculation (and it will reference the value of the History Stats sensor).

@CaptTom, I’m currently using another automation system for this function and i’m trying to get it converted to HA. The original calculations and variables were done in vb. I always used 275 as the tank value and just subtracted the daily usage from there. When the value got down to 80, it would trigger a pushover alert to order oil. I never got into a situation where I ran out. When I received a delivery, I would set a virtual device to full, which in turn set the tank variable back to 275. I also displayed the calculated remaining gallons on my kiosk around the house so I always could just look at the value. I did not try to be exact in my computations, just good enough so I did not have to look at the gauge on the tank.


So that gets me the daily run time, how do I then get the gallons used for the day which in my case would be “Boiler Operation Today” x .85 ? Where .85 is the gallons used per hour. Then I need to take that value and subtract it from “tank” which will initially have a value of 275.

As mentioned, the actual calculation is performed by a Template Sensor. However, that’s just one way to do it.

Just brain-storming here but another way would be to create an automation triggered by the History Stats sensor. It computes the new tank level and uses it to set, say, an input_number. As the value of the History Stats sensor increases, the input_number’s value decreases (from its maximum value of 275). Whenever you get an oil delivery you simply drag the input_number’s slider to its max value. Anyway, just a thought.

1 Like

Another way to do this is to set an alert after the boiler has been on for X amount of time. A 275 with a .85 burn rate would allow the burner to operate for ~320 hours if it were to run empty. So just back out your minimum oil level and you’ll have your runtime.

@123, I appreciate the help. HA is all new to me and I am still finding my way around it. Could you provide an example to the Template Sensor?

I have oil and I’m planning on going the runtime route. I’ve only just set my runtime sensors. I plan to use this integration. It tracks runtime to replace a filter in my case it will be an oil delivery. I used the sensor examples form his yaml file.

@Mikefila, thanks for posting your suggestion. I will take a look and see if I can take that approach.

I’m still very new at Home Assistant and still learning the “sensor”, “template” and yaml way of doing things.

I do appreciate the help.


I should mention that I’m on auto delivery, that for the most part are right on time. I wanted this as a backup notification. A visual count on your dash board that you’ll notice everyday might be a better option then just an automation that’s working in the background.

I also plan to add a vibration sensor to the fill line. To try and auto reset from the disturbance of a delivery.

I call for my deliveries and use the pushover notification as a backup to when I need to call for delivery. It’s set to 80 gallons or less so I always have some time. The visual display on my Kiosk is what I usually check daily along with the weather.

Do you use node red? I’m sure there is a way to set this up in yaml but it’s beyond me. This flow checks to see if the furnace is on every 15 seconds. And for each consecutive 15 seconds it is on, it subtracts 0.00357gal from 275.

It exposes a sensor to HA that will show a count down from 275 albeit with a lot of decimal places. It’s ruff but it’s something to start with.

[{"id":"bd2250f5.562288","type":"counter","z":"7d105d71.cc18e4","name":"oil counter","init":"275","step":"0.00357","lower":"","upper":"","mode":"decrement","outputs":2,"x":750,"y":920,"wires":[["2a4aa0d1.e11918"],[]]},{"id":"2a4aa0d1.e11918","type":"ha-entity","z":"7d105d71.cc18e4","name":"Oil usage","server":"7c85afe1.bbea8","version":1,"debugenabled":true,"outputs":1,"entityType":"sensor","config":[{"property":"name","value":"oil"},{"property":"device_class","value":""},{"property":"icon","value":""},{"property":"unit_of_measurement","value":"gal"}],"state":"payload","stateType":"msg","attributes":[{"property":"msg.count","value":"","valueType":"num"}],"resend":true,"outputLocation":"","outputLocationType":"none","inputOverride":"allow","outputOnStateChange":false,"outputPayload":"$entity().state ? \"on\": \"off\"","outputPayloadType":"jsonata","x":1000,"y":920,"wires":[[]]},{"id":"7a32a30c.d5ba2c","type":"poll-state","z":"7d105d71.cc18e4","name":"Is furnace on?","server":"7c85afe1.bbea8","version":1,"exposeToHomeAssistant":false,"haConfig":[{"property":"name","value":""},{"property":"icon","value":""}],"updateinterval":"15","updateIntervalUnits":"seconds","outputinitially":false,"outputonchanged":false,"entity_id":"binary_sensor.furnace","state_type":"str","halt_if":"on","halt_if_type":"str","halt_if_compare":"is","outputs":2,"x":530,"y":920,"wires":[["bd2250f5.562288"],[]]},{"id":"7c85afe1.bbea8","type":"server","name":"Home Assistant","legacy":false,"addon":true,"rejectUnauthorizedCerts":true,"ha_boolean":"y|yes|true|on|home|open","connectionDelay":true,"cacheJson":true}]

I use this:
Integration - Riemann sum integral - Home Assistant (
It sums up the time and calculates the total. Template sensor *.85 and you get the oil used, another template sensor and subtract it from the full tank capacity and you’ve got a fuel remaining sensor.

1 Like

Maybe someone can help explain what I’m missing here. It seems like it should be far simpler than some of the solutions above would suggest, although I suppose a lot of it is just using whatever you have available and are familiar with.

I use a history_stats sensor to sum up my daily oil burner 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() }}'

(I wired the reed switch of a Visonic door sensor to a relay which comes on when the burner fires.)

It seems to me the math would be simple:
(Burner on today x Gallons per hour) + (Gallons used previous days) = Gallons used total.

Subtract that from tank capacity to get gallons remaining.

The only problem I see is keeping track of gallons used to date. I assume HA has a way to store a variable which would persist between reboots, but I haven’t looked at the best method of doing that yet.

Then I’d need to learn about user input via Lovelace, another thing I’ve never tried but I know is possible. That would allow me to reset the variable when fuel is delivered.

I just want to make sure I’m not missing something obvious before going down that path. So many fun things to try with HA, and only so many hours in a day!

1 Like

Use either an input_text or an input_number. Their states are stored when updated and restored on startup.

1 Like

I like the idea of the count down so I cleaned this up if anyone wants to use node red. It creates a sensor that will count down from tank size. Then a reset switch exposed to HA.

To use change in oil counter node. Tank size currently set at 550. Calculate how much oil you burn in 15 seconds, enter that under default step.

[{"id":"43680138.7be8c8","type":"inject","z":"7d105d71.cc18e4","name":"Test","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"on","payloadType":"str","x":350,"y":900,"wires":[["bd2250f5.562288"]]},{"id":"bd2250f5.562288","type":"counter","z":"7d105d71.cc18e4","name":"oil counter","init":"550","step":"0.0104","lower":"","upper":"","mode":"decrement","outputs":2,"x":730,"y":900,"wires":[["43a6f30c.96e75c"],[]]},{"id":"2a4aa0d1.e11918","type":"ha-entity","z":"7d105d71.cc18e4","name":"Oil usage","server":"7c85afe1.bbea8","version":1,"debugenabled":true,"outputs":1,"entityType":"sensor","config":[{"property":"name","value":"oil"},{"property":"device_class","value":""},{"property":"icon","value":""},{"property":"unit_of_measurement","value":"gal"}],"state":"payload","stateType":"msg","attributes":[{"property":"msg.count","value":"","valueType":"num"}],"resend":true,"outputLocation":"","outputLocationType":"none","inputOverride":"allow","outputOnStateChange":false,"outputPayload":"$entity().state ? \"on\": \"off\"","outputPayloadType":"jsonata","x":1160,"y":900,"wires":[[]],"info":"decimal: 2"},{"id":"7a32a30c.d5ba2c","type":"poll-state","z":"7d105d71.cc18e4","name":"Is furnace on?","server":"7c85afe1.bbea8","version":1,"exposeToHomeAssistant":false,"haConfig":[{"property":"name","value":""},{"property":"icon","value":""}],"updateinterval":"15","updateIntervalUnits":"seconds","outputinitially":false,"outputonchanged":false,"entity_id":"binary_sensor.furnace","state_type":"str","halt_if":"on","halt_if_type":"str","halt_if_compare":"is","outputs":2,"x":530,"y":860,"wires":[["bd2250f5.562288"],[]]},{"id":"ae473a86.d9996","type":"ha-entity","z":"7d105d71.cc18e4","name":"HA reset","server":"7c85afe1.bbea8","version":1,"debugenabled":false,"outputs":2,"entityType":"switch","config":[{"property":"name","value":"Tank reset"},{"property":"device_class","value":""},{"property":"icon","value":""},{"property":"unit_of_measurement","value":""}],"state":"payload","stateType":"msg","attributes":[],"resend":true,"outputLocation":"","outputLocationType":"none","inputOverride":"allow","outputOnStateChange":true,"outputPayload":"","outputPayloadType":"none","x":340,"y":960,"wires":[["dec3ceb1.0b3c08"],["2274d86c.b1991"]]},{"id":"2274d86c.b1991","type":"function","z":"7d105d71.cc18e4","name":"reset message ","func":"msg.reset = true;\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","x":540,"y":940,"wires":[["bd2250f5.562288"]]},{"id":"dec3ceb1.0b3c08","type":"delay","z":"7d105d71.cc18e4","name":"","pauseType":"delay","timeout":"750","timeoutUnits":"milliseconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"x":530,"y":980,"wires":[["aeebe36e.f42be8"]]},{"id":"aeebe36e.f42be8","type":"api-call-service","z":"7d105d71.cc18e4","name":"Turn off reset switch","server":"7c85afe1.bbea8","version":1,"debugenabled":false,"service_domain":"switch","service":"turn_off","entityId":"switch.tank_reset","data":"","dataType":"jsonata","mergecontext":"","output_location":"","output_location_type":"none","mustacheAltTags":false,"x":740,"y":980,"wires":[[]]},{"id":"43a6f30c.96e75c","type":"change","z":"7d105d71.cc18e4","name":"set to 2 decimal points","rules":[{"t":"set","p":"payload","pt":"msg","to":"$round(payload, 2)","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":960,"y":900,"wires":[["2a4aa0d1.e11918"]]},{"id":"7c85afe1.bbea8","type":"server","name":"Home Assistant","legacy":false,"addon":true,"rejectUnauthorizedCerts":true,"ha_boolean":"y|yes|true|on|home|open","connectionDelay":true,"cacheJson":true}]
1 Like

@CaptTom, your approach is similar to what I use currently in Homeseer using a vb script and some virtual devices.

@Mikefila, your approach is interesting and I tried loading it into my Node-Red but I’m getting this error " Attention: This node requires Node-RED custom integration to be installed in Home Assistant for it to function." when I try to edit some of the nodes. I installed the custom integration as outlined. I’m going to play with it some more. The value you have of in “oil counter” of .0104, is that the burn rate of your burner for 15 seconds?