Home Assistant & ECS

Mon chauffe eau électrique était géré par une automation datant de mes débuts sur Home Assistant. Ca fait le job mais je me disais que je pourrais améliorer la  chose afin de pouvoir ajuster plus facilement la plage horaire et les jours de chauffe. En effet faute d'avoir la température de l'eau (il faudrait y insérer une sonde, et comme je viens de le changer dans l'urgence je veux pas bricoler), bref, quand je suis seul dans la maison chauffer l'eau un jour sur deux est bien suffisant.

Bien sur je pourrait améliorer ça en me basant sur la présence ou non des enfants ou des invités. Mais parfois il faut savoir faire simple...

Interface

Je suis donc parti de l'interface car c'était le but premier. Pouvoir facilement et rapidement changer les heures et jours de chauffe. J'aurais pu me servir du Scheduller (moins pratique d'accès) ou de la nouvelle entité Agenda (pas encore aboutie). J'ai donc fait à ma sauce.

J'utilise quelques composants que vous allez retrouver dans le code de la carte :

type: vertical-stack
cards:
  - type: entities
    entities:
      - entities:
          - entity: automation.ecs_on
            name: false
          - entity: sensor.energy_total_yearly_1pm_ecs
            name: false
            unit: kWh
            format: precision2
          - entity: sensor.pm_ecs_power
            name: false
        entity: switch.pm_ecs
        name: ECS
        icon: mdi:waves
        show_state: false
        state_color: true
        type: custom:multiple-entity-row
  - type: horizontal-stack
    cards:
      - type: custom:button-card
        color_type: card
        entity: input_boolean.ecs_day_monday
        name: Lundi
        show_last_changed: false
        show_state: false
        tap_action:
          action: toggle
        state:
          - value: 'on'
            color: green
            icon: mdi:water-boiler
          - value: 'off'
            color: grey
            icon: mdi:water-boiler-off
        styles:
          card:
            - height: 60px
            - border-radius: 5px
            - font-size: 12px
      - type: custom:button-card
        color_type: card
        entity: input_boolean.ecs_day_tuesday
        name: Mardi
        show_last_changed: false
        show_state: false
        tap_action:
          action: toggle
        state:
          - value: 'on'
            color: green
            icon: mdi:water-boiler
          - value: 'off'
            color: grey
            icon: mdi:water-boiler-off
        styles:
          card:
            - height: 60px
            - border-radius: 5px
            - font-size: 12px
      - type: custom:button-card
        color_type: card
        entity: input_boolean.ecs_day_wednesday
        name: Mercredi
        show_last_changed: false
        show_state: false
        tap_action:
          action: toggle
        state:
          - value: 'on'
            color: green
            icon: mdi:water-boiler
          - value: 'off'
            color: grey
            icon: mdi:water-boiler-off
        styles:
          card:
            - height: 60px
            - border-radius: 5px
            - font-size: 12px
      - type: custom:button-card
        color_type: card
        entity: input_boolean.ecs_day_thursday
        name: Jeudi
        show_last_changed: false
        show_state: false
        tap_action:
          action: toggle
        state:
          - value: 'on'
            color: green
            icon: mdi:water-boiler
          - value: 'off'
            color: grey
            icon: mdi:water-boiler-off
        styles:
          card:
            - height: 60px
            - border-radius: 5px
            - font-size: 12px
      - type: custom:button-card
        color_type: card
        entity: input_boolean.ecs_day_friday
        name: Vendredi
        show_last_changed: false
        show_state: false
        tap_action:
          action: toggle
        state:
          - value: 'on'
            color: green
            icon: mdi:water-boiler
          - value: 'off'
            color: grey
            icon: mdi:water-boiler-off
        styles:
          card:
            - height: 60px
            - border-radius: 5px
            - font-size: 12px
      - type: custom:button-card
        color_type: card
        entity: input_boolean.ecs_day_saturday
        name: Samedi
        show_last_changed: false
        show_state: false
        tap_action:
          action: toggle
        state:
          - value: 'on'
            color: green
            icon: mdi:water-boiler
          - value: 'off'
            color: grey
            icon: mdi:water-boiler-off
        styles:
          card:
            - height: 60px
            - border-radius: 5px
            - font-size: 12px
      - type: custom:button-card
        color_type: card
        entity: input_boolean.ecs_day_sunday
        name: Dimanche
        show_last_changed: false
        show_state: false
        tap_action:
          action: toggle
        state:
          - value: 'on'
            color: green
            icon: mdi:water-boiler
          - value: 'off'
            color: grey
            icon: mdi:water-boiler-off
        styles:
          card:
            - height: 60px
            - border-radius: 5px
            - font-size: 12px
  - type: conditional
    conditions:
      - entity: automation.ecs_on
        state: 'on'
    card:
      type: custom:vertical-stack-in-card
      cards:
        - type: horizontal-stack
          cards:
            - type: markdown
              content: '#### <center> Heure de début'
            - type: markdown
              content: '#### <center> Chauffe Eau'
            - type: markdown
              content: '#### <center> Heure de Fin'
        - type: horizontal-stack
          cards:
            - entity: input_datetime.ecs_start
              type: custom:time-picker-card
              name: Début
              layout:
                align_controls: center
                embedded: true
              hide:
                name: true
            - type: glance
              show_state: true
              show_name: false
              entities:
                - switch.pm_ecs
            - entity: input_datetime.ecs_stop
              type: custom:time-picker-card
              layout:
                align_controls: center
                embedded: true
              hide:
                name: true

Automation

Bien plus simple que la carte Lovelace. On se base sur des input_datetime:

input_datetime:
  ecs_start:
    has_date: false
    has_time: true
  ecs_stop:
    has_date: false
    has_time: true

Et des input_boolean:

input_boolean:
  ecs_day_monday:
    name: "ECS : Lundi"
    icon: mdi:toggle-switch
  ecs_day_tuesday:
    name: "ECS : Mardi"
    icon: mdi:toggle-switch
  ecs_day_wednesday:
    name: "ECS : Mercredi"
    icon: mdi:toggle-switch
  ecs_day_thursday:
    name: "ECS : Jeudi"
    icon: mdi:toggle-switch
  ecs_day_friday:
    name: "ECS : Vendredi"
    icon: mdi:toggle-switch
  ecs_day_saturday:
    name: "ECS : Samedi"
    icon: mdi:toggle-switch
  ecs_day_sunday:
    name: "ECS : Dimanche"
    icon: mdi:toggle-switch

Et ensuite deux automations, la première pour activer :

- id: '678d0e1-fcb6-4412-abvfr-99c4d37be5aa'
  alias: 'ECS ON'
  trigger:
  - platform: template
    value_template: '{{ states.sensor.time.state == states.input_datetime.ecs_start.state[0:5] }}'
  condition:
    condition: or
    conditions:
      - '{{ (now().strftime("%a") == "Mon") and is_state("input_boolean.ecs_day_monday", "on") }}'
      - '{{ (now().strftime("%a") == "Tue") and is_state("input_boolean.ecs_day_tuesday", "on") }}'
      - '{{ (now().strftime("%a") == "Wed") and is_state("input_boolean.ecs_day_wednesday", "on") }}'
      - '{{ (now().strftime("%a") == "Thu") and is_state("input_boolean.ecs_day_thursday", "on") }}'
      - '{{ (now().strftime("%a") == "Fri") and is_state("input_boolean.ecs_day_friday", "on") }}'
      - '{{ (now().strftime("%a") == "Sat") and is_state("input_boolean.ecs_day_saturday", "on") }}'
      - '{{ (now().strftime("%a") == "Sun") and is_state("input_boolean.ecs_day_sunday", "on")}}'
  action:
  - service: switch.turn_on
    entity_id: switch.pm_ecs

Et une seconde pour désactiver :

- id: 'c69csdfsef-76dd-4fdd-9d20-40bfsdq158ae'
  alias: 'ECS OFF'
  trigger:
  - platform: template
    value_template: '{{ states.sensor.time.state == states.input_datetime.ecs_stop.state[0:5] }}'
  action:
  - service: switch.turn_off
    entity_id: switch.pm_ecs

Home Assistant, ZHA & Legrand

