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.
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.
{{ now() }}
La fonction utcnow() permet d'obtenir la date et l'heure actuelles au format UTC.
{{ 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 :
{{ 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.
{{ 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).
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 :
{{ now().tzinfo }}
Il est même possible de savoir si, selon la date, l'heure locale est à l'heure avancée (Daylight Daving Time).
{{ now().timetuple().tm_isdst }}
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.
{{ state_attr('input_datetime.date_et_heure', 'timestamp') }}
Ainsi, il est possible d'obtenir la date de la semaine suivante (en timestamp) comme ceci :
{{ 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.
{{ as_timestamp(states('input_datetime.date_et_heure')) }}
as_timestamp() peut également recevoir un objet datetime :
{{ as_timestamp(now()) }}
Notez que puisqu'un timestamp est basé sur UTC, on obtiendra le même résultat avec utcnow().
{{ 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 :
{{ states('input_datetime.date_et_heure') | as_timestamp }}
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.
{{ 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.
{{ as_timestamp(states('sensor.date_time_iso')) }}
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.
{{ as_timestamp('1970-01-01 ' + states('sensor.time')) }}
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.
{{ 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.
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.
{{ 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.
{{ 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.
{{ state_attr('input_datetime.date_et_heure', 'timestamp') | timestamp_custom("%Y-%m-%d", true) }}
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).
{{ (as_timestamp(states('input_datetime.date_et_heure')) + 604800) | timestamp_local }}
Plusieurs techniques permettent de comparer une date avec la date du jour.
On peut travailler avec now() :
{{ 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 :
{{ 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 :
{{ state_attr('input_datetime.date_et_heure', 'timestamp') < as_timestamp(states('sensor.date_time').replace(',','')) }}
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.
{{ 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.
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.
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.
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) }}
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
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.
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) }}
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
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.
heure (saisi)
{{ states('input_datetime.heure') }}
heure (UTC)
heure (local)
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.
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) }}
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 : 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!
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 }}
date et heure (UTC)
2021-10-29 14:30:00
date et heure (local)
2021-10-29 10:30:00
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.
{{ as_timestamp('1970-01-01 ' + states('sensor.time')) >= as_timestamp('1970-01-01 ' + states('input_datetime.heure')) - 60*30 }}
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.
{{ (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 :
{{ 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!
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.
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/
« Templating - time ». Home Assistant. https://www.home-assistant.io/docs/configuration/templating/#time
▼Publicité