I think this is a pretty interesting idea that I can put to use for my smoker too so I spent a bit of time hammering this out.
I expanded a bit on what @GlennHA suggested above since there’s no real way to know the actual heat transfer rate based on the scientific stuff but we can measure the actual heating rate of the food.
Since I don’t have any food oin the smoker right now (drat! ) I had to do some simulations (which is a bit harder than it sounds).
My baseline was to sample the food temp every 5 minutes and then store those values (current and previous) to a variable (using the hass-variables custom integration). That sampling rate may be too fast depending on the size of the food you are cooking or how “low ands slow” you go so therefore may result in getting a heating rate of 0. And since division by 0 causes issues the time calculation needs to take that into account.
If you are working from actual sensors then you will need the following:
- actual food temp (measured from sensor)
- variable to hold the current and last reading of food temp (variable using hass-variables or you could use two input numbers)
- rate of change of food temp sensor (sensor value set by automation)
- desired finished temperature (constant set by input number)
- calculated finish time estimate (an input_datetime set via automation)
- two automations (one to calculate the rate of change of food temp & one to calculate the food done datetime)
To do the simulation you will need a couple of additional things:
- a counter to simulate the steadily increasing food temp that gets incremented by an automation
- an automation to increment the counter
- an input number to simulate the current food temp
The counter simulates a heating rate of 2 degrees per minute.
so all of that said here is the simulation code. You can just dump all of this into a package and everything should just work. The result of this calculation should end up setting a time that the food is done to 1 hour and 15 minutes in the future updated every 5 minutes:
counter:
food_counter:
initial: 0
step: 2
input_datetime:
food_done:
name: Estimated Time Food Will Be Done
has_date: true
has_time: true
input_number:
food_temp_current:
name: Food Current Temp
initial: 45
min: -20
max: 200
step: 1
food_temp_done:
name: Food Done Temp
initial: 195
min: 0
max: 200
step: 1
sensor:
- platform: template
sensors:
food_temp_rate:
friendly_name: Food Temp Rate
value_template: "{{ (states('variable.food_temp_history') | float - state_attr('variable.food_temp_history', 'history_01') | float) / 5 }}"
unit_of_measurement: "degrees/min"
automation:
- alias: simulate 2 degrees per minute
trigger:
- platform: time_pattern
minutes: "/1"
action:
- service: counter.increment
entity_id: counter.food_counter
- alias: calc rate of change of food temp
trigger:
- platform: time_pattern
minutes: "/5"
action:
- service: variable.set_variable
data:
variable: food_temp_history
attributes:
history_01: "{{ states('variable.food_temp_history') }}"
- service: variable.set_variable
data:
variable: food_temp_history
value: "{{ states('counter.food_counter') }}"
- alias: set time food done
trigger:
- platform: time_pattern
minutes: "/5"
action:
- service: input_datetime.set_datetime
entity_id: input_datetime.food_done
data:
timestamp: >
{% if states('sensor.food_temp_rate') | float != 0.0 %}
{{ (((states('input_number.food_temp_done') | float - states('input_number.food_temp_current') | float) / states('sensor.food_temp_rate') | float) * 60) + as_timestamp(now()) }}
{% else %}
{{ as_timestamp(now()) + 86400 }}
{% endif %}
variable:
food_temp_history:
value: 'Not set'
restore: true
attributes:
name: 'Food Temp History'
to use it with just the real sensors:
input_datetime:
food_done:
name: Estimated Time Food Will Be Done
has_date: true
has_time: true
input_number:
food_temp_done:
name: Food Done Temp
initial: 195
min: 0
max: 200
step: 1
sensor:
- platform: template
sensors:
food_temp_rate:
friendly_name: Food Temp Rate
value_template: "{{ (states('variable.food_temp_history') | float - state_attr('variable.food_temp_history', 'history_01') | float) / 5 }}"
unit_of_measurement: "degrees/min"
automation:
- alias: set sampling rate of food temp
trigger:
- platform: time_pattern
minutes: "/5"
action:
- service: variable.set_variable
data:
variable: food_temp_history
attributes:
history_01: "{{ states('variable.food_temp_history') }}"
- service: variable.set_variable
data:
variable: food_temp_history
value: "{{ states('sensor.food_temp_current') }}"
- alias: set time food done
trigger:
- platform: time_pattern
minutes: "/5"
action:
- service: input_datetime.set_datetime
entity_id: input_datetime.food_done
data:
timestamp: >
{% if states('sensor.food_temp_rate') | float != 0.0 %}
{{ (((states('input_number.food_temp_done') | float - states('sensor.food_temp_current') | float) / states('sensor.food_temp_rate') | float) * 60) + as_timestamp(now()) }}
{% else %}
{{ as_timestamp(now()) + 86400 }}
{% endif %}
variable:
food_temp_history:
value: 'Not set'
restore: true
attributes:
name: 'Food Temp History'
The division by zero issue is handled by just setting the finishing time to 24 hours from the current time because if the food temp isn’t increasing then the food will technically never get done.
This isn’t obviously going to be very precise but it will basically get you a “time to doneness” that will get more accurate as the doneness temperature is approached.
Let me knows what you think. I threw this together pretty quickly so I may have screwed something up.