Il existe des devices plus ou moins bien intégrés et c'est le cas du contacteur Legrand 412170 (16A) ou 412171 (20A) qui de base est configuré en HP/HC via le signal fourni par le compteur et ou rien dans l'intégration ne permet à ce jour de la passer en mode on/off.

Vous allez me dire qu'il y est très bien géré en Zigbee2MQTT, ou que je pourrais utiliser quelque chose de moins cher (120 €). Là n'est pas la question, mon client (et ami) ne veut que du Legrand dans son tableau et pour simplifier au maximum je ne veux pas de modules externes, d'où ZHA.

L'intégration du contacteur sous ZHA se fait en appuyant 5 à 8 seconde sur le petit bouton qui passe en rouge. La légende dit que les produits Legrand ne fonctionneraient que sur le canal 11, mon réseau est en 15 et d'autres l'on fait fonctionner en 25...

En cherchant sur la toile je me suis aperçu que nos collègues amateurs de Jeedom avaient réussit à changer les choses via deconz/Phoscon. J'avais donc les valeurs à changer, mais je n'ai pas réussit à les intégrer. 

{"endpoint" : 1,"cluster":64513,"attribute":0,"manufacturer":64513,"name":"Mode","type":"select","values":[{"value":3,"name":"on/off"},{"value":4,"name":"hp/hc"}]},

Sous ZHA en natif on ne peut rien faire sur les clusters à ce niveau. Avec l'excellent ZHA Toolkit il y beaucoup plus de possibilités. Encore fallait t'il bien connaitre le domaine Zigbee afin de trouver le type de valeur d'attribut à configurer. Et là je remercie mdeweerd pour sa gentillesse et sa patience (je vous laisse lire l'échange ici).

Mise en place

