Comment migrer vos automatisations vers "Blueprints"

Je voulais partager ma première expérience avec la nouvelle fonctionnalité de Home Assistant appelé Blueprint.

Les développeurs connaissent bien la règle DRY (Don’t Repeat Yourself - Ne vous répétez pas). En effet lorsque l’on se répète, le code (ou les automatisations) deviennent plus difficile à maintenir. Blueprint permet d’éviter Cela.

Grace à Blueprint vous allez créer un modèle que vous allez appliquer à plusieurs automatisations. Changer ce modèle changera toutes les automatisations dépendante de ce modèle. Mais ce n’est pas tout. Blueprint vous permettra de partager ces modèles configurable en utilisant l’interface graphique.

Nous allons voir comment j’ai migré une automatisation présente 8 fois dans ma configuration vers Blueprint.

Mon automatisation

Cette automatisation arrête le chauffage d’une pièce si j’ouvre une fenêtre, et le relance après fermeture.

Extinction du chauffage lors de l’ouverture:

---
description: Éteindre le chauffage lorsque la fenêtre s'ouve
alias: bathroom_turn_off_heating_window
id: 74c8d918-3549-41ea-b9cc-d184d717cb93

trigger:
  platform: state
  entity_id: binary_sensor.open_bathroom
  to: "on"

action:
  - service: climate.turn_off
    data:
      entity_id: climate.salle_de_bain 

Allumage du chauffage lors de la fermeture:

---
description: Allumer le chauffage lorsque la fenêtre se ferme
alias: bathroom_turn_on_heating_window
id: 1a674a55-4983-45e5-a94f-9acf1b1aa59d
trigger:
  platform: state
  entity_id: binary_sensor.open_bathroom
  to: "off"
action:
  - service: climate.turn_on
    data:
      entity_id: climate.salle_de_bain 

L’automatisation en soit est simple. j’ai un binary_sensor template qui regroupe mes détecteurs présents dans la pièce et qui ressemble à ça:

---
platform: template
sensors:
  open_bathroom:
    friendly_name: Salle de bain
    device_class: window
    value_template: >
      {{ 
        is_state('binary_sensor.x_window_bathroom_left', 'on')
        or is_state('binary_sensor.x_window_bathroom_right', 'on')
      }}

et un thermostat générique.

J’ouvre la fenêtre, le thermostat s’éteint, je ferme la fenêtre, le thermostat se rallume. Voyons comment extraire une grande partie de l’automatisation dans un Blueprint.

Blueprint

Comme les automatisations commençons par décrire ce modèle.

blueprint:
  name: Ne pas chauffer l'extérieur
  description: Eteindre le chauffage lors de l'ouverture d'une fenêtre, puis le rallumer à la fermeture
  domain: automation

Ensuite il nous faut définir les entrées du Blueprint. Comme énuméré précédemment nous avons:

  • Un binary sensor
  • Un Climate

J’ai ajouté un sensor qui représente le statut du chauffage. Il sera sur ‹ off › lorsque le chauffage est en mode été pour éviter d’allumer le chauffage en été lors de la fermeture de la fenêtre.

 input:
    windows:
      name: Fenêtres
      description: Entité représentant toutes les fenêtres de la pièce
      selector: 
        entity:
          domain: binary_sensor
          device_class: window
    is_heating_on:
      default: false
      name: Chauffage ON/OFF
      description: Entité indiquant si le chauffage est en fonctionnement (on) (optionnel)
      selector: 
        entity:
    climate:
      name: Thermostat
      description: Thermostat contrôlant le chauffage de la pièce. 
      selector:
        entity:
            domain: climate

Vous remarquerez que j’ai rendu le statut du chauffage optionnelle en utilisant default: false.

Et enfin l’automatisation comme on les connais:

trigger:
  platform: state
  entity_id: !input windows

action:
  - choose:
    - conditions:
      - condition: state
        entity_id: !input is_heating_on
        state: 'on'
      - condition: template
        value_template: "{{ trigger.to_state.state == 'on' }}"
      sequence:
        - service: climate.turn_off
          entity_id: !input climate 
    - conditions:
      - condition: state
        entity_id: !input is_heating_on
        state: 'on'
      - condition: template
        value_template: "{{ trigger.to_state.state == 'off' }}"
      sequence:
        - service: climate.turn_on
          entity_id: !input climate
    default: []

Ce qui donne:

blueprint:
  name: Ne pas chauffer l'extérieur
  description: Eteindre le chauffage lors de l'ouverture d'une fenêtre, puis le rallumer à la fermeture
  domain: automation

  input:
    windows:
      name: Fenêtres
      description: Entitée représentant toutes les fenêtres de la pièce
      selector: 
        entity:
          domain: binary_sensor
          device_class: window
    is_heating_on:
      default: false
      name: Chauffage ON/OFF
      description: Entité indiquant si le chauffage est en fonctionnement (on) (optionnel)
      selector: 
        entity:
    climate:
      name: Thermostat
      description: Thermostat contrôlant le chauffage de la pièce. 
      selector:
        entity:
            domain: climate

