Formation PUB420 : Système domotique DIY, 2020 Home Assistant

6.49 Modèles qui manipulent des dates et des heures


Les opérations d'addition et de soustractions sont possibles sur les dates et heures dans un modèle Home Assistant.

Veuillez noter que même si Home Assistant permet d'afficher les dates et heures dans le fuseau horaire local, il représente à l'interne toutes les dates et heures en temps universel coordonné (UTC). Il faut donc être prudent dans ces manipulations.

Plusieurs techniques permettent de manipuler les heures dans Home Assistant. Je vous en présente quelques-unes ici.

Date du jour

La fonction now() permet d'obtenir la date et l'heure actuelles dans le fuseau horaire local. Elle retourne un objet Python de type datetime.

Modèle

{{ now() }}

La fonction utcnow() permet d'obtenir la date et l'heure actuelles au format UTC.

Modèle

{{ utcnow() }}

Puisqu'on obtient un objet Python, il est possible de le manipuler à l'aide des méthodes et propriétés de la classe datetime.

Par exemple, pour obtenir l'année courante :

Modèle

{{ now().year }}

Attention : l'heure n'est pas réévaluée à chaque seconde. Selon la documentation officielle de Home Assistant1:

Using now() will cause templates to be refreshed at the start of every new minute.

Il est aussi possible de travailler avec un capteur virtuel qui affiche la date et l'heure actuelles, par exemple sensor.date, sensor.time, sensor.date_time.

Remarquez qu'on obtient ici une chaîne de caractères et non un objet datetime de Python.

Modèle

{{ states('sensor.date_time') }} 

Selon la documentation officielle de Home Assistant2:

Sensors including the time update every minute, the date sensor updates each day at midnight, and the beat sensor updates with each beat (86.4 seconds).

Fuseau horaire

Il est rare que les modèles aient besoin de connaître le fuseau horaire local mais si jamais vous en avez besoin, vous pouvez connaître le fuseau horaire configuré dans Home Assistant à l'aide de la propriété tzinfo de la classe Python datetime.

Ceci affichera chez moi America/Toronto :

Modèle

{{ now().tzinfo }}

Il est même possible de savoir si, selon la date, l'heure locale est à l'heure avancée (Daylight Daving Time).

Modèle

{{ now().timetuple().tm_isdst }}

Convertion d'une date en timestamp

Une date est généralement représentée sous forme de chaîne de caractères. Sa conversion en un timestamp permettra d'utiliser cette date dans des calculs et dans des comparaisons.

Un timestamp représente le nombre de secondes écoulées entre le 1er janvier 1970 à 00:00:00 UTC et la date.

L'attribut timestamp d'un capteur de date représente cette date convertie en UTC puis en timestamp. 

Il s'agit d'une variable de type float.

Modèle

{{ state_attr('input_datetime.date_et_heure', 'timestamp') }}

Ainsi, il est possible d'obtenir la date de la semaine suivante (en timestamp) comme ceci :

Modèle

{{ state_attr('input_datetime.date_et_heure', 'timestamp') + 60*60*24*7 }}

La fonction as_timestamp() permet elle aussi d'obtenir un timestamp lorsqu'on lui passe en paramètre une chaîne qui représente une date.

Modèle

{{ as_timestamp(states('input_datetime.date_et_heure')) }}

as_timestamp() peut également recevoir un objet datetime :

Modèle

{{ as_timestamp(now()) }}

Notez que puisqu'un timestamp est basé sur UTC, on obtiendra le même résultat avec utcnow().

Modèle

{{ as_timestamp(utcnow()) }}

En effet, si on utilise now(), la date sera d'abord convertie en UTC avant de passer en timestamp. Avec utcnow(), la première étape est déjà réalisée.

La conversion en timestamp peut se faire également à l'aide du filtre as_timestamp :

Modèle

{{ states('input_datetime.date_et_heure') | as_timestamp }}

timestamp d'un sensor.date_time

Avec un sensor.date_time, il faut utiliser une astuce supplémentaire.

En effet, ce capteur virtuel affiche la date au format AAAA-MM-JJ, HH:MM

alors que as_timestamp attend une chaîne au format AAAA-MM-JJ HH:MM:SS, avec ou sans l'heure.

Il faut alors utiliser la méthode replace() pour enlever la virgule.