Très simple !

  1. On commence par installer ZHA Toolkit
  2. Ensuite il n'y a qu'à lancer cette séquence dans les outils de développement (et on replace la valeur de l'attribut à 4 si on veut revenir au mode HP/HC)
service: zha_toolkit.attr_write
data:
  ieee: 00:04:74:00:00:83:f4:f6
  endpoint: 1
  cluster: 64513
  attribute: 0
  attr_val: [3, 0]
  attr_type: 0x09  # Devrait être facultatif si la lecture précède l'écriture.
  # manf: 4129     # Ne pas définir le fabricant car cela ne semble pas nécessaire dans le cas présent.
  event_done: legrand_done
  write_if_equal: false

Bonus

Sur le même principe il est possible de configurer le comportement de la Led. Ca ne m'a pas intéressé mais voici les valeurs.

    {"endpoint" : 1,"cluster":64513,"attribute":1,"manufacturer":64513,"name":"Led dark","type":"select","values":[{"value":0,"name":"Off"},{"value":1,"name":"On"}]}
    {"endpoint" : 1,"cluster":64513,"attribute":2,"manufacturer":64513,"name":"Led if on","type":"select","values":[{"value":0,"name":"Off"},{"value":1,"name":"On"}]}

Consommation

Pour l'instant l'intégration ne présente que la puissance en W ou A (encore que je n'ai pas testé). Je vais voir quand j'aurais du temps si on peut ajouter le cumul en kWh, mais c'ets toujours possible sous HA avec Reiman ou Powercalc.

J'ai passé pas mal de temps (trop car j'ai aussi un job...) sur cette affaire, mais ça m'a permis d'explorer un peu les possibilité de ZHA Toolkit et comprendre qu'il est possible d'intégrer des objets ou des fonctions non reconnues.

Liens

En vrac, mes sources :

 

Home Assistant & Remote Alarm

Ici je vais explorer un nouvel objet qui paraissait simple à exploiter mais s'est avéré un peu compliqué. J'ai acheté une télécommande d'alarme Heiman HS1RC en me disant que l'intégration allait être simple. Sauf que, comme ce clavier, sous ZHA cette télécommande n'est pas vue comme une classique télécommande mais comme un panneau de contrôle d'alarme.

Pourquoi pas, il doit y avoir une raison à ce choix (probablement le bouton du bas qui passe en mode "triggered" quelque soit l'état). Sauf que pour gérer les clavier on configure un code dans ZHA, code que va attendre cette télécommande pour se désarmer. Dans ZHA on peu se passer de code pour armer, mais pas pour désarmer. Et comme le bouton désarmer de la télécommande n'envoie pas ce code. Il va donc falloir passer par une petite automation intermédiaire afin de lui faire manger ce code et ensuite avoir un comportement normal de ce panneau d'alarme pour le désarmement... enfin je pensais que ça suffirait....

- id: '2bd0ertyyf-43fa-45f98f-aed0-heiman-001'
  alias: "Alarm @ Heiman RC Home"
  description: 'Disarm RC Control Panel to use events'
  mode: single
  trigger:
    - platform: event
      event_type: zha_event
      event_data:
        device_ieee: 5c:02:72:ff:fe:e9:2f:ff
        command: 'arm'
        args:
          arm_mode: 0
          arm_mode_description: 'Disarm'
          code: ''
          zone_id: 255
  condition: []
  action:
    - service: alarm_control_panel.alarm_disarm
      data:
        code: !secret alarm_code_zha
      target:
        entity_id: alarm_control_panel.heiman_rc_ef_3_0_alarmcontrolpanel
    - delay : '00:00:05'

A noter qu'il y a une particularité, quand on appuie sur un des 3 boutons du haut ça envoie 3 event's identiques. Bug ZHA ? Je n'ai pas trouvé d'explications vraiment acceptables, certains disent qu'il s'agit d'event's transmis par des équipements relais, d'autres non.... Et le seul contournement que j'ai trouvée pour l'instant consiste à passer mon automation en mode single et d'ajouter un petit delay à la fin afin de ne pas exécuter cette automation trois fois de suite...

Armement de l'alarme

Dans l'automation qui sert à armer on va utiliser l'état de l'entité Alarm Control Panel de la télécommande avec un ID afin de dérouler la séquence souhaitée et tout ce qui s'en suit (on peu imaginer faire de choses différentes selon le bouton sur lequel on appuie) :

    - platform: state
      entity_id: alarm_control_panel.heiman_rc_ef_3_0_alarmcontrolpanel
      to: "armed_home"
      id: "rc_1_home"

A noter que l'on peut également au besoin utiliser to : "triggered" afin de déclencher une autre action immédiate. Genre je me fait agresser quand j'ouvre la porte....

Désarmement de l'alarme

Paradoxalement c'est un peu plus compliqué. En effet si on a armé avec la télécommande et qu'elle se trouve en armed_home il n'y aura pas de soucis quand on va désarmer avec cette même télécommande car elle enverra un disarmed. Par contre si on veut désarmer avec une seconde télécommande ou un clavier qui n'aura pas servi à armer, celui-ci se trouvant déjà en disarmed il n'y aura pas de changement d'état et il ne se passera rien.

Donc dans l'automation qui sert à désarmer on ne va donc pas utiliser l'état de l'entité Alarm Control Panel des différents devices (télécommandes, claviers) mais un input_button: (à créer) :

    - platform: state
      entity_id: input_button.disarm_alarm
      id: "button"

Et cet input_button: sera commandé par l'automation de départ (plus haut) que j'ai adaptée :

  1. Elle se déclenche à partir des event's des différents devices
  2. Elle change l'état de l'entité Alarm Control Panel des différents devices
  3. Elle envoie un input_button.press qui va déclencher l'automation de désarmement.
- id: '2bd0ertyyf-43fa-45f98f-aed0-disarm'
  alias: "Alarm @ Remote Disarm"
  description: 'Disarm Remote Control panel'
  mode: single
  trigger:
    - platform: event
      event_type: zha_event
      event_data:
        device_ieee: 68:0a:e2:ff:fe:ea:88:22 # Keyboard
        command: 'arm'
        args:
          arm_mode: 0
          arm_mode_description: 'Disarm'
          code: !secret alarm_code_zha
          zone_id: 0
    - platform: event
      event_type: zha_event
      event_data:
        device_ieee: 5c:02:72:ff:fe:e9:2f:ba # RC
        command: 'arm'
        args:
          arm_mode: 0
          arm_mode_description: 'Disarm'
          code: ''
          zone_id: 255
  condition: []
  action:
    - service: alarm_control_panel.alarm_disarm
      data:
        code: !secret alarm_code_zha
      target:
        entity_id: alarm_control_panel.lk_zb_keypad_ias_ace
    - service: alarm_control_panel.alarm_disarm
      data:
        code: !secret alarm_code_zha
      target:
        entity_id: alarm_control_panel.heiman_rc_ef_3_0_alarmcontrolpanel
    - service: input_button.press
      target:
        entity_id: input_button.disarm_alarm        
    - delay : '00:00:05'

Ainsi quand on désarme une télécommande on désarme les autres qui seront disponibles pour un futur armement. Et vu qu'on désarme avec un push button, on peu également envoyer depuis Lovelace ou toute autre automation.

Bref, voilà comment perdre un après midi...

Les commentaires de mon blog n'étant pas des plus pratiques, il est également possible d'échanger sur cet article ici, sur HACF.

 

Home Assistant, boost ponctuel du chauffage

Ceux qui avaient lu mes articles précédents consacrés à Schedy ont du remarquer que j'avais introduit un mode BOOST qui permettait de débrayer la planification pendant n minutes et ainsi forcer le thermostat d'une pièce pensant x minutes à une température choisie. Dans la pratique on s'aperçoit que ce mode n'a d'intérêt que dans la salle de bain si l'on déroge aux horaires habituel. Et c'est particulièrement mon cas car je n'ai pas d'horaires figés et je peux très bien avoir envie de me doucher à 3 heures du mat avant de me coucher après avoir végété sur mon canapé face au dernier épisode de la série du moment...

Ma salle de bain est donc toujours en ECO et j'ai créé une automation basée sur un timer: qui va faire le déroulé dès lors qu'il sera lancé. Je démarre le timer directement avec un bouton de télécommande via ControllerX, mais il est également possible de configurer ça dans la liste des triggers. La durée du boost est contenue dans un input_nuber: 

aqara_button:
  module: controllerx
  class: WXKG11LMRemoteLightController
  controller: 00:15:8d:00:01:e7:d5:24
  integration: 
    name: deconz
    listen_to: unique_id
  light: light.off
  mapping:
    1002:
      service: timer.finish
      entity_id: timer.shower    
    1004:
      service: timer.start
      data:
        duration: "{{ states('input_number.sdb_boost_time') | int * 60 }}"
        entity_id: timer.shower

Ensuite la liste des courses...

timer:
  shower:
    name: Boost SdB

input_number:
  sdb_boost_good:
    name: Boost Good
    min: 20
    max: 25
    step: 0.5
    unit_of_measurement: °C
  sdb_boost_max:
    name: Boost Max
    min: 23
    max: 28
    step: 0.5
    unit_of_measurement: °C
  sdb_boost_time:
    name: Boost Time
    min: 15
    max: 90
    step: 5

Et ensuite l'automation avec un choose: avec des actions conditionnées par les id: des trigger:, avec pour objectif de :

  • Au démarrage du timer on passe le thermostat du sèche serviette sur BOOST et on allume le radiateur soufflant et on l'annonce.
  • Si le timer est démarré et que la température de confort est atteinte on l'annonce sur une enceinte Sonos. A la douche !
  • Si la durée du timer est terminée ou que la température maximum est atteinte on éteint le radiateur soufflant et on repasse le sèche serviette en mode ECO.
- id : 'c88f056d-8bbc-40ff-a044-a3b1b733e3c8'
  alias: "RC : Boost SdB"
  description: "Boost SdB by chooser"
  trigger:
    - platform: numeric_state
      entity_id: sensor.rpi_mi_t_sdb
      above: input_number.sdb_boost_good
      id: "t_good"
    - platform: numeric_state
      entity_id: sensor.rpi_mi_t_sdb
      above: input_number.sdb_boost_max
      id: "tmax"
    - platform: event
      event_type: timer.finished
      event_data:
        entity_id: timer.shower
      id: "timer.finished"
    - platform: event
      event_type: timer.started
      event_data:
        entity_id: timer.shower
      id: "timer.started"
  condition: []
  action:
    - choose:
        - conditions: # Condition n°1
            - condition: trigger
              id: "timer.started"
          sequence:
            - service: climate.set_preset_mode
              data:
                preset_mode: boost
              target:
                entity_id: climate.thermostat_salle_de_bain
            - service: switch.turn_on
              entity_id: switch.plug_bw_01
            - service: tts.cloud_say
              entity_id: media_player.sonos_cloud_hall
              data_template:
                message: "Préparation de la salle de bain. Je vous dirait quand vous pourrez vous doucher !"
                cache: 'false'
        - conditions: # Condition n°2
            - condition: and
              conditions:
                - condition: trigger
                  id: "timer.started"
                - condition: trigger
                  id: "t_good" # On annonce que la température de confort+ est atteinte
          sequence:
            - service: tts.cloud_say
              entity_id: media_player.sonos_cloud_hall
              data_template:
                message: "La température de la salle de bain est de {{states('sensor.rpi_mi_t_sdb')}}°, vous pouvez vous doucher !"
                cache: 'false'
        - conditions: # Condition n°3
            - condition: or
              conditions:
                - condition: trigger
                  id: "timer.finished" # On arrete de chauffage à la fin du timer
                - condition: trigger
                  id: "t_max"          # On arrete le chauffage si la température max est atteinte.
          sequence:
            - service: switch.turn_off
              entity_id: switch.plug_bw_01
            - service: climate.set_preset_mode
              data:
                preset_mode: eco
              target:
                entity_id: climate.thermostat_salle_de_bain
            - service: tts.cloud_say
              entity_id: media_player.sonos_cloud_hall
              data_template:
                message: "Fin du chauffage de la salle de bain. A bientôt !"
                cache: 'false'
      default:

Rien d'extraordinaire et tout ça reste à affiner selon les besoin de chacun. On peut également ajouter une condition à cette automation pour qu'elle ne s'exécute qu'en hiver en se basant que l'intégration Season. En même temps les saisons ça ne veut plus dire grand chose, donc dans la pratique il vaudra mieux avoir quelque part un input_boolean: qui globalement autorisera ou non les fonctions liés au chauffage. Pour l'anecdote (vécue) ça évitera, que comme la semaine dernière (fin mai), la condition 2 n'annonce à la femme de ménage qu'elle peut se doucher...

A noter que si comme moi vous comptez remplacer Schedy par une automation du genre de ce dont j'ai parlé ici, il faudra prévoir de débrayer l'automatisme en le passant en manuel dans la condition 1 et en automatique dans la condition 3.

- service: input_select.set_options
  data:
    options: Automatique
  target:
    entity_id: input_select.comfort_sdb

Voilà !

 

Home Assistant, nommage, Energy & SAV.

Sous Home Assistant le module Energy est très pratique, mais il est encore un peu jeune. L'objectif ici est de ne pas perdre la continuité des informations si on doit changer un module ou une prise.

Prenons le cas de mon lave linge. Il est branché sur une prise commandée, ce qui me permet de remonter sa consommation. Pas de chance, le relais de cette prise a lâchée et il me faut la changer. Et je ne veux pas perdre la continuité des informations de consommation du module Energy.

Le nommage

Quand on installe une prise ou un module on peut lui donner un nom personnalisé. Chacun sa méthode, mais idéalement, et avec du recul, je conseillerais de donner aux équipements un nom générique lié au matériel et non un nom lié à la fonction. Ainsi au lieu d'avoir switch.lave_linge je vais nommer ma prise Plug BW 03 et ainsi avoir switch.plug_bw_03, sensor.plug_bw_03_energy, etc. Dans certains cas il faudra renommer les entités manuellement mais cela permettra d'avoir une installation propre.

De plus je conseille de maintenir un tableau d'affection des équipements, inutile sur une toute petite installation, mais indispensable quand on a plusieurs douzaines d'équipements. On y renseignera les IP, MAC, Tokens, Key, etc...

Energy

Pour utiliser le module Energy je préconise de passer par des utility_meter:. Dans la pratique ce sont des compteurs. Cette intégration va enregistrer les cumuls de consommation et c'est celui ci que l'on va déclarer dans le module Energy. Avant cela se faisait via le fichier de configuration en yaml. Depuis quelques releases on peut créer des UM depuis l'interface GUI et même modifier ensuite le Friendly Name (celui apparaitra dans Lovelace ou le module Energy) et même la source.

Je vais donc nommer mon compteur sensor.energy_total_yearly_plug_bw_03, avec comme Frienly Name Lave Linge et l'ajouter au module Energy. Et surtout il y restera, faute de quoi le module Energy perdrait l'historique des données.

En cas de problème avec une source de consommation, il est maintenant possible de changer celle ci en cliquant sur le bouton options. Ca n'a pas toujours été le cas, notamment en yaml ou il m'est arrivé de perdre des données.

Pour être sur d'assurer la continuité quand on doit remplacer un équipement, le plus simple est de supprimer l'ancien (ou le renommer) avant d'appairer le nouveau. Et ensuite de vérifier que le nouveau a bien toutes les entités avec le même nommage que le précédent, en ajustant le cas échéant.

Parfois il sera impossible de supprimer un module dans Home Assistant et il faudra taper dans le dur (fichiers cachés...), comme par exemple ce qui remonte de Deconz ou même en supprimant un module dans Phoscon celui-ci ne disparait pas toujours de HA tant que l'on aura pas supprimé l'intégration... (il y a un contournement qui consiste à appairer le module en ZHA, le supprimer de ZHA, ce qui le supprimera de ZHA et Deconz, et ensuite le réappairer. Tordu, mais ça fonctionne).

 

Home Assistant, planification, encore...

J'ai souvent parlé de planification des convecteurs et du climatiseur. Depuis un an je tourne sur deux installations avec ce dont j'avais parlé ici en utilisant le moteur de planification Schedy. Schedy était très bien en son temps et il palliait à des lacunes de Home Assistant. Mais il y a quelques restrictions et son auteur ne le fait plus évoluer. En fait ça se comprend car on peut maintenant à peu près faire la même chose avec le mode choose: et quelques lignes de yaml, et ainsi supprimer une dépendance à un outil externe. Et plus c'est simple, moins il y a de chances de dysfonctionnements.

Je vais donc changer de moteur, par contre je vais réutiliser toutes les entités que j'avais crée pour Schedy, ce qui dans l'absolu en fait une solution réversible. Attention, cet article a été remanié, donc si vous vous étiez inspiré de la première version il sera judicieux de repartir à zéro...

Voici donc ce que j'ai fait pour le climatiseur, plus complexe qu'un simple radiateur car on va gérer les modes chauffage et refroidissement ainsi que le débrayage en manuel.

input_select:
  comfort_ac:
    name: Modes du climatiseur
    icon: mdi:form-select
    options:
      - Refroidissement
      - Chauffage
      - Manuel
      - "Off"

On va utiliser deux automations par pièce ou appareil, la première va gérer les multiples déclencheurs (trigger:), les modes (input_select:) et surtout les exclusions liées au mode de vie (absence de courte ou longue durée, sommeil, fenêtres ouvertes, etc...

Si aucune exclusion n'est validée et selon le mode en cours on lance la seconde automation dans le mode sélectionné. Si on est en mode Manuel, on ne fait rien, mais la seconde automation ne s'exécutera pas.

- id: 56ee275e-3f52-4d88-a862-5e21f5708a82
  description: Comfort - AC - Immediate
  alias: "Comfort - AC - Immediate"
  mode: restart
  trigger:
    - platform: state
      entity_id:
        - input_number.heating_ac_temperature_confort_1
        - binary_sensor.heating_ac_1
        - input_boolean.to_away
        - binary_sensor.life_windows_and_doors_delayed
        - input_boolean.to_sleep
        - binary_sensor.lionel_geo
        - input_select.comfort_ac
        - [...] ⚠️ A compléter 
  condition:
  action:
    - choose:
        - conditions: # 0 OFF
            - condition: or
              conditions:
                - "{{ is_state('binary_sensor.life_windows_and_doors_delayed', 'on') }}"
                - "{{ is_state('input_select.comfort_ac', 'Off') }}"
                - "{{ is_state('input_boolean.presence_ac', 'off') }}"
          sequence:
            - service: climate.set_hvac_mode
              data:
                hvac_mode: 'off'
              target:
                entity_id: climate.daikin

        - conditions: # 01 - COOL OFF
            - condition: or
              conditions:
                - "{{ is_state('input_boolean.thermostats_away', 'on') }}"
                - "{{ is_state('input_boolean.to_away', 'on') }}"
                - "{{ is_state('binary_sensor.lionel_geo', 'off') }}"
            - condition: 
                - "{{ is_state('input_select.comfort_ac', 'Refroidissement') }}"
          sequence:
            - service: climate.set_hvac_mode
              data:
                hvac_mode: 'off'
              target:
                entity_id: climate.daikin

        - conditions:  # 02 - COOL SLEEP
            - "{{ is_state('input_boolean.to_sleep', 'on') }}"
            - "{{ is_state('input_select.comfort_ac', 'Refroidissement') }}"
          sequence:
            - service: climate.set_hvac_mode
              data:
                hvac_mode: 'cool'
              target:
                entity_id: climate.daikin
            - service: climate.set_temperature
              data:
                temperature: "{{ states('input_number.heating_ac_temperature_eco') }}"
              target:
                entity_id: climate.daikin                

        - conditions:  # 03 - HEAT ECO
            - condition: or
              conditions:
                - "{{ is_state('input_boolean.to_away', 'on') }}"
                - "{{ is_state('input_boolean.to_sleep', 'on') }}"
                - "{{ is_state('binary_sensor.lionel_geo', 'off') }}" # input_boolean.presence_lionel_geo
            - condition: 
                - "{{ is_state('input_select.comfort_ac', 'Chauffage') }}"
          sequence:
            - service: climate.set_hvac_mode
              data:
                hvac_mode: 'heat'
              target:
                entity_id: climate.daikin
            - service: climate.set_temperature
              data:
                temperature: "{{ states('input_number.heating_ac_temperature_eco') }}"
              target:
                entity_id: climate.daikin

        - conditions: # 04 - HEAT AWAY
            - "{{ is_state('input_boolean.thermostats_away', 'on') }}"
            - "{{ is_state('input_select.comfort_ac', 'Chauffage') }}"
          sequence:
            - service: climate.set_hvac_mode
              data:
                hvac_mode: 'heat'
              target:
                entity_id: climate.daikin
            - service: climate.set_temperature
              data:
                temperature: "{{ states('input_number.heating_ac_temperature_away') }}"
              target:
                entity_id: climate.daikin

      default:
        - choose:
            - conditions: "{{ is_state('input_select.comfort_ac', 'Refroidissement') }}"
              sequence:
                - service: climate.set_hvac_mode
                  data:
                    hvac_mode: 'cool'
                  target:
                    entity_id: climate.daikin
                - service: automation.trigger
                  target:
                    entity_id: "automation.comfort_ac"

            - conditions: "{{ is_state('input_select.comfort_ac', 'Chauffage') }}"
              sequence:
                - service: climate.set_hvac_mode
                  data:
                    hvac_mode: 'heat'
                  target:
                    entity_id: climate.daikin
                - service: automation.trigger
                  target:
                    entity_id: "automation.comfort_ac"

La seconde automation sert à ajuster la température de consigne, elle est appelée par la première pour une action immédiate et se relancera également toutes les 10 minutes afin de rattraper une action non exécutée, un redémarrage ou un actionneur qui n'a pas fait son travail. Ca compense la replanification qui faisait la force de Schedy.

Qu'elle soit appelée par la première ou qu'elle s'exécute via le time_pattern: il ne se passera rien si toutes (and: par défaut) les conditions ne sont pas remplies (les contraintes gérées dans la première automation). A noter que je place les conditions après action: afin que ces conditions soient prises en compte même si cette automation est lancée manuellement.

Si aucune des conditions liées aux plages horaires actives n'est remplie (plages horaires définies par des binary_sensor: dans le précédent article), alors l'action par défaut passe le thermostat en mode ECO.

automation:
- id: 0ff7454c-4592-4479-b2ae-40eaf3043853
  description: Comfort - AC
  alias: "Comfort - AC"
  mode: restart
  trigger:
    - platform: time_pattern
      minutes: "/10"
  condition:
  action:
    - condition:
        - "{{ not states('input_select.comfort_ac') in ('Manuel', 'Off') }}"
        - "{{ is_state('binary_sensor.life_windows_and_doors_delayed', 'off') }}"
        - "{{ is_state('binary_sensor.lionel_geo', 'on') }}"
        - "{{ is_state('input_boolean.thermostats_away', 'off') }}"
        - "{{ is_state('input_boolean.presence_ac', 'on') }}"
        - "{{ is_state('input_boolean.to_away', 'off') }}"
        - "{{ is_state('input_boolean.to_sleep', 'off') }}"
    - choose:
        - conditions: "{{ is_state('binary_sensor.heating_ac_1', 'on') }}" # Plages horaires
          sequence:
            - service: climate.set_temperature
              data:
                temperature: "{{ states('input_number.heating_ac_temperature_confort_1') }}"
              target:
                entity_id: climate.daikin
        - conditions:  [...] ⚠️ A Compléter
      default:
        - choose:
            - conditions:
              sequence:
                - service: climate.set_temperature
                  data:
                    temperature: "{{ states('input_number.heating_ac_temperature_eco') }}"
                  target:
                    entity_id: climate.daikin

Voilà, c'est finalement assez simple et il suffira ensuite de reproduire pour les autres pièces. Je vous conseille d'utiliser un fichier par pièce dans les packages.

Un grand merci à Fabien, Mathieu et Philipp m'ont ouvert les yeux sur les possibilités offertes par choose: et leur patience quand je sèche sur les templates...

 

Bibliothèque Sonos

L'écosystème Sonos a bien évolué au fil des années, avec du positif comme du négatif ( 1 | 2 ), mais tout en intégrant une multitude de services musicaux, Sonos a toujours délaissé, le nombre de fichiers musicaux locaux explorables. En l'état cette limite est toujours fixée à 65.000 fichiers alors même que l'évolution Sonos 2 permettrait certainement de s'en affranchir.

J'ai longtemps utilisé Subsonic (ou ses forks) qui permet de contourner cette limite. Mais d'une part Subsonic est payant, et surtout n'est plus maintenu.

Aujourd'hui il existe un nouveau fork open source, Navidrome, qui est compatible avec les clients Subsonic, mais hélas ne propose pas de compatibilité Sonos. Fort heureusement un autre développeur de génie a eu la bonne idée de mettre à disposition une interface, Bonob, via les API Sonos, qui va permettre de faire le lien entre Sonos et les fors de Subsonic, en l'occurrence ici Navidrome.

Si ces programmes sont installables sous Linux, Windows ou MacOS, je vais choisir la facilité en passant par Docker. Pour y parvenir je commence par installer une petite VM Ubuntu Serveur avec Docker installé, et comme mes fichiers musicaux sont sur mon Nas je vais le lier en créant un volume NFS.

On commence par installer les paquets NFS :

[email protected]:~# sudo apt install nfs-common

Ensuite on crée le répertoire idoine et on le lie au Nas :

[email protected]:~# sudo mkdir -pv /nas/Music
[email protected]:~# sudo mount 192.168.0.241:/volume1/Music /nas/music

Et pour terminer cette partie on fige le montage NFS en éditant le fichier /etc/fstab et en y ajoutant une ligne :

[email protected]:~# sudo nano /etc/fstab
192.168.0.22:/volume1/Music /nas/music nfs auto,nofail,noatime,nolock,intr,tcp,actimeo=1800 0 0

Navidrome

On commence par créer un répertoire de travail ou sera installé le cache :

[email protected]:~# sudo mkdir -pv /navidrome/data/

On installe le docker Navidrome avec la commande suivante :

sudo docker run -d \
  --name navidrome \
  --restart=unless-stopped \
  --user $(id -u):$(id -g) \
  -v /nas/music:/music \
  -v /data/navidrome:/data \
  -p 4533:4533 \
  -e ND_LOGLEVEL=info \
  deluan/navidrome:latest

Il est possible de ne pas indiquer le user à des fin de test et debug, mais je ne le conseille pas de fonctionner en root. On retrouve ici nos deux répertoires, /data/navidrome pour le cache et /nas/music pour les fichiers musicaux qui pointe sur le NAS. Il y a pas mal d'autres options plus ou moins intéressantes que l'on pourra ajouter ici ou dans un fichier de configuration.

Il ne reste plus qu'à se connecter sur http://ip_serveur:4533 ... Il est bien sur possible de passer en SSL avec un reverse proxy (un petit Docker de plus...), mais également de créer des comptes secondaire pour d'autres utilisateurs qui pourront également télécharger fichiers ou albums.

Bonob

C'est ici que ça devient intéressant pour Sonos. Et ça se passe également sous Docker avec à minima :

sudo docker run -d \
  --name bonob \
  --restart=unless-stopped \
  -e BNB_PORT=4534 \
  -e BNB_URL=http://192.168.0.33:4534 \
  -e BNB_SONOS_SERVICE_NAME=Canaletto \
  -e BNB_SONOS_SEED_HOST=192.168.0.57 \
  -e BNB_SONOS_AUTO_REGISTER=true \
  -e BNB_SONOS_DEVICE_DISCOVERY=true \
  -e BNB_SUBSONIC_URL=http://172.17.0.2:4533 \
  -p 4534:4534 \
  simojenki/bonob

Il y a quelques options qui méritent explication :

  • BNB_URL= l'url du service Bonob afin de le faire savoir à Sonos
  • BNB_SONOS_SERVICE_NAME= Le nom du service qui apparaitra dans Sonos

  • BNB_SONOS_SEED_HOST= L'IP (fixe) d'un équipement Sonos permanent

  • BNB_SUBSONIC_URL= L'url interne à docker de Navidrome

Pour le reste je vous renvoi au GitHub afin d'adapter votre configuration.

A ce stade il suffit d'aller dans l'interface Sonos et d'ajouter le service que l'on viens de créer

Et de s'authentifier avec le compte précédemment créé dans Navidrome et de profiter de votre bibliothèque (MP3, FLAC, etc). Navidrome va indexer la bibliothèque et servir de cache. Je trouve les temps de recherche excellents au regard des 192 217 fichiers de ma bibliothèque répartis dans 15 753 répertoires. Il est possible dans Navidrome de créer des listes de lecture et des favoris que l'on retrouvera sous Sonos, par contre il n'est pas possible d'explorer l'arborescence des fichiers comme le permet Sonos dans son service de base.

Selon l'échantillonnage et le transcodage souhaité il faudra peut être modifier la configuration de Navi drome pour s'y adapter.

Home Assistant, timers & choose

Il y a bientôt deux ans, peu après mes débuts sous Home Assistant, j'ai mis en place un scénario de confort pour la douche. En gros la salle de bain est toujours en mode ECO et quand je souhaite prendre une douche j'appuie sur un bouton, ça lance le chauffage et un radiateur soufflant et ça l'éteint au bout d'un moment.

Aujourd'hui j'ai voulut l'améliorer afin de le lancer quand je dois me lever à une heure fixe, ce qui heureusement est plutôt rare. J'ai donc fait une automation qui se déclenche à partir d'un input_datetime: qui va chauffer la salle de bain, lancer la clim en mode chauffage via un script de dérogation de Schedy, éclairer quelques lampes et ouvrir quelques volets. Rien d'extraordinaire à ce stade et il faudrait y ajouter de la musique et allumer la bouilloire pour le thé....

automation:
- id: 'fab3sdfty-bgg6-4ddc-a23f-ee589300012c'
  alias: "RC : Réveil + douche"
  description: ''
  trigger:
    platform: time
    at: input_datetime.alarm_clock_date_time
  condition:
  action:
  - service: timer.start              # On lance le chauffage de la SdB
    data:
      duration: 00:25:30
    target:
      entity_id: timer.shower
  - service: input_boolean.turn_off   # On passe en mode jour (utile pour le chauffage)
    target:
      entity_id: input_boolean.to_sleep 
  - service: script.heating_ac_boost
  - delay: 00:10:00                   # On se prélasse encore un peu au lit...
  - service: cover.set_cover_position # On entreouvre le volet de la chambre
    data:
      position: 22
    target:
      entity_id:
        - cover.vr_lionel
  - service: cover.set_cover_position # On ouvre le volet de la cuisine
    data:
      position: 60
    target:
      entity_id:
        - cover.vr_cuisine      
  - service: light.turn_on            # On allume quelques lampes
    data:
      color_name: yellowgreen
    target:
      entity_id: light.groupe_d_ampoules_led

Là ou ça va devenir intéressant c'est que j'ai refondu mes automations de douche en une seule qui va utiliser un timer: et la fonction choose:. Je n'avais jamais utilisé de timer et encore moins le chooser, j'ai donc un peu galéré, mais voici le résultat, largement simplifié avec les trigger_id:, ce qui évite des templates...

automation:
- id : 'c88f056d-8bbc-40ff-a044-a3b1b733e3c8'
  alias: "RC : Boost SdB"
  description: "Boost SdB by chooser"
  trigger:
    - platform: numeric_state
      entity_id: sensor.rpi_mi_t_sdb
      above: input_number.sdb_boost_good
      id: "t_good"
    - platform: numeric_state
      entity_id: sensor.rpi_mi_t_sdb
      above: input_number.sdb_boost_max
      id: "tmax"
    - platform: event
      event_type: timer.finished
      event_data:
        entity_id: timer.shower
      id: "timer.finished"
    - platform: event
      event_type: timer.started
      event_data:
        entity_id: timer.shower
      id: "timer.started"
  condition: []
  action:
    - choose:
        - conditions:
            - condition: trigger
              id: "timer.started" # Mode ON sur la base su timer qui est lancé depuis une télécommande ou une autre automation
          sequence:
            - service: climate.set_preset_mode
              data:
                preset_mode: boost
              target:
                entity_id: climate.thermostat_salle_de_bain
            - service: switch.turn_on
              entity_id: switch.plug_bw_01
            - service: notify.slack_hass_canaletto
              data:
                message: "{{now().strftime('%d/%m/%Y, %H:%M')}} > BOOST | START | Salle de bain | Température : {{ states('sensor.rpi_mi_t_sdb') }}°"
            - service: tts.cloud_say
              entity_id: media_player.sonos_cloud_hall
              data_template:
                message: "Préparation de la salle de bain. Je vous dirait quand vous pourrez vous doucher !"
                cache: 'false'
        - conditions:
            - condition: trigger
              id: "t_good" # On annonce que la température de confort+ est atteinte
          sequence:
            - service: tts.cloud_say
              entity_id: media_player.sonos_cloud_hall
              data_template:
                message: "La température de la salle de bain est de {{states('sensor.rpi_mi_t_sdb')}}°, vous pouvez vous doucher !"
                cache: 'false'
            - service: notify.slack_hass_canaletto
              data:
                message: "{{now().strftime('%d/%m/%Y, %H:%M')}} > BOOST | READY | Salle de bain | Température : {{ states('sensor.rpi_mi_t_sdb') }}°"
        - conditions:
            - condition: or
              conditions:
                - condition: trigger
                  id: "timer.finished" # On arrete de chauffage à la fin du timer
                - condition: trigger
                  id: "t_max"          # On arrete le chauffage si la température max est atteinte.
          sequence:
            - service: switch.turn_off
              entity_id: switch.plug_bw_01
            - service: climate.set_preset_mode
              data:
                preset_mode: eco
              target:
                entity_id: climate.thermostat_salle_de_bain
            - service: notify.slack_hass_canaletto    
              data:
                message: "{{now().strftime('%d/%m/%Y, %H:%M')}} > BOOST | END | Salle de bain | Température : {{ states('sensor.rpi_mi_t_sdb') }}°"
            - service: tts.cloud_say
              entity_id: media_player.sonos_cloud_hall
              data_template:
                message: "Fin du chauffage de la salle de bain. A bientôt !"
                cache: 'false'
      default:

Le log dans Slack me sert au debug. Le TTS via l'intégration Sonos Cloud permet de baisser la musique et de la réactiver une fois l'annonce terminée.

On notera également que par défaut les conditions du chooser son and et qu'il est possible de les passer en or.

Si aucun trigger n'est utilisé on peut également définir une action par défaut. J'ai fait le choix de ne pas l'utiliser et d'activer ou désactiver le chauffage en lançant le timer pour une certaine durée, depuis un bouton (via ControlerX ou un BluePrint) ou l'automation du réveil : 

  - service: timer.start
    data:
      duration: 00:25:30
    target:
      entity_id: timer.shower

ou en forçant sa fin...

  - service: timer.finish
    target:
      entity_id: timer.shower

Et pour paramétrer une ligne sur Lovelace, à noter que j'ai oublié d'intégrer un template pour la durée dans les commandes ci dessus.

 

Home Assistant & Keypad

Notre serveur domotique préféré inclus un système de sécurité intrusion, communément appelé alarme. Attention, c'est du DIY, ça fait le job, mais ça ne répond pas aux normes en vigueur :

Les systèmes d'alarmes sont évalués en fonction de divers critères donnant lieu à l'attribution d'une certification appelée "norme alarme". En France, les alarmes sont certifiées par le CNPP, qui leur attribue des normes : NFA2P bouclier 1, NFA2P bouclier 2, NFA2P bouclier 3, suivant le degré de sécurité. Au niveau européen, il existe également une norme alarme : la norme EN 50131.

Ceci étant posé, rappelons de Home Assistant intègre un panneau d'alarme basic que l'intégration Alarmo vient avantageusement compléter, et dans l'absolu ça fait mieux que beaucoup de produit du marché.

Pour une bonne efficacité on part du principe que l'installation Home Assistant est fiable et secourue par un UPS.

Dans cet article je ne vais pas détailler le fonctionnement de cette alarme mais m'intéresser aux différentes façons de l'activer / désactiver :

  • Via l'application mobile : facile, mais fastidieux à l'usage. Tous les occupants ne disposent pas nécessairement de l'application mobile Home Assistant.
  • Avec un bouton ou une télécommande : facile, mais il faut transporter l'objet et les distribuer.
  • Avec un badge ou tag RFID sur un lecteur : lecteur esp32 à configurer, il faut transporter l'objet. Facile à distribuer et à révoquer.
  • Avec un badge et un téléphone mobile : facile et sécurisé, il faut enregistrer au préalable les mobiles qui devront disposer de l'application Home Assistant..
  • Avec un clavier numérique : le mode classique et universel, changement des codes faciles, encore faut t'il trouver un clavier, et c'est l'objet de cet article.

Un keypad Zigbee compatible

Il y a quelques jours j'ai vu passer une vidéo qui parle d'un kit alarme Linkind qui utlise Zigbee. Ce kit est composé d'une sirène qui est en fait un hub Zigbee qui se connecte au cloud du fabricant chinois pour gérer l'ensemble, d'un clavier et de quelques détecteurs. L'auteur de la vidéo à utilisé zigbee2mqtt et Node Red (je n'aime pas) pour l'intégrer à Home Assistant, moi je vais essayer de faire ça via ZHA.

J'ai commandé ce kit chez Amazon (28 €) en me disant qu'au pire ce serait un retour de plus, mon idée étant d'utiliser le clavier. Il y a des travaux en cours sur le Hub / Sirène mais c'est loin d'être aboutit. En ce qui concerne les capteurs il est possible de les associer via ZHA (ou z2m et Deconz).

Premier problème que je n'ai pas résolu, le code (1234 par défaut) qui se change via l'application du constructeur. Sauf que quand on l'intègre avec ZHA ça passe par un reset et on se retrouve avec le code par défaut. Bref un clavier avec comme code 1234 ça ne servirait pas à grand chose. Mais il y a une astuce, et la voici : (et une info à la fin de cet article)

Quand on entre une information sur le clavier, celle ci est envoyée à Home Assistant via ZHA (ça doit fonctionner à l'identique avec Deconz, sauf que pour l'instant il n'est pas reconnu) et on peut la récupérer via les "events". Voici un exemple si je saisit Disarm + 1234 + Valid sur ce clavier :

{
    "event_type": "zha_event",
    "data": {
        "device_ieee": "69:0a:x2:ff:fe:xa:x8:22",
        "unique_id": "69:0a:e2:xf:xe:xa:88:2x:1:0x05xx",
        "device_id": "c036fgqd qfdqs56hshs56shsdd06152267ab",
        "endpoint_id": 1,
        "cluster_id": 1281,
        "command": "arm",
        "args": {
            "arm_mode": 0,
            "arm_mode_description": "Disarm",
            "code": "1111",
            "zone_id": 0
        }
    },
    "origin": "LOCAL",
    "time_fired": "2022-02-21T23:27:45.169891+00:00",
    "context": {
        "id": "edada8770ddfd7045b1835acb0888bad",
        "parent_id": null,
        "user_id": null
    }
}

A partir de là il est facilement possible de traiter cette information dans une automation et de générer une action, ici un message :

automation:
- alias: Keypad Test
  description: 'Triggers an Event When code 1111 is entered into any keypad.
  trigger:
    - platform: event
      event_type: zha_event
      event_data:
        command: 'arm'
        args:
          arm_mode: 0
          arm_mode_description: 'Disarm'
          code: '1111'
          zone_id: 0
  condition: []
  action:
    - service: notify.slack_hass_canaletto
      data:
        message: "{{now().strftime('%d/%m/%Y, %H:%M:%S')}} > ENTER HOME | Code 1111 | State : {{ states.alarm_control_panel.alarmo.state }}"

Et là ou ça devient intéressant, c'est que l'on peut saisir n'importe quoi et que ça sera toujours remonté via un event. A partir de là on peu faire passer tous les codes possibles via 2 pseudos modes d’armement, voire même le disarm.

  • Arm_All_Zones
  • Arm_Day_Home_Only

Bon c’est un peu du bricolage… mais ça fait le taff. Dans la pratique, de base le désarmement demande le code enregistré dans le clavier (1234 par défaut) on peu armer les deux modes à la volée avec n'importe quel code, par exemple :

Touche Arm Home + 4444 + Valid va envoyer un event avec :

"args": {
            "arm_mode": 1,
            "arm_mode_description": "Arm_Day_Home_Only",
            "code": "4444",
            "zone_id": 0

Touche Arm Away + 5555 + Valid va envoyer un event avec :

"args": {
            "arm_mode": 3,
            "arm_mode_description": "Arm_All_Zones",
            "code": "5555",
            "zone_id": 0

A partir de là on interprète le code avec une automation et on lui fait faire ce que l’on veut très simplement.

automation:
  alias: Keypad Test
  description: 'Triggers an Event When code 1111 is entered into any keypad.'
  trigger:
    - platform: event
      event_type: zha_event
      event_data:
        command: 'arm'
        args:
          arm_mode: 0
          arm_mode_description: 'Disarm'
          code: '1111'
          zone_id: 0
  condition: []
  action:
  - service: alarm_control_panel.alarm_disarm
    data:
      code: !secret alarm_code
    entity_id: alarm_control_panel.alarmo
  - service: alarm_control_panel.alarm_disarm
    data:
      code: !secret alarm_code_visonic
    entity_id: alarm_control_panel.visonic_alarm
  - service: notify.slack_hass_canaletto
    data:
      message: "{{now().strftime('%d/%m/%Y, %H:%M:%S')}} > ENTER HOME | DISARM ALARM's | State : {{ states.alarm_control_panel.alarmo.state }}" 

Bonus

Rester appuyé 3 secondes sur SOS, ça active le Panel alarme Linkind dans HA et on peut traiter l'information.

Edit 05/07/2022

Je ne m'étais pas repenché sur la question de ce code 1234 par défaut depuis l'écriture de cet article. Mais Aurel RV a creusé un peu plus et à découvert par hasard que ZHA sait gérer ce code par défaut. Depuis quand je ne sais pas.

Le clavier étant vu comme un panneau de contrôle d'alarme, on pourra interagir avec Alarmo ou continuer à se servir des autres codes vie les events pour commander d'autres actions...

Conclusion

Ce résultat n'est pas totalement satisfaisant (mais qui le devient depuis que ZHA gère ce code), mais ça permet de faire passer les bons codes à Alarmo et c'est utilisable. De plus on peut utiliser d'autres codes pour déclencher d'autres actions, ouvrir un portail, allumer des projecteurs, etc....

 

 

 

 
 

 

 

Home Assistant & Volets roulants

Voilà un chapitre que je n'avais pas encore abordé, et pour cause je n'(avait pas de volets roulants. J'ai donc pensé mon installation de volets roulants avec la contrainte (ou la présence) de la domotique, tout en excluant pas un fonctionnement autonome.

Dans le cadre d'une isolation par l'extérieur (ITE), j'ai fait le choix de volets roulants filaires, d'une par afin d'éviter un surcout, mais surtout afin de pouvoir les commander à ma guise. Pour la commande j'ai choisit de faire ramener tous les câbles au tableau électrique afin que les modules Shelly 2.5 soient accessibles et surtout qu'ils ne soient pas exposés à la chaleur, le froid et l'humidité s'ils étaient placés dans les caissons. Les 6 Shelly 2.5 sont montés sur des supports DIN imprimés en 3D et j'ai trouvé des câbles en 4 fils souples en 0.75, ce qui est tout de même facile pour câbler l'ensemble dans le tableau.

Vous remarquerez l'utilisation d'embout de câblage pour câble souple multibrins. Utiliser du câble souple en 0.75 est largement suffisant, mais on ne câble jamais du fil souple multibrin directement sur un module ou un disjoncteur, soit on utilise ce genre d'embouts, soit on étame.

J'ai pensé le tout afin que mon installateur (qui a joué le jeu et c'est rare pour le préciser) n'ait qu'à poser les volets et les raccorder dans les boites de dérivation qu'il a au préalable posées dans les combles. Vous l'aurez compris il n'y a pas d'inverseur sous les volets, il était hors de question de poser des baguettes en plastique dans une maison ou tout est encastré. La commande de ces volets se fera donc soit par des scénarios, soit par des télécommandes sans fil via Home Assistant.

Un mode autonome

Il faut bien sur penser à la possibilité ou Home Assistant est défaillant, ou simplement au jour ou je ne serais plus là pour le maintenir. Pour ça les Shelly 2.5 permettent le câblage d'inverseurs filaires et je vais installer 6 inverseurs au tableau (voir plus bas en bonus).

Une alternative serait de piloter les 6 Shelly 2.5 par des poussoirs sur des Shelly E3, mais on ne s'affranchit pas d'une panne de WI-FI, et tant qu'à penser une commande autonome autant qu'elle le soit à 100%.

Le mode assisté

Pour le mode sans fil je vais utiliser des télécommandes on/off de chez Ikea à 5.99 € pour les chambres et une télécommande à 5 boutons pour les pièces de vie. En Zigbee elle sont appairées sous Home Assistant avec ZHA, mais le process est identique avec Z2M ou Deconz.

Quant au contrôle de ces télécommandes je vais simplement utiliser l'excellent ControlerX sous appDaemon que j'utilise déjà avec satisfaction pour les éclairages.

Ca reste très simple et le contrôle est dynamique, voici un exemple pour la télécommande on/off Ikea, comme vous pouvez le voir, en 6 lignes c'est géré :

volet_zha_ikea_05:
  module: controllerx
  class: E1743CoverController
  controller: "8c:f6:81:ff:fe:51:d0:b4"
  integration: zha
  cover: cover.vr_marie

Attention, certaines version de cette télécommande on on problème qui conduit sous ZHA ou Zigbee2MQTT à un rapide (24 h.) vidage des piles. J'ai donc été contraint de toutes les repasser sous Deconz...

volet_rc_ikea_05:
  module: controllerx
  class: E1743CoverController
  controller: 8c:f6:81:ff:fe:51:d0:b4
  integration: 
    name: deconz
    listen_to: unique_id
  cover: cover.vr_marie

Pour la télécommande à 5 boutons on va commencer par gérer le choix du volet à commander via un input_select:, et donc créer celui ci :

input_select:
  rc_ikea_vr:
    name: Select
    icon: mdi:light-switch
    options:
      - vr_baie
      - vr_sejour
      - vr_cuisine
      - vr_antoine
      - vr_lionel
      - vr_marie

Ensuite on va créer une commande pour ControlerX qui va nous permettre de sélectionner un volet avec les touches < et >. Inconvénient ça se fait à l'aveugle, mais ça permet de tout rassembler dans une seule télécommande :

select_vr_zha:
  module: controllerx
  class: Controller
  controller: 84:2e:14:ff:fe:a9:4d:bd
  integration: zha
  mapping:
    press_257_13_0:
      service: input_select.select_previous
      data:
        entity_id: input_select.rc_ikea_vr
    press_256_13_0:
      service: input_select.select_next
      data:
        entity_id: input_select.rc_ikea_vr

Ensuite on va les commandes propres à chaque volet (voir ici pour les mappings propres à chaque source Zigbee), on y place des contraintes (la position de notre input_select) et on exclu les actions droite et gauche que l'on utilise pour le choix du volet à commander. Il faudra bien sur dupliquer cette commande en fonction du nombre de volets. :

vr_app_1_zha:
  module: controllerx
  class: CoverController
  controller: 84:2e:14:ff:fe:a9:4d:bd
  integration: zha
  cover: cover.vr_cuisine
  constrain_input_select: input_select.rc_ikea_vr,vr_cuisine
  excluded_actions: [press_257_13_0, press_256_13_0]
  mapping:
    step_with_on_off_0_43_5: toggle_open
    step_1_43_5: toggle_close

Il nous reste le bouton central de cette télécommande qui est inexploité. On va s'en servir pour ouvrir ou fermer l'ensemble des volets de la maison en créant un groupe de volets avec l'intégration Cover Group :

cover:
  - platform: group
    entities:
      - cover.vr_baie
      - cover.vr_sejour
      - cover.vr_cuisine
      - cover.vr_antoine
      - cover.vr_lionel
      - cover.vr_marie

Et on poursuit avec la commande idoine pour ControlerX, il n'y a pas de contrainte sur l'input_select car le bouton central est utilisable dans toutes les positions :

vr_app_0_zha:
  module: controllerx
  class: CoverController
  controller: 84:2e:14:ff:fe:a9:4d:bd
  integration: zha
  cover: cover.volets
  excluded_actions: [press_257_13_0, press_256_13_0]
  mapping:
    press_2_0_0 : toggle_close
    toggle: toggle_open

Commandes

Pour l'affichage je me suis simplement inspiré de de post. Merci à lui !

L'avantage c'est qu'il y a un preset à x%. On pourrait facilement faire plusieurs. Voici le code de la carte en Vertical Stack :

type: grid
cards:
  - type: button
    show_name: false
    entity: cover.volets
    show_state: true
    show_icon: true
    hold_action:
      action: none
    tap_action:
      action: more-info
    theme: teal
  - type: button
    tap_action:
      action: call-service
      service: cover.open_cover
      service_data: {}
      target:
        entity_id: cover.volets
    show_name: false
    name: Ouvrir
    icon: mdi:arrow-up-bold
  - type: button
    tap_action:
      action: call-service
      service: cover.stop_cover
      service_data: {}
      target:
        entity_id: cover.volets
    show_name: false
    name: Stop
    icon: mdi:pause
  - type: button
    tap_action:
      action: call-service
      service: cover.close_cover
      service_data: {}
      target:
        entity_id: cover.volets
    show_name: false
    name: Fermer
    icon: mdi:arrow-down-bold
  - type: button
    tap_action:
      action: call-service
      service: cover.set_cover_position
      service_data:
        position: 20
      target:
        entity_id: cover.volets
    show_name: false
    name: Soleil
    icon: mdi:weather-sunny
  - type: picture-elements
    image: /local/images/1px2.png
    elements:
      - type: state-label
        entity: cover.volets
        attribute: current_position
        suffix: '%'
        tap_action:
          action: more-info
        style:
          top: 50%
          left: 50%
          font-size: 16px
          font-weight: bold
          color: '#44739E'
    view_layout:
      position: sidebar
columns: 6
square: true

Il ne faut pas oublier une petite image de  pixel. Et la seconde partie à dupliquer par le nombre de volets :

type: grid
cards:
  - type: button
    show_name: true
    name: Baie
    show_state: false
    tap_action:
      action: more-info
    entity: cover.vr_baie
    hold_action:
      action: none
    theme: teal
  - type: button
    tap_action:
      action: call-service
      service: cover.open_cover
      service_data: {}
      target:
        entity_id: cover.vr_baie
    icon: mdi:arrow-up-bold
    name: Ouvrir
    show_icon: true
    show_name: false
  - type: button
    tap_action:
      action: call-service
      service: cover.stop_cover
      service_data: {}
      target:
        entity_id: cover.vr_baie
    icon: mdi:pause
    name: Stop
    show_name: false
  - type: button
    tap_action:
      action: call-service
      service: cover.close_cover
      service_data: {}
      target:
        entity_id: cover.vr_baie
    icon: mdi:arrow-down-bold
    name: Fermer
    show_name: false
  - type: button
    tap_action:
      action: call-service
      service: cover.set_cover_position
      service_data:
        position: 70
      target:
        entity_id: cover.vr_baie
    icon: mdi:weather-sunset-up
    name: Soleil
    show_name: false
  - type: picture-elements
    image: /local/images/1px2.png
    elements:
      - type: state-label
        entity: cover.vr_baie
        attribute: current_position
        suffix: '%'
        tap_action:
          action: more-info
        style:
          top: 50%
          left: 50%
          font-size: 16px
          font-weight: bold
          color: '#44739E'
    view_layout:
      position: sidebar
square: true
columns: 6

Optimisation

Disposer de volets roulants électriques permet également une optimisation du confort thermique. Pour résumer on peut gagner en température en gérant leurs positions en fonction de l'ensoleillement. Il existait sous d'autres solutions domotiques des "choses" permettant cette optimisation, et je sais que quelque chose se prépare sous Home Assistant, on en reparlera donc bientôt.

Sécurisation

Les volets roulants étant moins sécurisants que mes vieux volets en vrai bois, je vais installer des contacteurs filaires afin de détecter un éventuel arrachage. J'avais pensé à un détecteur de vibration, mais je pense qu'avec le Mistral cela va créer trop de faux positifs.

Je ne vais pas utiliser l'aimant fournit avec ces contacteurs magnétiques, mais un aimant fin collé sur la dernière lame du volet. Et comme j'ai déjà sur chaque fenêtre un détecteur d'ouverture Visonic qui dispose d'une entrée filaire, ça devrait être un jeu d'enfant (le fil sera noyé dans l'isolant du tableau des fenêtres). Ensuite l'information remonte dans la centrale Visonic et dans Home assistant, je pourrais ainsi allumer les projecteurs extérieurs si un malotru tente d'arracher un de mes volets...

Bonus

Toute ces automatisations et commandes à distance c'est bien beau, mais imaginez que le WI-FI déraille ou que Home Assistant, que je trouve pourtant hyper fiable, se plante ? Vous risquez de vous retrouver dans le noir car je n'ai pas mis d'interrupteur filaire sous les volets.

Il y a deux façons d'aborder la chose, j'ai d'abord pensé à installer un panneau un peu caché au centre de la maison avec ces interrupteurs, dans un placard par exemple, sauf qu'il n'y en pas et qu'il aurait fallut y emmener un multipaire connecté aux Shelly's 2.5. Le faire en WI-FI avec des modules I3 était une option qui permet de s'affranchir de Homme Assistant mais pas du WI-FI. Au final j'ai trouvé ces modules DIN de chez Schneider, 3 modules feront l'affaire pour 6 volets en configurant correctement le Shelly (ça monte ou descend et un second appui fait le stop). Attention, si le prix catalogue de ces modules est de 27 € HT, je les ai trouvé à 82.50 € chez Amazon, à environ 40 chez les boutiquiers de la domotique pour finir ici à 9.70 TTC (et pour une fois je vous mets le lien).

Un prochain article parlera de l'automatisation intégrale des volets en fonction des températures, de l'ensoleillement et des contraintes de vie de chacun. A suivre...