trigger:
  platform: state
  entity_id: !input windows

action:
  - choose:
    - conditions:
      - condition: state
        entity_id: !input is_heating_on
        state: 'on'
      - condition: template
        value_template: "{{ trigger.to_state.state == 'on' }}"
      sequence:
        - service: climate.turn_off
          entity_id: !input climate 
    - conditions:
      - condition: state
        entity_id: !input is_heating_on
        state: 'on'
      - condition: template
        value_template: "{{ trigger.to_state.state == 'off' }}"
      sequence:
        - service: climate.turn_on
          entity_id: !input climate
    default: []

Et voilà maintenant n’importe qui peut aller sur Configuration -> Plan -> Importer un plan et coller le lien suivant https://gist.github.com/clempat/b2534120c51307d0c36b94e143594cba.

Puis « Créer une automatisation ». En tant qu’utilisateur pas besoin de connaître le YAML, vous pourrez configurer l’automatisation à partir de l’interface graphique.

Ce modèle est simple mais il existe beaucoup plus d’entrée possible (nombre, action, temps,…). Voir la documentation

La seule limitation que j’ai rencontré est de ne pas pouvoir faire ajouter plusieurs entités ou une area pour éviter la création d’une entités supplémentaire par pièce. Mais ce n’est que le début. Et pour un début je suis plutôt satisfait.

Pour ceux qui comme moi, aime bien séparer les automatisations par zones, il est possible d’étendre le modèle de cette façon:

---
  id: 0acd3875-234b-4cfe-86d8-dcefd380d458
  alias: bathroom_window_heating
  description: Turn on/off Heating when window is open or closed
  use_blueprint:
    path: clempat/windows_heating.yaml
    input:
      windows: binary_sensor.open_bathroom
      climate: climate.salle_de_bain
      is_heating_on: binary_sensor.heating_is_on

Retrouvez ici ma configuration complète ainsi que la migration vers Blueprint

6 J'aime

Merci pour le partage, cas simple mais qui explique bien l’intérêt de blueprint.
Ne serait-il pas possible de sauvegarder l’état du thermostat avant d’éteindre le chauffage dans une scène pour la restaurer quand la fenêtre se referme, sans se poser de question si été ou hiver, si ça chauffait avant ou pas ?
De plus j’aurais ajouté un délai pour ne pas éteindre le chauffage si j’ouvre la fenêtre 3s. Si on a des radiateurs électriques c’est pas très grave mais dans le cas de clim réversible c’est bête d’éteindre et rallumer le split pour 3s je trouve.

Concernant la consigne, elle devrait rester la même. Donc en effet le « Chauffage ON/OFF » n’est pas forcément utile. Pour cette raison il est optionnel.

En ce qui concerne le délai, cela donnerait:

blueprint:
  name: Ne pas chauffer l'extérieur
  description: Eteindre le chauffage lors de l'ouverture d'une fenêtre, puis le rallumer
    à la fermeture
  domain: automation
  input:
    windows:
      name: Fenêtres
      description: Entitée représentant toutes les fenêtres de la pièce
      selector:
        entity:
          domain: binary_sensor
          device_class: window
    delay:
      name: Délai
      description: Délai à partir duquel le chauffage s'éteins (secondes)
      default: 0
      selector:
        number:
          min: 0
          max: 120
          unit_of_measurement: secondes
    is_heating_on:
      default: false
      name: Chauffage ON/OFF
      description: Entité indiquant si le chauffage est en fonctionnement (on) (optionnel)
      selector:
        entity: {}
    climate:
      name: Thermostat
      description: Thermostat contrôlant le chauffage de la pièce.
      selector:
        entity:
          domain: climate
  source_url: https://gist.github.com/clempat/b2534120c51307d0c36b94e143594cba
trigger:
  platform: state
  entity_id: !input 'windows'
  for:
    seconds: !input delay
action:
- choose:
  - conditions:
    - condition: state
      entity_id: !input 'is_heating_on'
      state: 'on'
    - condition: template
      value_template: '{{ trigger.to_state.state == ''on'' }}'
    sequence:
    - service: climate.turn_off
      entity_id: !input 'climate'
  - conditions:
    - condition: state
      entity_id: !input 'is_heating_on'
      state: 'on'
    - condition: template
      value_template: '{{ trigger.to_state.state == ''off'' }}'
    sequence:
    - service: climate.turn_on
      entity_id: !input 'climate'
  default: []

Gist mis à jours avec le délai

1 J'aime

Comment tu gères le fait de ne pas activer le thermostat s’il était éteint avant d’ouvrir ?
Pour le délai c’est nickel :+1:

Pour le moment je ne le gère pas mais je suis ouvert aux propositions.

