Tous est fait de façon native (sans carte personnalisée/custom-card) .
Même si vous ne souhaitez pas utiliser ce genre de carte, je vous conseille de lire tranquillement ce tutoriel pour y approfondir vos connaissances
Mon approche utilise l’utilisation d’un timer, d’entités personnalisées, de plusieurs automations et d’un script python.
Si vous ne souhaitez pas la lire (vous passerez à côté des explications), rendez-vous directement au Résumé pour obtenir les codes à incorporer à votre installation.
Niveau requis
- Débutant / Intermédiaire / Avancé
Contexte de l’aide
Parce que le but de la communauté est de s’entraider, voici comment créer une carte sur votre tableau de bord représentant un minuteur demandé par un utilisateur :
Création des entités
Tout d’abord, il faut créer un timer (ici, il sera d’une heure une minute et quarante cinq secondes):
#Définition des timers
timer:
timer_1h_temps:
duration: '01:01:45'
Pour pouvoir afficher le temps restant du timer, il va falloir passer par des entités personnalisées :
Pourquoi ? Car nativement seule la carte entités
permet d’afficher le temps restant :
Il va donc falloir créer une entité qui représentera le temps restant.
Pour la créer, je me suis basé sur les états et attributs de l’entité timer.timer_1h_temps
.
Son état peut prendre les valeurs idle
(non démarré), paused
(en pause) ou active
(démarré). Aucun de ces états ne permet de récupérer le temps restant.
Passons donc au attributs :
Il y en a plusieurs, duration
(temp du timer), editable
(edition possible).
Et c’est tout ? Non ! Pour voir apparaître d’autre attributs, il faut lancer le timer :
Les attributs remaining
(temps restant du timer qui se met à jour seulement quand le timer est en pause) et finishes_at
(horaire de fin du timer qui apparait uniquement lorsque le timer est démarré) apparaissent.
On va donc utiliser l’attribut finishes_at
qui indique l’horaire de fin du timer pour créer notre entité personnalisée :
sensor:
- platform: template
sensors:
#Temps restant du timer timer.timer_1h_temps suivant l'attribut 'finishes_at' de l'entité 'timer.timer_1h_temps'
#l'attribut 'finishes_at' n'est présent dans les attributs que lorsque le timer est démarré et represente l'horaire de fin du timer
timer_1h_temps_restant_int:
friendly_name: 'Timer 1h : Temps restant'
value_template: >
{% if is_state("timer.timer_1h_temps", "active") %}
{{ ( as_timestamp(state_attr('timer.timer_1h_temps', 'finishes_at')) | int - (now().timestamp()) | int ) | timestamp_custom('%H:%M:%S', False) }}
{% elif is_state("timer.timer_1h_temps", "paused") %}
En pause
{% else %}
Non démarré
{% endif %}
attribute_templates:
icon: 'mdi:clock-fast'
Explication de la formule dans la balise value_template
Il y a 3 possibilités d’état de l’entité à l’aide de l’utilisation de if/elif/else
:
1. Quand le timer est démarré
{% if is_state("timer.timer_1h_temps", "active") %}
alors l’entité prend la valeur :
( as_timestamp(state_attr('timer.timer_1h_temps', 'finishes_at')) | int - (now().timestamp()) | int ) | timestamp_custom('%H:%M:%S', False)
Décortiquons :
as_timestamp(state_attr('timer.timer_1h_temps', 'finishes_at')) |int
Cette formule récupère l’attribut ‹ finishes_at › de l’entité timer.timer_1h_temps
state_attr('timer.timer_1h_temps', 'finishes_at')
que l’on transforme en timestamp à l’aide de la fonction as_timestamp()
et où l’on applique le format int
pour faire des calcul.
(now().timestamp()) | int
Cette formule récupère le timestamp() de l’heure actuelle que l’on convertie en integer
afin de pourvoir utiliser les fonctions de calcul
Ensuite on soustrait ces deux formules :
as_timestamp(state_attr('timer.timer_1h_temps', 'finishes_at')) | int - (now().timestamp()) | int
ce qui peux donner comme résultat 3694
…
On va le rendre plus compréhensible en englobant la formule par :
timestamp_custom('%H:%M:%S', False)
ce qui donne au final la formule plus haut :
( as_timestamp(state_attr('timer.timer_1h_temps', 'finishes_at')) | int - (now().timestamp()) | int ) | timestamp_custom('%H:%M:%S', False)
ce qui donne comme résultat : 01:01:34
Beaucoup plus compréhensible.
2. Quand le timer est en pause
{% elif is_state("timer.timer_1h_temps", "paused") %}
alors l’entité prend la valeur En pause
3. Quand le timer n’est pas démarré ni en pause
{% else %}
alors l’entité prend la valeur Non démarré
Mise à jour de l’entité
Malheureusement, l’entité que nous venons de créer ne se met à jour que sur un changement d’état de l’entité timer.timer_1h_temps
.
Donc on aura une mise à jour du temps restant seulement lorsque l’on démarre le timer, le met en pause ou l’arrête. Pour avoir une mise à jour toutes les secondes, on va passer par une automatisation (automation).
Mise à jour de l’entité " timer.timer_1h_temps_restant_int
"
L’automation suivante permet de forcer la mise à jour de l’entité :
automation:
- alias: timer_1h_mise_a_jour_pour_les_secondes
#Mise à jour de l'entité sensor.timer_1h_temps_restant toutes les secondes
id: 'timer_1h_mise_a_jour_pour_les_secondes'
trigger:
platform: time_pattern
seconds: "/1"
condition:
action:
- service: homeassistant.update_entity
entity_id: sensor.timer_1h_temps_restant_int
Mais comme il n’est pas nécessaire de mettre à jour l’entité si le timer n’est pas démarré, on va optimiser tout ça par une seconde automation qui l’activera/désactivera dès qu’un changement d’état de l’entité est détecté :
automation:
- alias: 'activation_desactivation_automation_timer_1h_mise_a_jour_pour_les_secondes'
#Permet d'activer la mise à jour de l'entité sensor.timer_1h_temps_restant
trigger:
platform: state
entity_id: timer.timer_1h_temps
condition:
action:
- choose:
- conditions:
#Le timer n'est pas démarré
- condition: state
entity_id: timer.timer_1h_temps
state: "idle"
sequence:
- service: automation.turn_off
entity_id: automation.timer_1h_mise_a_jour_pour_les_secondes
- conditions:
#Le timer est démarré
- condition: state
entity_id: timer.timer_1h_temps
state: "active"
sequence:
- service: automation.turn_on
entity_id: automation.timer_1h_mise_a_jour_pour_les_secondes
- conditions:
#Le timer est en pause
- condition: state
entity_id: timer.timer_1h_temps
state: "paused"
sequence:
- service: automation.turn_off
entity_id: automation.timer_1h_mise_a_jour_pour_les_secondes
default:
- service: automation.turn_off
entity_id: automation.timer_1h_mise_a_jour_pour_les_secondes
L’utilisation de la balise choose
est semblable à l’utilisation des if/elif/else
.
La mise à jour toute les secondes est activée quand l’état correspond à active
.
Où en est-on ?
Si nous affichons l’entité sensor.timer_1h_temps_restant_int
dans Lovelace, on verra bien le timer mis à jour toutes les secondes.
Mais …
Lorsque l’on met en pause le timer, l’attribut finishes_at
n’est plus disponible, et donc, la valeur de l’entité devient En pause
, alors qu’il serait plus intéressant de garder le nombre d’heures/minutes/secondes restantes.
Afficher le temps restant (même en cas de pause du timer)
Pour réussir cela, nous allons créer une nouvelle fois une entité, mais nous n’allons pas conditionner sa mise à jour sur un changement d’état d’une quelconque entité mais la faire nous même à l’aide d’un script python
.
Utilisation des scripts pythons
Pour pouvoir appeler le service python_script.nom_du_fichier_python
, il faut ajouter ceci dans votre fichier configuration.yaml
:
# configuration.yaml
python_script:
Et il faut placer tous vos scripts python
dans le dossier <config>/python_scripts/
où <config>
correspond au dossier où se trouve votre fichier configuration.yaml
Le code contenu dans notre fichier ecrire_etat_entite.py
:
# ecrire_etat_entite.py
#--------------------------------------------------------------------------------------------------
# Force l'écriture de l'état d'une entité (ses attributs ne sont pas modifiés)
#--------------------------------------------------------------------------------------------------
#Récupération de l'entité à écrire
inputEntity = data.get('entity_id')
#Récupératon de la valeur à écrire
inputState = data.get('state')
#Chargement de l'entité à son état actuel
inputStateObject = hass.states.get(inputEntity)
#On recopie les attributs
inputAttributesObject = inputStateObject.attributes.copy()
#Ecriture de la nouvelle valeur de l'entité avec conservation des attributs
hass.states.set(inputEntity, inputState, inputAttributesObject)
Et pour l’appeler, il suffit de connaitre le nom du fichier (ici : ecrire_etat_entite.py)
:
- service: python_script.ecrire_etat_entite
data:
entity_id: nom_de_l_entite
state: valeur_à_inscrire_pour_l_etat
On crée donc une entité personnelle qui n’a aucune valeur car nous la fixerons nous même à l’aide de l’appel du service python_script
:
sensor:
- platform: template
sensors:
#Temps restant du timer timer.timer_1h_temps mis à jour par le script python
timer_1h_temps_restant:
friendly_name: 'Timer 1h : Temps restant'
value_template: ""
attribute_templates:
icon: 'mdi:clock-fast'
et on rajoute à l’automation plus haut l’utilisation du script python :
automation:
- alias: timer_1h_mise_a_jour_pour_les_secondes
#Mise à jour de l'entité sensor.timer_1h_temps_restant toutes les secondes
id: 'timer_1h_mise_a_jour_pour_les_secondes'
trigger:
platform: time_pattern
seconds: "/1"
condition:
action:
- service: homeassistant.update_entity
entity_id: sensor.timer_1h_temps_restant_int
- service: python_script.ecrire_etat_entite
data:
entity_id: 'sensor.timer_1h_temps_restant'
state: "{{states('sensor.timer_1h_temps_restant_int')}}"
Maintenant, l’affichage dans Lovelace est presque parfait car nous gardons bien l’affichage du temps restant même quand le timer est en pause.
« il a mis presque dans sa phrase ».
Vous suivez bien !
Effectivement, j’ai mis presque car si on arrête le timer, la valeur affichée dans Lovelace reste le temps restant jusqu’au prochain lancement du timer, alors que je trouve plus naturel d’avoir le temps initial du timer.
On va y remédier, en ajoutant une fois de plus l’appel du script python mais quand le timer prend l’état idle
, c’est à dire quand il est arrêter, dans l’automation plus haut :
- service: python_script.ecrire_etat_entite
data:
entity_id: 'sensor.timer_1h_temps_restant'
state: "0{{state_attr('timer.timer_1h_temps', 'duration')}}"
On met donc la valeur de l’attribut duration
. le 0
permet juste d’avoir le format 01:01:45
au lieu de seulement 1:01:45
(la valeur de l’attribut duration
).
Conclusion
On a donc :
-
Un fichier python :
ecrire_etat_entite.py
(qui doit se trouver dans le dossier<config>/python_scripts
:Code (cliquez pour afficher)
# ecrire_etat_entite.py #-------------------------------------------------------------------------------------------------- # Force l'écriture de l'état d'une entité (ses attributs ne sont pas modifiés) #-------------------------------------------------------------------------------------------------- #Récupération de l'entité à écrire inputEntity = data.get('entity_id') #Récupératon de la valeur à écrire inputState = data.get('state') #Chargement de l'entité à son état actuel inputStateObject = hass.states.get(inputEntity) #On recopie les attributs inputAttributesObject = inputStateObject.attributes.copy() #Ecriture de la nouvelle valeur de l'entité avec conservation des attributs hass.states.set(inputEntity, inputState, inputAttributesObject)
-
Un fichier hacf_timer.yaml (si vous utilisez la méthode packages) :
Code (cliquez pour afficher)
#Définition des timers timer: timer_1h_temps: duration: '01:01:45' sensor: - platform: template sensors: #Capteur dont l'état est vide (utilisé dans la carte picture elements pour le titre de la carte) vide: value_template: "" - platform: template sensors: #Temps restant du timer timer.timer_1h_temps mis à jour par le script python timer_1h_temps_restant: friendly_name: 'Timer 1h : Temps restant' value_template: "" attribute_templates: icon: 'mdi:clock-fast' - platform: template sensors: #Temps restant du timer timer.timer_1h_temps suivant l'attribut 'finishes_at' de l'entité 'timer.timer_1h_temps' #l'attribut 'finishes_at' n'est présent dans les attributs que lorsque le timer est démarré et represente l'horaire de fin du timer timer_1h_temps_restant_int: friendly_name: 'Timer 1h : Temps restant' value_template: > {% if is_state("timer.timer_1h_temps", "active") %} {{ ( as_timestamp(state_attr('timer.timer_1h_temps', 'finishes_at')) | int - (now().timestamp()) | int ) | timestamp_custom('%H:%M:%S', False) }} {% elif is_state("timer.timer_1h_temps", "paused") %} En pause {% else %} Non démarré {% endif %} attribute_templates: icon: 'mdi:clock-fast' automation: - alias: 'activation_desactivation_automation_timer_1h_mise_a_jour_pour_les_secondes' #Permet d'activer la mise à jour de l'entité sensor.timer_1h_temps_restant trigger: platform: state entity_id: timer.timer_1h_temps condition: action: - choose: - conditions: #Le timer n'est pas démarré - condition: state entity_id: timer.timer_1h_temps state: "idle" sequence: - service: automation.turn_off entity_id: automation.timer_1h_mise_a_jour_pour_les_secondes - service: python_script.ecrire_etat_entite data: entity_id: 'sensor.timer_1h_temps_restant' state: "0{{state_attr('timer.timer_1h_temps', 'duration')}}" - conditions: #Le timer est démarré - condition: state entity_id: timer.timer_1h_temps state: "active" sequence: - service: automation.turn_on entity_id: automation.timer_1h_mise_a_jour_pour_les_secondes - conditions: #Le timer est en pause - condition: state entity_id: timer.timer_1h_temps state: "paused" sequence: - service: automation.turn_off entity_id: automation.timer_1h_mise_a_jour_pour_les_secondes default: - service: automation.turn_off entity_id: automation.timer_1h_mise_a_jour_pour_les_secondes - alias: timer_1h_mise_a_jour_pour_les_secondes #Mise à jour de l'entité sensor.timer_1h_temps_restant toutes les secondes id: 'timer_1h_mise_a_jour_pour_les_secondes' trigger: platform: time_pattern seconds: "/1" condition: action: - service: homeassistant.update_entity entity_id: sensor.timer_1h_temps_restant_int - service: python_script.ecrire_etat_entite data: entity_id: 'sensor.timer_1h_temps_restant' state: "{{states('sensor.timer_1h_temps_restant_int')}}"
-
Une carte Lovelace :
Code (cliquez pour afficher)
elements: - entity: sensor.vide prefix: Timer Couloir style: color: var(--primary-color) font-size: 20px font-variant: small-caps left: 17% pointer-events: none top: 20% tap_action: action: none type: state-label - entity: sensor.timer_1h_temps_restant style: color: var(--primary-color) font-size: 200% font-variant: small-caps left: 50% pointer-events: none top: 40% tap_action: action: none type: state-label - type: conditional conditions: - entity: timer.timer_1h_temps state: idle elements: - entity: timer.timer_1h_temps icon: 'mdi:play' style: color: var(--primary-color) '--paper-item-icon-active-color': var(--primary-color) font-size: 20px font-variant: small-caps left: 50% bottom: 0% tap_action: action: call-service service: timer.start service_data: entity_id: timer.timer_1h_temps type: state-icon - type: conditional conditions: - entity: timer.timer_1h_temps state_not: idle elements: - entity: timer.timer_1h_temps icon: 'mdi:pause' style: color: var(--primary-color) '--paper-item-icon-active-color': var(--primary-color) font-size: 20px font-variant: small-caps left: 20% bottom: 0% tap_action: action: call-service service: timer.pause service_data: entity_id: timer.timer_1h_temps type: state-icon - entity: timer.timer_1h_temps icon: 'mdi:close' style: color: var(--primary-color) '--paper-item-icon-active-color': red font-size: 20px font-variant: small-caps left: 40% bottom: 0% tap_action: action: call-service service: timer.cancel service_data: entity_id: timer.timer_1h_temps type: state-icon - entity: timer.timer_1h_temps icon: 'mdi:checkbox-marked-circle-outline' style: color: var(--primary-color) '--paper-item-icon-active-color': green font-size: 20px font-variant: small-caps left: 60% bottom: 0% tap_action: action: call-service service: timer.finish service_data: entity_id: timer.timer_1h_temps type: state-icon - entity: timer.timer_1h_temps icon: 'mdi:play' style: color: var(--primary-color) '--paper-item-icon-active-color': var(--primary-color) font-size: 20px font-variant: small-caps left: 80% bottom: 0% tap_action: action: call-service service: timer.start service_data: entity_id: timer.timer_1h_temps type: state-icon image: /local/images/transparent/transparent.png type: picture-elements
L’image transparente est ici [ ] (entre les crochets)
La carte est fonctionnelle malgré quelques latences parfois :
Dues au fait que le déclenchement ne se fait pas réellement toutes les secondes mais à quelques centièmes de secondes près), mais au final, sur mon test d1h01m45s, l’affichage est fonctionnel et le timer s’arrête correctement sans une seconde d’erreur .
Une question, un problème
Suivi des modifications
- 16/01/2022: correction appel icone au début du tuto @pascal_ha
- 01/02/2021: Passage en article officiel ( ) @Clemalex
- 19/10/2020 : Création @Clemalex