Indices météo sous Home Assistant : rosée, givre et humidex
Point de rosée, humidité absolue, givre, indice de chaleur et humidex : voici comment je calcule mes indices météo dans Home Assistant.
N'ayant pas encore de station météo digne de ce nom, j'utilise trois sources distinctes pour connaitre quelques données météorologiques chez nous.
- un capteur de température Aqara dans un boitier adapté
- l'intégration Météo-France de Home Assistant
- un capteur de luminosité Xiaomi YTC4043GL pour chaque façade de la maison

ℹ️ Je privilégie autant que possible les sources de données locales plutôt que celles hébergées dans le cloud.
⚠️ Cet article a été publié initialement en 2022 et certains exemples ne sont plus fonctionnels en l'état. L'attribut forecast des entités météo a été supprimé avec la version 2024.4 au profit de l'action weather.get_forecasts : le capteur de température maximale de la section Bonus, ainsi que le binary sensor de forte chaleur qui en dépend, ne fonctionnent plus. Par ailleurs, la syntaxe des automatisations a changé (service est devenu action avec la version 2024.8) et Home Assistant impose désormais des unités strictes selon la classe d'appareil, ce qui affecte le capteur d'humidité absolue ci-dessous. Le principe général reste valable, mais il vous faudra adapter les exemples à votre version.
Il existe une intégration disponible sur HACS, Thermal Comfort, qui fournit une partie des éléments présentés dans cet article, mais j'ai préféré le faire moi-même pour mieux comprendre chaque donnée, calcul ou indice.
Le point de rosée
Le point de rosée est également appelé « température de rosée ». Il s'agit tout simplement de la température en degrés en dessous de laquelle la rosée se dépose de façon naturelle à l'extérieur.
template:
- sensor:
# https://en.wikipedia.org/wiki/Dew_point#Calculating_the_dew_point
name: "Dew Point"
unit_of_measurement: "°C"
device_class: temperature
state_class: measurement
state: >-
{% set h = states('sensor.garden_sensors_humidity') | float(1.0) %} {## ⚠️ À modifier ##}
{% set t = states('sensor.garden_sensors_temperature') | float(1.0) %} {## ⚠️ À modifier ##}
{% set b = 17.62 %}
{% set c = 243.12 %}
{% set l = log(h / 100, e, 1.0) + ((b * t) / (c + t)) %}
{{ ((c * l) / (b - l)) | round(2) }}
availability: >-
{{ is_number(states('sensor.garden_sensors_humidity')) and
is_number(states('sensor.garden_sensors_temperature')) }} {## ⚠️ À modifier ##}
L'humidité absolue
L'humidité absolue décrit la quantité totale d'eau contenue dans un certain volume d'air (g/m³).
template:
- sensor:
# https://pon.fr/dzvents-alerte-givre-et-calcul-humidite-absolue/
name: "Absolute Humidity"
unit_of_measurement: "g/m³"
device_class: humidity
state_class: measurement
state: >-
{% set h = states('sensor.garden_sensors_humidity') | float(1.0) %} {## ⚠️ À modifier ##}
{% set t = states('sensor.garden_sensors_temperature') | float(1.0) %} {## ⚠️ À modifier ##}
{{ ((6.112 * (e**((17.67 * t) / (t + 243.5))) * h * 2.1674) / (273.15 + t)) | round(2) }}
availability: >-
{{ is_number(states('sensor.garden_sensors_humidity')) and
is_number(states('sensor.garden_sensors_temperature')) }} {## ⚠️ À modifier ##}
Le point de givrage
Le point de givrage est la température en dessous de laquelle l'air n'a plus assez d'énergie pour maintenir l'eau qu'il contient sous forme de vapeur : elle se solidifie et se transforme en cristaux.
Indice de risque :
- 0 : Aucun risque de givrage
- 1 : Givre peu probable malgré la température
- 2 : Givre probable malgré la température
- 3 : Forte probabilité de givre
template:
- sensor:
# https://pon.fr/dzvents-alerte-givre-et-calcul-humidite-absolue/
name: "Frost Point"
unit_of_measurement: "°C"
device_class: temperature
state_class: measurement
state: >-
{% set t = (states('sensor.garden_sensors_temperature') | float(1.0)) + 273.15 %} {## ⚠️ À modifier ##}
{% set td = (states('sensor.dew_point') | float(1.0)) + 273.15 %}
{{ ((td + (2671.02 / ((2954.61 / t) + (2.193665 * log(t, e, 1.0)) - 13.3448)) - t) - 273.15) | round(2) }}
attributes:
risk: >-
{% set f = states('sensor.frost_point') | float(1.0) %}
{% set a = states('sensor.absolute_humidity') | float(1.0) %}
{% set t = states('sensor.garden_sensors_temperature') | float(1.0) %} {## ⚠️ À modifier ##}
{% if t <= 1 and f <= 0 %}
{% if a <= 2.8 %}
1
{% else %}
3
{% endif %}
{% elif t <= 4 and f <= 0.5 and a > 2.8 %}
2
{% else %}
0
{% endif %}
availability: >
{{ is_number(states('sensor.garden_sensors_temperature')) and
is_number(states('sensor.absolute_humidity')) and
is_number(states('sensor.dew_point')) }} {## ⚠️ À modifier ##}
L'indice de chaleur
L'indice de chaleur indique une température ressentie par le corps humain à l'ombre, en fonction de l'humidité relative et de la température de l'air. Il n'est valable qu'à partir d'une température de l'air de 27 °C, d'un point de rosée de 12 °C et d'une humidité relative de 40 %.
Indice de risque :
- 0 : Aucun inconfort
- 1 : Inconfort
- 2 : Extrême inconfort
- 3 : Danger
- 4 : Danger extrême
template:
- sensor:
# https://en.wikipedia.org/wiki/Heat_index
name: "Heat Index"
unit_of_measurement: "°C"
device_class: temperature
state_class: measurement
state: >-
{% set t = states('sensor.garden_sensors_temperature') | float(0.0) %} {## ⚠️ À modifier ##}
{% set r = states('sensor.garden_sensors_humidity') | float(0.0) %} {## ⚠️ À modifier ##}
{% set p1 = -8.78469475556 %}
{% set p2 = 1.61139411 * t %}
{% set p3 = 2.33854883889 * r %}
{% set p4 = -0.14611605 * t * r %}
{% set p5 = -0.012308094 * t * t %}
{% set p6 = -0.0164248277778 * r * r %}
{% set p7 = (2.211732e-3) * t * t * r %}
{% set p8 = (7.2546e-4) * t * r * r %}
{% set p9 = (3.582e-6) * t * t * r * r %}
{{ (p1 + p2 + p3 + p4 + p5 + p6 + p7 + p8 + p9) | round(0) }}
attributes:
risk: >-
{% if (states('sensor.garden_sensors_temperature') | float(0.0)) >= 27 and (states('sensor.garden_sensors_humidity') | float(0.0)) >= 40 and (states('sensor.dew_point') | float(0.0)) >= 12 %} {## ⚠️ À modifier ##}
{% set i = states('sensor.heat_index') | float(0.0) %}
{% if i >= 54 %}
4
{% elif i >= 41 %}
3
{% elif i >= 32 %}
2
{% elif i >= 27 %}
1
{% else %}
0
{% endif %}
{% else %}
0
{% endif %}
icon: >-
{% set r = state_attr('sensor.heat_index', 'risk') | int(0) %}
{% if r == 4 %}
mdi:alert-octagon
{% elif r == 3 %}
mdi:alert
{% elif r == 2 %}
mdi:alert-outline
{% elif r == 1 %}
mdi:information-outline
{% else %}
mdi:check-circle-outline
{% endif %}
L'indice humidex
L'indice humidex prend en compte la température de l'air et le point de rosée pour mesurer le confort ressenti par un humain vis-à-vis de la chaleur.
template:
- sensor:
# https://fr.wikipedia.org/wiki/Indice_humidex
name: "Humidex"
state: >-
{% set t = states('sensor.garden_sensors_temperature') | float(0.0) %} {## ⚠️ À modifier ##}
{% set r = states('sensor.dew_point') | float(0.0) %}
{% set a = 5417.7530*((1 / 273.16) - (1 / (273.15 + r))) %}
{{ (t + 0.55555 * (6.11 * (e ** a) - 10)) | round(0) }}
attributes:
risk: >-
{% set h = states('sensor.humidex') | float(0.0) %}
{% if h >= 54 %}
4
{% elif h >= 45 %}
3
{% elif h >= 40 %}
2
{% elif h >= 30 %}
1
{% else %}
0
{% endif %}
icon: >-
{% set r = state_attr('sensor.humidex', 'risk') | int(0) %}
{% if r == 4 %}
mdi:alert-octagon
{% elif r == 3 %}
mdi:alert
{% elif r == 2 %}
mdi:alert-outline
{% elif r == 1 %}
mdi:information-outline
{% else %}
mdi:check-circle-outline
{% endif %}
Bonus
Anticiper les grosses chaleurs
Pour anticiper les périodes de chaleur, j'ai besoin de connaitre la température maximale actuelle et celles des 3 prochains jours. Pour cela, je m'appuie sur les données du capteur de température Aqara du jardin et sur celles de Météo-France.
⚠️ N'oubliez pas de remplacer les entités sensor.garden_sensors_temperature et weather.xxx par les vôtres !
template:
- sensor:
name: "Outside - Temperature max - Next days"
unit_of_measurement: "°C"
device_class: temperature
state_class: measurement
state: >-
{% set sensors = [
states('sensor.garden_sensors_temperature') | float(0.0),
state_attr('weather.xxx', 'forecast')[0].temperature | float(0.0),
state_attr('weather.xxx', 'forecast')[1].temperature | float(0.0),
state_attr('weather.xxx', 'forecast')[2].temperature | float(0.0),
state_attr('weather.xxx', 'forecast')[3].temperature | float(0.0),
] %}
{{ sensors | max }}
On ajoute un input_number pour régler notre seuil limite de fortes chaleurs.
input_number:
outside_heatwave:
name: Seuil - Forte chaleur
min: "0"
max: "50"
step: "1"
unit_of_measurement: "°C"
Et enfin un binary_sensor pour savoir si nous sommes actuellement, ou serons dans les trois prochains jours, en période de fortes chaleurs :
template:
- binary_sensor:
name: "Outside - Is heatwave"
state: "{{ states('sensor.outside_temperature_max_next_days') | float(0) >= states('input_number.outside_heatwave') | float(0) }}"
device_class: heat
Ensoleillement
Pour déterminer si le soleil tape sur la façade, j'utilise le capteur de luminosité Xiaomi YTC4043GL.

⚠️ Le Xiaomi YTC4043GL n'est pas étanche, il faut le protéger au maximum de la pluie !
Le capteur me donne la luminosité à un instant T et non une moyenne, ce qui peut être gênant lorsqu'un nuage passe devant le soleil et fait chuter la valeur. Pour lisser cela, nous allons utiliser l'intégration statistics afin de calculer la moyenne sur les 15 dernières minutes, ainsi qu'un sensor pour reprendre la valeur brute du capteur lorsque la moyenne calculée est unavailable :
⚠️ N'oubliez pas de remplacer outside_south_illuminance_lux par votre capteur de luminosité
sensor:
- platform: statistics
name: "Outside - Illuminance (lux) - South - Stats"
entity_id: sensor.outside_south_illuminance_lux
state_characteristic: average_linear
max_age: "00:15"
sampling_size: 500
precision: 0
template:
- sensor:
- name: "Outside - Illuminance (lux) - South"
unit_of_measurement: "lx"
device_class: illuminance
state_class: measurement
state: "{{ states('sensor.outside_illuminance_lux_south_stats') if is_number(states('sensor.outside_illuminance_lux_south_stats')) else states('sensor.outside_south_illuminance_lux') }}"
Pour éviter qu'au seuil mesuré le booléen oscille entre true et false, on va utiliser le principe du cycle d'hystérésis :
- Si la valeur dépasse (seuil + marge) lux, on considère qu'il y a ensoleillement (booléen à true)
- Si la valeur est inférieure à (seuil - marge) lux, on considère qu'il n'y a plus d'ensoleillement (booléen à false)
- Entre (seuil - marge) et (seuil + marge) lux, on garde la valeur précédente
Nous allons utiliser deux input_number pour les seuils, une automatisation pour gérer le cycle d'hystérésis et un input_boolean pour stocker le résultat :
⚠️ Le seuil haut (seuil + marge) doit toujours être supérieur au seuil bas (seuil - marge). Par simplification, il n'y a aucune vérification dans l'automatisation !
input_boolean:
sunny_south:
name: Ensoleillement - Sud
input_number:
sunny_south_low_threshold:
name: Ensoleillement - Sud - Seuil bas
min: "0"
max: "83000"
step: "1"
unit_of_measurement: "lux"
sunny_south_high_threshold:
name: Ensoleillement - Sud - Seuil haut
min: "0"
max: "83000"
step: "1"
unit_of_measurement: "lux"
automation:
- description: Sunny - South - Threshold
alias: sunny_south_threshold
id: d5637a4f-08b6-4ff2-88ba-77b7626a0513
mode: restart
trigger:
- platform: state
entity_id: input_boolean.sunny_south
- platform: state
entity_id: sensor.outside_illuminance_lux_south
action:
- choose:
- conditions:
- condition: template
value_template: "{{ not is_state('input_boolean.sunny_south', 'on') }}"
- condition: numeric_state
entity_id: sensor.outside_illuminance_lux_south
above: input_number.sunny_south_high_threshold
sequence:
- service: input_boolean.turn_on
target:
entity_id: input_boolean.sunny_south
- conditions:
- condition: template
value_template: "{{ not is_state('input_boolean.sunny_south', 'off') }}"
- condition: numeric_state
entity_id: sensor.outside_illuminance_lux_south
below: input_number.sunny_south_low_threshold
sequence:
- service: input_boolean.turn_off
target:
entity_id: input_boolean.sunny_south
Il ne vous reste plus qu'à dupliquer cet ensemble pour chacune de vos façades, en remplaçant south par l'orientation concernée.
Lovelace

J'utilise trois cartes personnalisées sous Lovelace pour réaliser cette interface :
type: vertical-stack
title: Jardin
cards:
- type: entities
entities:
- entity: sensor.garden_sensors_temperature
type: custom:multiple-entity-row
name: Informations
state_header: Température
secondary_info: last-changed
entities:
- sensor.garden_sensors_humidity
- entity: sensor.outside_illuminance_lux_south
type: custom:multiple-entity-row
name: Luminosité
icon: mdi:brightness-5
state_header: Sud
secondary_info: last-changed
entities:
- entity: sensor.outside_illuminance_lux_east
name: Est
- entity: input_boolean.sunny_south
type: custom:multiple-entity-row
name: Ensoleillement
icon: mdi:weather-sunny
state_header: Sud
secondary_info: last-changed
entities:
- entity: input_boolean.sunny_east
name: Est
- entity: sensor.frost_point
attribute: risk
type: custom:multiple-entity-row
name: Point de givrage
state_header: Risque
secondary_info: last-changed
- entity: binary_sensor.outside_is_heatwave
type: custom:multiple-entity-row
name: Forte chaleur
secondary_info: last-changed
- entity: sensor.humidex
attribute: risk
type: custom:multiple-entity-row
name: Humidex
state_header: Risque
secondary_info: last-changed
- entity: sensor.heat_index
attribute: risk
type: custom:multiple-entity-row
name: Indice de chaleur
state_header: Risque
secondary_info: last-changed
- type: custom:fold-entity-row
head:
label: PARAMÉTRAGE
type: section
entities:
- entity: input_number.outside_heatwave
type: custom:numberbox-card
icon: mdi:thermometer
icon_plus: mdi:chevron-up
icon_minus: mdi:chevron-down
- entity: input_number.sunny_east_high_threshold
type: custom:numberbox-card
icon: mdi:eye-plus
icon_plus: mdi:chevron-up
icon_minus: mdi:chevron-down
- entity: input_number.sunny_east_low_threshold
type: custom:numberbox-card
icon: mdi:eye-minus
icon_plus: mdi:chevron-up
icon_minus: mdi:chevron-down
- entity: input_number.sunny_south_high_threshold
type: custom:numberbox-card
icon: mdi:eye-plus
icon_plus: mdi:chevron-up
icon_minus: mdi:chevron-down
- entity: input_number.sunny_south_low_threshold
type: custom:numberbox-card
icon: mdi:eye-minus
icon_plus: mdi:chevron-up
icon_minus: mdi:chevron-down
Conclusion
Cet article est un peu long, car j'avais besoin de vous présenter les différentes données, calculs et indices météorologiques que j'utilise. Ils nous resserviront dans le prochain article sur la gestion des volets.
Vous pouvez aller plus loin en ajoutant, par exemple, des alertes sur les différents risques (givre, chaleur).