Mais que um projeto, per se, trata-se de um conceito-ideia que permite, através da conjugação de vários sensores e de templates, saber quanto tempo levará um termoacumulador a aquecer a água - independentemente da temperatura inicial e da final.
O conceito baseia-se nas experiências de Joule que comprovou que são necessários 4,186 J/g∙°C para aquecer 1 cal/g de água.
Num sentido prático, suponhamos que temos um termoacumulador com 80L de capacidade e uma resistência de 1500W.
A temperatura inicial é de 30ºC e a final (pretendida) é de 60ºC.
A fórmula para saber quanto tempo levará ditos 80L a alcançarem a temperatura final representa-se por:
Segundos = 4.186J/g∙°C ( Volume/g [ temperatura final - temperatura inicial]) / Wattage
4.186J/g∙°C ( 80000g [60ºC-30ºC]) / 1500W = 6698 segundos
Ou seja, algo como: 1h:51m:38s
Aplicando isso ao HA, podemos recorrer a dois tipos de sensores:
Sonda termoacumulador - temperatura em tempo real
Tomata inteligente/dados do próprio termoacumulador/etc./definir potência fixa - Potência/consumo
ex:
template:
- sensor:
- name: "Time Heater Seconds"
unique_id: time_heater_seconds
state: >
{{ (4.186*(80000*(60-states('sensor.temperature)|float(0)))/states('sensor.heater_watts')|float(0)) | round(0) }}
availability: "{{ states('sensor.heater_watts') | float > 0 }}"
Dessa forma conseguimos saber, de forma constante, quanto tempo levará a água a aquecer - a qualquer momento.
No meu caso, tratando-se de um termoacumulador antigo, estou a utilizar como sensores um Shelly 1PM, como sonda, e os dados (FreeDS - MQTT - freeds_XXXX/calcWatts) vindos do gestor de excedentes; o que permite recolher os dados, não só com a potência máxima (ex: 1500W) mas também com, e apenas, o recurso dos excedentes (ex: 500W, 650W, 1400W, etc.)
Se preferirem que os dados venham em HH:MM:SS, podem alterar o template para:
- sensor:
- name: "Time Heater - HH:MM:SS"
unique_id: time_heater_hms
state: >
{{ (4.186*(80000*(60-states('sensor.temperature')|float(0)))/states('sensor.heater_watts')|float(0)) | round(0) | int | timestamp_custom('%H:%M:%S' , false) }}
availability: "{{ states('sensor.heater_watts') | float > 0 }}"
Outro exemplo prático será um template que diga, em vez de quanto tempo levará (segundos), a que horas estará a água pronta:
- sensor:
- name: "Time Temperature"
unique_id: time_heater_temperature
state: >
{{ (states('sensor.time_heater_seconds') | float + now().timestamp() | int) | timestamp_custom('%H:%M:%S' , false) }}
availability: "{{ states('sensor.time_heater_seconds') not in ['0', 'unavailable', 'unknown', 'none'] }}"
Ps. Se os dados de potência (Watts) forem 0W, os sensores, obviamente, ficarão ‘Unavailable’.
Edit: Acréscimo de ‘availability’ aos templates para resolver qualquer erro inevitável como “ZeroDivisionError”, quando a 0W.
EDIT:
Para resolver algumas questões estéticas, como os sensores ficarem a gray (names, icons & state-text [Unavailable]), e valores errados, caso a temperatura pretendida fosse atingida (ex: 55ºC) mas o termostato só disparasse a uma temperatura superior, induzindo a equação a valores errados: (now)HH:MM:SS - segundos & 00:00:00 - segundos; criei outros 3 templates, para se acrescentar aos 3 já criados.
Ficando a solução completa:
- sensor:
- name: "Time Heater Seconds"
unique_id: time_heater_seconds
state: >
{{ (4.186*(80000*(60-states('sensor.temperature')|float(0)))/states('sensor.heater_watts')|float(0)) | round(0) }}
availability: "{{ states('sensor.heater_watts') | float > 0 }}"
- name: "Time Heater"
unique_id: time_heater
state: >
{{ (4.186*(80000*(60-states('sensor.temperature')|float(0)))/states('sensor.heater_watts')|float(0)) | round(0) | int | timestamp_custom('%H:%M:%S' , false) }}
availability: "{{ states('sensor.heater_watts') | float > 0 }}"
- name: "Heater When"
unique_id: heater_when
state: >
{{ (states('sensor.time_heater_seconds') | float + now().timestamp() | int) | timestamp_custom('%H:%M:%S' , false) }}
availability: "{{ states('sensor.time_heater_seconds') not in ['0', 'unavailable', 'unknown', 'none'] }}"
#########Mode 2########## Sensores a serem adicionados ao Frontend
- name: "Time Heater Seconds 2"
unique_id: time_heater_seconds_2
state: >
{% if (is_state('sensor.time_heater_seconds','unavailable')) %}
Idle
{% elif (states('sensor.time_heater_seconds') | float <= 0) %}
Idle
{% else %}
{{ states('sensor.time_heater_seconds') }}
{% endif %}
- name: "Time Heater 2"
unique_id: time_heater_2
state: >
{% if (is_state('sensor.time_heater_seconds','unavailable')) %}
Idle
{% elif (states('sensor.time_heater_seconds') | float <= 0) %}
Idle
{% else %}
{{ states('sensor.time_heater') }}
{% endif %}
- name: "Heater When 2"
unique_id: heater_when_2
state: >
{% if (is_state('sensor.time_heater_seconds','unavailable')) %}
Idle
{% elif (states('sensor.time_heater_seconds') | float <= 0) %}
Idle
{% else %}
{{ states('sensor.heater_when') }}
{% endif %}
Honestamente, não consegui contemplar uma solução mais elegante. Mas, ao menos, essa parece resolver todas as pequenas questões e imperfeições.
Exemplos de ambos modes:
Ex: 1500W - Temperatura pretendida por atingir
Aqui começam os erros & questões estéticas, já resolvidos…
Ex: 1500W - Temperatura pretendida alcançada (ex:55ºC), mas termostato só dispara a uma temperatura superior (ex: 60ºC)
Ex: 0W - Temperatura pretendida alcançada