Sans cette précaution, on obtiendrait la valeur None.

Modèle

{{ as_timestamp(states('sensor.date_time').replace(',','')) }}

L'autre option est de travailler avec un sensor.date_time_iso, qui représente la date et l'heure actuelles au format AAAA-MM-JJTHH:MM:SS (remarquez le T entre la date et l'heure) et qui peut être directement converti en timestamp.

Modèle

{{ as_timestamp(states('sensor.date_time_iso')) }}

timestamp d'un sensor.time

Avec un sensor.time, il faut adopter une autre technique.

Le problème, c'est qu'il manque la partie date à la valeur de ce capteur. Alors puisque la date buttoir d'un timestamp est le 1er janvier 1970, il est possible de concaténer cette date avant de faire la conversion.  Il ne faut pas oublier l'espace requis entre la date et l'heure.

Sans cette concaténation, on obtiendrait la valeur None.

Modèle

{{ as_timestamp('1970-01-01 ' + states('sensor.time')) }}

Conversion d'un timestamp en chaîne qui représente la date

Le filtre timestamp_utc permet de convertir un timestamp en une chaîne qui représente la date UTC.

La chaîne sera au format AAAA-MM-JJ HH:MM:SS.

Modèle

{{ as_timestamp(states('sensor.date_time_iso')) | timestamp_utc }}

Attention : ceci ne fonctionnera que si vous avez en main un timestamp. Si vous essayez de passer le filtre à une chaîne, vous obtiendrez la chaîne inchangée.

Modèle

{{ states('sensor.date_time_iso') | timestamp_utc }} 

Le filtre timestamp_local permet de convertir un timestamp en une chaîne qui représente la date dans le fuseau horaire local.

La chaîne sera ici aussi au format AAAA-MM-JJ HH:MM:SS.

Modèle

{{ as_timestamp(states('sensor.date_time_iso')) | timestamp_local }}

Le filtre timestamp_custom permet de convertir un timestamp en une chaîne qui représente la date dans le format souhaité, en heure locale ou UTC. 

Le premier paramètre représente le format souhaité. Vous pouvez utiliser les directives documentées ici : https://docs.python.org/3/library/time.html#time.strftime.

Si vous lui passez la valeur true comme second paramètre, la chaîne représentera la date au fuseau horaire local.

Le paramètre false donnera la date au format UTC.

Ici, on convertit la valeur du capteur en timestamp puis on lui donne le format souhaité, selon le fuseau horaire local. Ceci donnera le même résultat que si on avait utilisé le filtre timestamp_local.

Modèle

{{ state_attr('input_datetime.date_et_heure', 'timestamp') | timestamp_custom("%Y-%m-%d %H:%M:%S", true) }}

Ici, on n'affichera que la date sans heure. 

Modèle

{{ state_attr('input_datetime.date_et_heure', 'timestamp') | timestamp_custom("%Y-%m-%d", true) }}

Calculs avec un timestamp

Le fait de transformer une date en timestamp permet de l'utiliser dans des calculs. Par exemple, on pourrait faire une addition pour obtenir la date de la semaine suivante.

Une fois les calculs effectués, la date pourra être reconvertie en chaîne.

Ici, j'ai choisi d'utiliser as_timestamp() plutôt que de travailler avec l'attribut timestamp afin d'illustrer les deux possibilités.

J'ai aussi effectué le calcul de secondes pour représenter 7 jours (60*60*24*7).

Modèle

{{ (as_timestamp(states('input_datetime.date_et_heure')) + 604800) | timestamp_local }}

Comparaison avec la date du jour

Plusieurs techniques permettent de comparer une date avec la date du jour.

On peut travailler avec now() :

Modèle

{{ state_attr('input_datetime.date_et_heure', 'timestamp') < as_timestamp(now()) }}

ou avec un sensor.date_time_iso, qui représente lui aussi la date du jour :

Modèle

{{ state_attr('input_datetime.date_et_heure', 'timestamp') < as_timestamp(states('sensor.date_time_iso')) }}

ou encore avec un sensor.date_time, qui représente également la date du jour mais nécessite une manipulation supplémentire :

Modèle

{{ state_attr('input_datetime.date_et_heure', 'timestamp') < as_timestamp(states('sensor.date_time').replace(',','')) }}

Comparaison avec l'heure actuelle sans calculs

Si vous devez comparer deux entités qui représentent une heure, il est possible d'effectuer une comparaison sans avoir à passer par un timestamp.

Modèle

{{ states('sensor.time') <= states('input_datetime.heure') }}

Mais si vous devez faire des calculs, par exemple poser une action 30 minutes avant l'heure affichée, il faudra passer par un timestamp avec les précautions qui s'imposent, comme présenté dans les prochaines sections.

Comparaison avec l'heure actuelle si besoin d'effectuer des calculs

Quand Home Assistant fait des calculs qui impliquent des heures, ces heures seront d'abord converties en UTC si elles sont dans un fuseau horaire différent.

La majorité des heures sont affichées par défaut dans le fuseau horaire local, mais il y a des exceptions. La principale difficulté lorsqu'on compare des heures est donc de s'assurer que le tout soit dans le même fuseau horaire.

input_datetime qui saisit la date et l'heure : affiché en local

Un capteur virtuel input_datetime qui saisit une date et une heure est affiché en heure locale, tel qu'on s'y attend.

À preuve, voici quelques modèles qui effectuent la conversion entre l'heure locale et l'heure UTC. Les résultats obtenus sont affichés plus bas.

Modèle

date et heure (saisi)
{{ states('input_datetime.date_et_heure') }}

date et heure (UTC)
{{ state_attr('input_datetime.date_et_heure', 'timestamp') | timestamp_utc }}
{{ state_attr('input_datetime.date_et_heure', 'timestamp') | timestamp_custom("%H:%M:%S", false) }}

date et heure (local)
{{ state_attr('input_datetime.date_et_heure', 'timestamp') | timestamp_local }}
{{ state_attr('input_datetime.date_et_heure', 'timestamp') | timestamp_custom("%H:%M:%S", true) }}

Résultat à l'écran

date et heure (saisi)
2022-01-11 10:30:00

date et heure (UTC)
2022-01-11 15:30:00
15:30:00

date et heure (local)
2022-01-11 10:30:00
10:30:00

sensor.time : affiché en local

Si on fait de même avec l'heure courante obtenue par un sensor.time, on voit qu'elle est elle aussi affichée par défaut en heure locale.

Modèle

sensor.time (affiché):
{{ states('sensor.time') }}

sensor.time (UTC)
{{ as_timestamp('1970-01-01 ' + states('sensor.time')) | timestamp_utc }}
{{ as_timestamp('1970-01-01 ' + states('sensor.time')) | timestamp_custom("%H:%M:%S", false) }}

sensor.time (local)
{{ as_timestamp('1970-01-01 ' + states('sensor.time')) | timestamp_local }}
{{ as_timestamp('1970-01-01 ' + states('sensor.time')) | timestamp_custom("%H:%M:%S", true) }}

Résultat à l'écran

sensor.time (affiché):
09:16

sensor.time (UTC)
1970-01-01 14:16:00
14:16:00

sensor.time (local)
1970-01-01 09:16:00
09:16:00

input_datetime qui ne saisit que l'heure : affiché en UTC si on ne prend pas de précautions

Avec un input_datetime qui ne saisit que l'heure, par contre, l'heure affichée est en UTC si on ne prend pas les précautions nécessaires.

Donc, s'il contient la valeur 10h30, c'est 10h30 UTC qui sera utilisé dans les calculs et non 10h30 local converti en UTC comme on s'y attendrait.

J'ai barré les instructions pour vous rappeler que ce n'est pas la technique à utiliser.

Modèle

heure (saisi)
{{ states('input_datetime.heure') }}

heure (UTC)
{{ state_attr('input_datetime.heure', 'timestamp') | timestamp_utc }}
{{ state_attr('input_datetime.heure', 'timestamp') | timestamp_custom("%H:%M:%S", false) }}

heure (local)
{{ state_attr('input_datetime.heure', 'timestamp') | timestamp_local }}
{{ state_attr('input_datetime.heure', 'timestamp') | timestamp_custom("%H:%M:%S", true) }}

Résultat à l'écran

heure (saisi)
10:30:00

heure (UTC)
1970-01-01 10:30:00
10:30:00

heure (local)
1970-01-01 05:30:00
05:30:00

La bonne technique consiste à utiliser la même astuce que pour un sensor.time : ajouter la date devant l'heure avant de la convertir en timestamp.

Modèle

heure (saisi)
{{ states('input_datetime.heure') }}

heure en ajoutant date (UTC)
{{ as_timestamp('1970-01-01 ' + states('input_datetime.heure')) | timestamp_utc }}
{{ as_timestamp('1970-01-01 ' + states('input_datetime.heure')) | timestamp_custom("%H:%M:%S", false) }}

heure en ajoutant date (local)
{{ as_timestamp('1970-01-01 ' + states('input_datetime.heure')) | timestamp_local }}
{{ as_timestamp('1970-01-01 ' + states('input_datetime.heure')) | timestamp_custom("%H:%M:%S", true) }}

Résultat à l'écran

heure (saisi)
10:30:00

heure en ajoutant date (UTC)
1970-01-01 15:30:00
15:30:00

heure en ajoutant date (local)
1970-01-01 10:30:00
10:30:00

Attention aux dates près des changements d'heure

Attention : dans Home Assistant core-2021.10.6, il y a un bogue dans l'algorithme de conversion entre l'heure locale et l'heure UTC.

Par exemple, j'ai fait ce test pour la date du 29 octobre, qui est située entre la date de changement d'heure en Europe et celle du Québec.

Résultat: j'ai obtenu seulement 4 heures de décallage alors qu'il devrait en avoir 5.

Soyez vigilants!

Modèle

date et heure (UTC)
{{ state_attr('input_datetime.date_et_heure', 'timestamp') | timestamp_utc }}

date et heure (local)
{{ state_attr('input_datetime.date_et_heure', 'timestamp') | timestamp_local }}

Résultat à l'écran

date et heure (UTC)
2021-10-29 14:30:00

date et heure (local)
2021-10-29 10:30:00

Comparaison avec calculs maintenant possible!

Après avoir appliqué la technique pour ramener le input_datetime dans le bon fuseau horaire, il est possible de faire des calculs puis de les comparer.

Ici, on vérifie si on est 30 minutes avant l'heure prévue.

Modèle

{{ as_timestamp('1970-01-01 ' + states('sensor.time')) >= as_timestamp('1970-01-01 ' + states('input_datetime.heure')) - 60*30 }}

Autre astuce

Il aurait également été possible d'ajouter 5 heures au input_datetime alors qu'il est au format timestamp afin de le mettre sur le fuseau horaire local (le nombre d'heures sera différent selon votre fuseau horaire).

Mais ceci est moins intéressant puisqu'il faudra gérer nous-mêmes les passages à l'heure avancée.

Modèle

{{ (state_attr('input_datetime.heure', 'timestamp') + 5*60*60) | timestamp_local }}

On obtiendra cette fois 1970-01-01 10:30:00 donc la comparaison est maintenant possible :

Modèle

{{ as_timestamp('1970-01-01 ' + states('sensor.time')) >= (state_attr('input_datetime.heure', 'timestamp') + 5*60*60) }}

Je ne suis pas tout à fait responsable de cette solution, j'ai l'ai trouvée pendant une nuit d'insomnie!

Brain meme

Source : https://starecat.com/brain-hey-youre-going-to-sleep-yes-now-shut-up-i-think-i-figuerd-out-how-to-debug-your-program-comic/

 

Attention : si vous faites une égalité entre deux heures dans un déclencheur, l'action risque de ne jamais être déclenchée puisque Home Assistant ne réévaluera pas l'heure à chaque seconde.

Il faut plutôt utiliser >= ou <=.

L'utilisation de < ou de > n'est pas non plus souhaitable puisque vos déclencheurs ne lanceraient l'action que la minute suivante, lors de la réévaluation des capteurs de temps.

Sources

1. « Templating ». Home Assistant. https://www.home-assistant.io/docs/configuration/templating/

2. « Time & Date ». Home Assistant. https://www.home-assistant.io/integrations/time_date/

Pour plus d'information

« Templating - time ». Home Assistant. https://www.home-assistant.io/docs/configuration/templating/#time

▼Publicité

Veuillez noter que le contenu de cette fiche vous est partagé à titre gracieux, au meilleur de mes connaissances et sans aucune garantie.
Merci de partager !
Soumettre