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 d'Home Assistant
- un capteur de luminosité Xiaomi YTC4043GL pour chaque façade de la maison
ℹ️ J'essaye d'utiliser, en priorité, des sources de données locales aux données dans le cloud.
Il existe un add-on sur HACS, Thermal Comfort, permettant d'obtenir quelques éléments présentés dans la suite de l'article, mais j'ai préféré le faire moi-même pour mieux comprendre chaque donnée, calcul ou index.
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.174) / (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 givre 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 pour se transformer 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 suivant l'humidité relative et la température de l'air. L'indice de chaleur n'est valable que pour des valeurs égalent ou dépassant une température de l'air de 27°C, un point de rosée de 12°C et 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:wifi-strength-4-alert
{% elif r == 3 %}
mdi:wifi-strength-3
{% elif r == 2 %}
mdi:wifi-strength-2
{% elif r == 1 %}
mdi:wifi-strength-1
{% else %}
mdi:wifi-strength-off-outline
{% endif %}
L’indice humidex
L’indice humidex est un indice, prenant en compte la température de l'air et le point de rosée, pour mesurer le confort ressenti par un humain par rapport à 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:wifi-strength-4-alert
{% elif r == 3 %}
mdi:wifi-strength-3
{% elif r == 2 %}
mdi:wifi-strength-2
{% elif r == 1 %}
mdi:wifi-strength-1
{% else %}
mdi:wifi-strength-off-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 des données 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 pour les 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 dans les trois prochains jours ou si actuellement on est dans une période de fortes chaleurs :
template:
- binary_sensor:
name: "Outside - Is heatwave"
state: "{{ states('sensor.outside_temperature_max_next_days') >= states('input_number.outside_heatwave') }}"
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 pas une moyenne ce qui peut être contraignant lorsque un nuage passe devant le soleil et fait chuter la luminosité. Pour cela nous allons utiliser l'intégration statistics pour calculer la moyenne sur les 15 dernières minutes et un sensor pour prendre la valeur du capteur quand la moyenne calculée est unavailable
:
⚠️ N'oubliez pas de modifier 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 / 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
Lovelace
J'utilise trois cards 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.garden_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 ou index 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).