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:
binary_sensor.visonic_mct_340_e_0b1243b6_1_1280:
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) }}'
duration:
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.
input_number:
oil_tank_remaining:
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.
input_number.oil_tank_remaining:
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:
notify:
- 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
trigger:
- 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:
- condition: template
value_template: '{{ now().strftime(''%x'') != as_timestamp(states.automation.burner_summary.attributes.last_triggered)
| timestamp_custom(''%D'') }}'
action:
- service: notify.burner_summary
data_template:
message: '{{ now().strftime(''%x'') }},{{ states.sensor.burner_on_yesterday.state
}}'
- service: input_number.set_value
target:
entity_id: input_number.oil_tank_remaining
data_template:
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: []
cards:
- type: gauge
entity: input_number.oil_tank_remaining
min: 0
max: 275
severity:
green: 35
yellow: 25
red: 15
- type: entities
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.