Bienvenue dans ma collection de tips et automatisations pour Home Assistant.
Ces guides documentent les solutions que j'ai trouvées après avoir galéré sur des cas concrets : intégrations capricieuses, comportements inattendus, patterns fiables à réutiliser. L'objectif est de garder une trace des approches qui fonctionnent — et pourquoi elles fonctionnent.
Note : L'intégration Apple TV dans Home Assistant peut être capricieuse. La logique ci-dessous a été affinée pour être la plus fiable possible, avec une vérification d'état active plutôt qu'un délai fixe.
Ce script centralise allumage et extinction dans une seule entité, appelée depuis les automatisations avec une variable action: "on" ou action: "off". Il est lancé en mode queued (max 5) pour absorber les appels simultanés sans conflit.
Fonctionnement :
- Recharge l'intégration, puis attend (jusqu'à 20s) que
remote.apple_tv_du_salonrepasse àon— connexion pyatv rétablie — au lieu d'un délai fixe. action: "off"→ envoiesuspendviaremote.send_command(contourne le gardepower_statedemedia_player.turn_off), puis vérifie que le media_player passe àoff.action: "on"→ envoiewakeupviamedia_player.turn_on(contourne le gardeFeatureName.TurnOn), puis vérifie que le media_player quitte l'étatoff.- Si l'état attendu n'est pas atteint après timeout, une entrée logbook est créée pour rendre l'échec visible.
alias: Allumer/Éteindre Apple TV du Salon
mode: queued
max: 5
sequence:
- action: homeassistant.reload_config_entry
target:
entity_id: remote.apple_tv_du_salon
data: {}
- wait_template: "{{ is_state('remote.apple_tv_du_salon', 'on') }}"
timeout:
seconds: 20
continue_on_timeout: true
- choose:
- conditions:
- condition: template
value_template: "{{ action == \"off\" }}"
sequence:
- action: remote.send_command
target:
entity_id: remote.apple_tv_du_salon
data:
command: suspend
continue_on_error: true
- wait_template: "{{ is_state('media_player.apple_tv_du_salon', 'off') }}"
timeout:
seconds: 15
continue_on_timeout: true
- conditions:
- condition: template
value_template: "{{ action == \"on\" }}"
sequence:
- action: media_player.turn_on
continue_on_error: true
data: {}
target:
entity_id: media_player.apple_tv_du_salon
- wait_template: "{{ not is_state('media_player.apple_tv_du_salon', 'off') }}"
timeout:
seconds: 20
continue_on_timeout: true
continue_on_error: true
- if:
- condition: template
value_template: "{{ not wait.completed }}"
then:
- action: logbook.log
data:
name: Apple TV du Salon
message: >-
Échec probable de la commande "{{ action }}" : l'état attendu n'a
pas été observé après reload + commande.
entity_id: remote.apple_tv_du_salonLe script est appelé via script.turn_on (fire-and-forget) pour ne pas bloquer le flux principal de l'automatisation en cas d'échec.
Allumage :
action: script.turn_on
target:
entity_id:
- script.allumer_eteindre_apple_tv_salon
data:
variables:
action: "on"
continue_on_error: trueExtinction :
action: script.turn_on
target:
entity_id:
- script.allumer_eteindre_apple_tv_salon
data:
variables:
action: "off"
continue_on_error: trueIl existe deux façons d'appeler un script depuis une automatisation, avec des comportements très différents en cas d'erreur.
Le script s'exécute dans le contexte de l'automatisation appelante. Une erreur non gérée remonte et fait planter l'automatisation, sauf si continue_on_error: true est ajouté sur l'étape.
action: script.notification_sms
data:
message_type: Alerte
message_sms: "{{ message_alerte }}"Le script est lancé en tâche de fond indépendante. Une erreur à l'intérieur du script n'impacte pas l'automatisation appelante, qui continue normalement. Les variables sont passées via data: variables:.
action: script.turn_on
target:
entity_id: script.notification_sms
data:
variables:
message_type: Alerte
message_sms: "{{ message_alerte }}"À privilégier : script.turn_on pour les scripts annexes (notifications, actions secondaires) afin qu'un échec ponctuel ne bloque jamais le flux principal. L'appel direct reste pertinent quand on veut que l'échec du script soit traité par l'automatisation appelante, ou avec continue_on_error: true pour ignorer l'erreur sans changer de méthode d'appel.
Deux limitations importantes rencontrées lors du développement de blueprints.
Les variables déclarées dans variables: ne sont pas encore évaluées au moment où les triggers sont définis. Il faut donc utiliser directement les !input dans les triggers, ou des entités statiques.
# ❌ Ne fonctionne pas
variables:
ma_entite: !input entity_input
trigger:
- platform: state
entity_id: "{{ ma_entite }}" # variables: pas encore disponible ici
# ✅ Correct
trigger:
- platform: state
entity_id: !input entity_input # utiliser !input directementDans les templates Jinja2 des actions, les !input doivent être capturés au préalable dans la section variables: pour être accessibles. Un accès direct à !input dans un template imbriqué peut échouer selon le contexte d'évaluation.
# ❌ Peut échouer selon le contexte
actions:
- condition: template
value_template: "{{ !input entity_input == 'on' }}"
# ✅ Correct : capturer d'abord dans variables:
variables:
mon_input: !input entity_input
actions:
- condition: template
value_template: "{{ mon_input == 'on' }}"