C’est pour cela que je proposais d’enregistrer son état dynamiquement dans une scène et de l’appliquer une fois la fenêtre fermée.
Je fais comme ça quand je fais du tts sur mon enceinte, pour restaurer son état initial (volume + lecture en cours)

Oui je vois bonne idée je vais essayer.

Je rejoins @djtef sur le fonctionnement.

Actuellement, je gère également l’ouverture des fenêtres avec un délai d’extinction, un délai d’allumage et une copie du mode avant l’extinction afin de le remettre au redémarrage…
Et tous les paramètres sont gérés par l’interface graphique et non codé en dur…

Pour la sauvegarde du mode courant, j’utilise un input_select qui comporte les mêmes options que l’input_select des modes du chauffage et qui est conservé après un redémarrage de HA.

@djtef, je te rejoins sur l’utilisation des scènes néamoins, mais la partie chauffage et le truc que j’ai fait en premier, qui fonctionne très bien, mais qui n’est pas, mais pas du tout, propre…:wink:

J’ai prévu de le mettre à jour…un jour… :wink:

utilisation des scènes pour sauvegarde des états

@clement, je pense que tu peux aller plus loin dans le DRY avec l’imbrication d’un second choose après la condition sur l’entité entity_id: !input is_heating_on.

et encore plus loin, avec l’utilisation d’un IF/ELSE…

L’intérêt que je vois de l’utilisation de Blueprint (en plus de ceux déjà cités :+1: ), c’est que le code n’a pas besoin d’être userfriendly mais peux partir complètement sur de la programmation avancée, étant donnée que ce code est la partie immergée de l’iceberg, la partie émergée par contre, se doit d’être bien présentée…

En utilisant les scenes, donc sans le is_heating_on…

blueprint:
  name: Ne pas chauffer l'extérieur
  description:
    Eteindre le chauffage lors de l'ouverture d'une fenêtre, puis le rallumer
    à la fermeture
  domain: automation
  input:
    windows:
      name: Fenêtres
      description: Entitée représentant toutes les fenêtres de la pièce
      selector:
        entity:
          domain: binary_sensor
          device_class: window
    delay:
      name: Délai
      description: Délai à partir duquel le chauffage s'éteins (secondes)
      default: 0
      selector:
        number:
          min: 0.0
          max: 120.0
          unit_of_measurement: secondes
          step: 1.0
          mode: slider
    climate:
      name: Thermostat
      description: Thermostat contrôlant le chauffage de la pièce.
      selector:
        entity:
          domain: climate
  source_url: https://gist.github.com/clempat/b2534120c51307d0c36b94e143594cba
trigger:
  platform: state
  entity_id: !input "windows"
  for:
    seconds: !input "delay"
action:
  # in order to use in template
  - variables:
      climate_input: !input "climate"
  - choose:
      - conditions:
          - condition: template
            value_template: '{{ trigger.to_state.state == "on" }}'
        sequence:
          - service: scene.create
            data:
              scene_id: previous_climate
              snapshot_entities: !input "climate"
          - service: climate.turn_off
            entity_id: !input "climate"
    default:
      - service: >
          {% if states("scene.previous_climate") != "unknown" %}
            scene.turn_on
          {% else %}
            climate.turn_on
          {% endif %}
        data:
          entity_id: >
            {% if states("scene.previous_climate") != "unknown" %}
              scene.previous_climate
            {% else %}
              {{climate_input}}
            {% endif %

Ça fontionne {{climate_input}} ? Je comprends pas d’où ça vient :thinking:

C’est un peu hacky, j’ai utilisé la variable car je n’arrive pas a avoir le input directement dans mon template.

Je penses que c’est pour ca que Franck à:

variables:
  force_brightness: !input force_brightness

En tout cas j’ai tester avec une typo dans mes conditions et ca a marché.

A la limite je comprends celui de Frenck qui définit sa variable, mais tu tu l’as définie où du coup ?

Dans action. Je pensais que ça réduisait le scope. Mais bon après je ne sais pas si ça vaut le coup…

Ah oui je regardais pas au bon endroit :upside_down_face:
Je pense que là on est pas mal, t’as même géré le cas de l’état « unknown » de la scene, ça arrive parfois ?

Si tu redémarre et que la fenêtre était ouverte je penses. En tout cas, j’ai quelque chose similaire pour ma scène « live » lorsque je suis en réunion, et je me suis déjà retrouvé sans scène précédente.

Merci Messieurs,

C’est top

Blueprint est vraiment une grosse avancée je trouve

Peut-etre qu’un sujet dans lequel serait partagés les blueprint serait utile non ? Si ca n’a pas deja ete fait :wink:

Une catégorie devrait apparaître sous peu :+1:

1 J'aime

Je dois nommer la scène en utilisant l’entité sinon il partage la scène je penses

Gist mis à jour https://gist.github.com/clempat/b2534120c51307d0c36b94e143594cba pour éviter les les conflits de scènes.