Home Assistant, Zigbee & Legrand

On m'a demandé récemment d'intégrer des appareils Legrand dans Home Assistant, je l'avais déjà fait pour certains et j'en avais parlé dans cet article. Aujourd'hui il s'agit d'intégrer des commandes sans fil vue come des télécommandes dans HA. Vous allez me demander quel est l'intérêt quand un simple bouton Ikea (E1743) à moins de 10 fait le job pour mes volets roulants. C'est purement esthétique, quand on a un logement équipé en Legrand Céliane ou Mosaic, on veut parfois que les commandes des volets soient dans la même collection.

Soit, Legrand propose des commandes en Zigbee, il n'y a plus qu'à !

Dans la pratique on va voir que ce n'est pas si simple, en tous cas bien moins simple qu'avec mon bouton Ikea à 10 € ! Je précise le prix car les commandes Legrand sont plutôt à 80 €. Mais quand on aime on ne compte pas.

Il existe plusieurs type d'équipements Zigbee chez Legrand

  • Ceux qui sont filaires, j'en ai parlé ici (micro modules, contacteur DIN, prise connectée, etc...)
  • La gamme des commandes sans fil à pile
  • La gamme des commandes sans fil sans piles (gamme Self-e). J'y reviendrait certainement, mais d'après mes infos cette gamme supporte tous les canaux et serait mieux intégrée ZHA/Z2M.

Je me suis donc penché sur la gamme avec pile.

Première observation, ces devices sont généralement livrés avec un firmware de niveau 42 qui ne permet que l'utilisation du canal Zigbee 11. Don impossible à faire fonctionner sur mon ZHA qui est en 15. Je l'ai donc appairé sur mon Z2M en 11 et ça fonctionne. Et là je me suis dit que j'allais pouvoir mettre à jour ce firmware en OTA. Mais non, ça ne fonctionne pas et on se retrouve dans la problématique des équipements franco Français qui n'intéressent pas grand monde au niveau international (on pense par exemple à tout ce qui est lié à l'intégration Overkiz...).

Après moultes lectures des forums de diverses plateformes domotique, la conclusion est que la mise à jour du firmware ne peut se faire qu'au travers d'une passerelle officielle Legrand ! J’ai donc acheté le kit qui comprend :

  • La prise Control qui sert de passerelle
  • Un inter sans fil
  • Une ampoule

Il faut bien sur installer l’appli Legrand et ensuite tenter d’associer la prise au WIFI. Facile ? Non, ça m’a pris de plombes car vu que leur process est trop long, le mobile qui se connecte à la prise pour la configurer repasse sur le WIFI normal ou il retrouve Internet, et perd la config en cours. Solution faire ça avec un vieux mobile… Bref, un premier amateurisme car on fait facilement ce genre d'association avec la majorité des objets mobile en WIFI !

Bon, la logique de l’appli est orientée électricien très grand public, avec une logique d'électricien qui n’est pas nécessairement la notre. C'est un choix, il faut s'y faire, mais n'oublions pas que nous sommes ici uniquement pour faire un mise à jour...

Ensuite il faut ajouter les inter. Sauf que la il faut comprendre qu’on ne peut pas ajouter un inter seul. Dans la logique Legrand si tu ajoute un inter, en fin de dialogue ça te demande ce que tu veux commander, sans quoi ça bloque et il n’y a plus qu’à forcer le redémarrage de l’appli et recommencer... Je n’avais pas de prise Legrand sous la main, mais au bout d’un moment j’ai fini par penser à appairer l’ampoule. Et la je vois enfin l’inter livré et mon inter de volet roulant qui ne peut rien commander, c’est le même mais avec un firmware différent.

Mais souvenons nous que nous somme là à jouer avec ce bazar dans le seul but de mettre à jour le firmware de ces putains d’inter qui au delà de couter un rein ne fonctionnent (mal) que sur le canal 11. Hélas il n’y arien pour faire cette mise à jour, mon inter de VR apparait en 42 dans l’appli et en 002a dans z2m, donc identique l’un étant en hexa. D’après ce que j’ai pu lire, la mise à jour se fera, un jour, mais on ne peut pas la forcer. Il faut juste laisser branché, en espérant que mon inter qui n’est pas connecté à un appareil se mette à jour tout seul…

Donc je laisse branché, la suite pour bientôt…

EDIT un peu plus tard :

  • L’inter (0 677 73N) est passé de 50 à 70
  • L’inter VR (0 777 48LA) n’a pas bougé…
  • Je testerais si on peut l’appairer hors du canal 11 mais je commence à douter…

EDIT le lendemain :

  • L’inter s’appaire bien sur un autre canal après sa mise à jour. Testé en ZHA sur 15, mais ZHA ne le supporte pas et rien ne bouge, aucun event.
  • L’inter VR ne s’est toujours pas mis à jour. Un peu comme s’il lui fallait une charge...

EDIT le lendemain soir :

  • L’inter VR est bien passé de 42 à 70, mon ZHA qui est sur le canal 15 le voit mais n’en fait rien. Quand à Z2M il n’en veut plus ! Pire ça me fait planter Z2M…

EDIT le sur lendemain midi :

  • L’inter VR qui ne veut toujours pas de ZHA ou Z2M s’est appairé correctement sur deconz en canal 15, il retourne dans les event les valeurs suivantes :

Montée : 1002 / Montée puis relâché : 1002/3002
Descente : 2002 / Descente puis relâché : 2002/3003
Stop (Les deux en même temps) : 3003

Ce comportement est identique à ce qu’il était sous Z2M et contrairement à d'autres télécommandes (le on/off Ikea (E1743 par exemple) il manque le fait qu’un second appui bref provoque un stop. Il faut donc bien appuyer au milieu pour faire un « stop » et ce n’est pas toujours pris en compte (problème physique).

Qu’il ne soit pas supporté sur ZHA est un fait, il faudrait développer un quirk et ça me dépasse. Je ne sais pas pourquoi il ne veut plus s’appairer sur Z2M, mais c’est peut être lié à mon installation, ma clé, que sais-je...

Il y a des chances que l’aventure Legrand va prendre fin et je que je retourne fissa ce Kit à Amazon !

Homekit

A signaler toutefois que cette passerelle est Homekit. Ce qui veut dire que ses équipements remontent dans Home Assistant. Mais ne rêvez pas trop, si l’ampoule remonte bien, et certainement tous les actionneurs connectés (prises, modules DIN), pas les inter En fait si, il faut juste attendre un peu. Néanmoins tout ne semble pas remonter, uniquement une action par bouton ! Mais j’avais déjà remarqué ça avec les télécommandes Tuya qui ne remontent pas via une passerelle Tuya Homekit. Il y a une forme de logique, Homekit sert à commander un équipement, comme une télécommande, hors on ne commande pas une télécommande…

Par contre ça veut dire que les équipements reconnus par Legrand vont remonter dans Homekit, ce qui peut être une solution de contrôle facile pour des produit pas reconnus par HA (Profalux, Bubendorf, Aldes, etc…).

La suite

Je vous propose de poursuivre ici et que chacun y apporte ses retours.

Sources

  • https://developer.legrand.com/production-firmware-download/

Home Assistant & Mi Boxer

J'avais acheté cette télécommande Mi Boxer il y a quelques mois afin de gérer les éclairages de mon séjour, en me disant que les curseurs seraient plus pratiques que mon Opple avec ses 6 boutons actuelle. Hélas elle n'était reconnue nulle part et avait terminé sa course dans le tiroir aux oubliettes du Zigbee...

C'était sans compter sur la ténacité de quelques amateurs de reverse engineering sur lesquels je suis tombé il y a quelques semaines et qui on fait un travail formidable qui a aboutit à une extension pour Zigbee2MQTT, ce qui rend cette télécommande enfin utilisable, tout au moins partiellement pour l'instant. Mais l'essentiel est là !

Ce qui fonctionne :

  • 7 boutons avec ON et OFF (deux actions et non un toggle). Celui du bas à droite n'est pas actif et je vous déconseille de l'utiliser...
  • 1 bouton avec ON et OFF (en haut)
  • 1 bouton W (si on veut bricoler...)
  • La barre de réglage de la luminosité
  • La remontée de l'état de la batterie

Ce qui ne fonctionne pas (pour l'instant) :

  • La barre de réglage des couleurs
  • La barre de réglage de la température du blanc
  • Les touches RGB
  • Les touches de temporisation (en bas à droite)

Une fois la télécommande reconnue sous Z2M on peut commencer à créer des actions. On remarque tout de suite que l'affaire va manquer de sensor: et que certains ne fonctionnent pas. Pas de panique, il y a une solution comme je vais vous le démonter avec l'automation: qui suit. 

J'ai fait un mélange de choose: / conditions: / trigger.id . C'est un peu long, mais je la colle en entier ce qui vous évitera une fastidieuse saisie. Il faudra tout de même y coller vous id: et entity: ! Ah j'allais oublier, il faut aussi ajouter un petit input_select: ... Voir plus bas EDIT du 11/01/2024 !

J'ai tenté de faire ça avec ControllerX que j'adore et qui me sert pour toutes mes télécommandes, mais pour l'instant c'est un échec !

alias: GUI - Mi Boxer
description: ""
trigger:
  - platform: state
    entity_id:
      - sensor.mi_boxeur_brightness
    id: bright
  - platform: device
    domain: mqtt
    device_id: 86c1403c24491ce021ac3ee081a86308
    type: button_short_press
    subtype: button_group_1_on
    discovery_id: 0x003c84fffec29c71_zone_1_button_on
    id: 1on
  - platform: device
    domain: mqtt
    device_id: 86c1403c24491ce021ac3ee081a86308
    type: button_short_press
    subtype: button_group_1_off
    discovery_id: 0x003c84fffec29c71_zone_1_button_off
    id: 1off
  - platform: device
    domain: mqtt
    device_id: 86c1403c24491ce021ac3ee081a86308
    type: button_short_press
    subtype: button_group_2_on
    discovery_id: 0x003c84fffec29c71_zone_2_button_on
    id: 2on
  - platform: device
    domain: mqtt
    device_id: 86c1403c24491ce021ac3ee081a86308
    type: button_short_press
    subtype: button_group_2_off
    discovery_id: 0x003c84fffec29c71_zone_2_button_off
    id: 2off
  - platform: device
    domain: mqtt
    device_id: 86c1403c24491ce021ac3ee081a86308
    type: button_short_press
    subtype: button_group_3_on
    discovery_id: 0x003c84fffec29c71_zone_3_button_on
    id: 3on
  - platform: device
    domain: mqtt
    device_id: 86c1403c24491ce021ac3ee081a86308
    type: button_short_press
    subtype: button_group_3_off
    discovery_id: 0x003c84fffec29c71_zone_3_button_off
    id: 3off
  - platform: device
    domain: mqtt
    device_id: 86c1403c24491ce021ac3ee081a86308
    type: button_short_press
    subtype: button_group_4_on
    discovery_id: 0x003c84fffec29c71_zone_4_button_on
    id: 4on
  - platform: device
    domain: mqtt
    device_id: 86c1403c24491ce021ac3ee081a86308
    type: button_short_press
    subtype: button_group_4_off
    discovery_id: 0x003c84fffec29c71_zone_4_button_off
    id: 4off
  - platform: device
    domain: mqtt
    device_id: 86c1403c24491ce021ac3ee081a86308
    type: button_short_press
    subtype: button_group_5_on
    discovery_id: 0x003c84fffec29c71_zone_5_button_on
    id: 5on
  - platform: device
    domain: mqtt
    device_id: 86c1403c24491ce021ac3ee081a86308
    type: button_short_press
    subtype: button_group_5_off
    discovery_id: 0x003c84fffec29c71_zone_5_button_off
    id: 5off
  - platform: device
    domain: mqtt
    device_id: 86c1403c24491ce021ac3ee081a86308
    type: button_short_press
    subtype: button_group_6_on
    discovery_id: 0x003c84fffec29c71_zone_6_button_on
    id: 6on
  - platform: device
    domain: mqtt
    device_id: 86c1403c24491ce021ac3ee081a86308
    type: button_short_press
    subtype: button_group_6_off
    discovery_id: 0x003c84fffec29c71_zone_6_button_off
    id: 6off
  - platform: device
    domain: mqtt
    device_id: 86c1403c24491ce021ac3ee081a86308
    type: button_short_press
    subtype: button_group_7_on
    discovery_id: 0x003c84fffec29c71_zone_7_button_on
    id: 7on
  - platform: device
    domain: mqtt
    device_id: 86c1403c24491ce021ac3ee081a86308
    type: button_short_press
    subtype: button_group_7_off
    discovery_id: 0x003c84fffec29c71_zone_7_button_off
    id: 7off
  - platform: device
    domain: mqtt
    device_id: 86c1403c24491ce021ac3ee081a86308
    type: button_short_press
    subtype: button_group_8_on
    discovery_id: 0x003c84fffec29c71_zone_8_button_on
    id: 8on
  - platform: device
    domain: mqtt
    device_id: 86c1403c24491ce021ac3ee081a86308
    type: button_short_press
    subtype: button_group_8_off
    discovery_id: 0x003c84fffec29c71_zone_8_button_off
    id: 8off
    
condition: []
action:
  - choose:
      - conditions: "{{ trigger.id in ['1on'] }}"
        sequence:
          - service: light.turn_on
            data: {}
            target:
              entity_id: light.shellydimmer_f3d426
          - service: input_select.select_option
            data:
              option: "1"
            target:
              entity_id: input_select.mi_boxer_select
      - conditions: "{{ trigger.id in ['1off'] }}"
        sequence:
          - service: light.turn_off
            data: {}
            target:
              entity_id: light.shellydimmer_f3d426

      - conditions: "{{ trigger.id in ['2on'] }}"
        sequence:
          - service: light.turn_on
            data: {}
            target:
              entity_id: light.shellydimmer_d3e57c
          - service: input_select.select_option
            data:
              option: "2"
            target:
              entity_id: input_select.mi_boxer_select
      - conditions: "{{ trigger.id in ['2off'] }}"
        sequence:
          - service: light.turn_off
            data: {}
            target:
              entity_id: light.shellydimmer_d3e57c

      - conditions: "{{ trigger.id in ['3on'] }}"
        sequence:
          - service: light.turn_on
            data: {}
            target:
              entity_id: light.ikea_e27_tv
          - service: input_select.select_option
            data:
              option: "3"
            target:
              entity_id: input_select.mi_boxer_select
      - conditions: "{{ trigger.id in ['3off'] }}"
        sequence:
          - service: light.turn_off
            data: {}
            target:
              entity_id: light.ikea_e27_tv

      - conditions: "{{ trigger.id in ['4on'] }}"
        sequence:
          - service: light.turn_on
            data: {}
            target:
              entity_id: light.lidl_led_stand
          - service: input_select.select_option
            data:
              option: "4"
            target:
              entity_id: input_select.mi_boxer_select
      - conditions: "{{ trigger.id in ['4off'] }}"
        sequence:
          - service: light.turn_off
            data: {}
            target:
              entity_id: light.lidl_led_stand

      - conditions: "{{ trigger.id in ['5on'] }}"
        sequence:
          - service: light.turn_on
            data: {}
            target:
              entity_id: light.dimmable_sm309
          - service: input_select.select_option
            data:
              option: "5"
            target:
              entity_id: input_select.mi_boxer_select
      - conditions: "{{ trigger.id in ['5off'] }}"
        sequence:
          - service: light.turn_off
            data: {}
            target:
              entity_id: light.dimmable_sm309

      - conditions: "{{ trigger.id in ['6on'] }}"
        sequence:
          - service: light.turn_on
            data: {}
            target:
              entity_id: light.shelly_bulb_1
          - service: input_select.select_option
            data:
              option: "6"
            target:
              entity_id: input_select.mi_boxer_select
      - conditions: "{{ trigger.id in ['6off'] }}"
        sequence:
          - service: light.turn_off
            data: {}
            target:
              entity_id: light.shelly_bulb_1

      - conditions: "{{ trigger.id in ['7on'] }}"
        sequence:
          - service: light.turn_on
            data: {}
            target:
              entity_id: light.led_strip_1
          - service: input_select.select_option
            data:
              option: "7"
            target:
              entity_id: input_select.mi_boxer_select
      - conditions: "{{ trigger.id in ['7off'] }}"
        sequence:
          - service: light.turn_off
            data: {}
            target:
              entity_id: light.led_strip_1

      - conditions: "{{ trigger.id in ['8on'] }}"
        sequence:
          - service: light.turn_on
            data: {}
            target:
              entity_id: 
                - light.shellydimmer_f3d426
                - light.shellydimmer_d3e57c
                - light.ikea_e27_tv
                - light.dimmable_sm309
                - light.shelly_bulb_1
                - light.lidl_led_stand
                - light.plug_tz_10_switch
                - light.led_strip_1
          - service: input_select.select_option
            data:
              option: "8"
            target:
              entity_id: input_select.mi_boxer_select
      - conditions: "{{ trigger.id in ['8off'] }}"
        sequence:
          - service: light.turn_off
            data: {}
            target:
              entity_id: 
                - light.shellydimmer_f3d426
                - light.shellydimmer_d3e57c
                - light.ikea_e27_tv
                - light.dimmable_sm309
                - light.shelly_bulb_1
                - light.lidl_led_stand
                - light.plug_tz_10_switch
                - light.led_strip_1
              
      - conditions: >-
          {{ is_state('input_select.mi_boxer_select', '1') and trigger.id in ['bright'] }}
        sequence:
          - service: light.turn_on
            data:
              brightness_pct: "{{ trigger.to_state.state }}"
              transition: 0.2
            target:
              entity_id: light.shellydimmer_f3d426
            enabled: true

      - conditions: >-
          {{ is_state('input_select.mi_boxer_select', '2') and trigger.id in ['bright'] }}
        sequence:
          - service: light.turn_on
            data:
              brightness_pct: "{{ trigger.to_state.state }}"
              transition: 0.2
            target:
              device_id: 1ff4112785e14b8b8cba18d45fec3b11
            enabled: true

      - conditions: >-
          {{ is_state('input_select.mi_boxer_select', '3') and trigger.id in ['bright'] }}
        sequence:
          - service: light.turn_on
            data:
              brightness_pct: "{{ trigger.to_state.state }}"
              transition: 0.2
            target:
              entity_id: light.ikea_e27_tv
            enabled: true

      - conditions: >-
          {{ is_state('input_select.mi_boxer_select', '4') and trigger.id in ['bright'] }}
        sequence:
          - service: light.turn_on
            data:
              brightness_pct: "{{ trigger.to_state.state }}"
              transition: 0.2
            target:
              entity_id: light.lidl_led_stand
            enabled: true

      - conditions: >-
          {{ is_state('input_select.mi_boxer_select', '5') and trigger.id in ['bright'] }}
        sequence:
          - service: light.turn_on
            data:
              brightness_pct: "{{ trigger.to_state.state }}"
              transition: 0.2
            target:
              entity_id: light.dimmable_sm309
            enabled: true

      - conditions: >-
          {{ is_state('input_select.mi_boxer_select', '6') and trigger.id in ['bright'] }}
        sequence:
          - service: light.turn_on
            data:
              brightness_pct: "{{ trigger.to_state.state }}"
              transition: 0.2
            target:
              entity_id: light.shelly_bulb_1
            enabled: true

      - conditions: >-
          {{ is_state('input_select.mi_boxer_select', '7') and trigger.id in ['bright'] }}
        sequence:
          - service: light.turn_on
            data:
              brightness_pct: "{{ trigger.to_state.state }}"
              transition: 0.2
            target:
              entity_id: light.led_strip_1
            enabled: true
mode: restart

EDIT 11/01/2024

Avec la dernière mise à jour Zigbee2MQTT (1.35.1-1) il n’y a plus besoin de l’extension. Par contre ce que j’avais fait sur la base de ce qui est proposé ici ne fonctionne plus (bien que toujours dans la doc).

J’ai donc biaisé en faisant du mqtt direct :

Pour la détection (trigger:) des boutons (à dupliquer) :

- platform: mqtt
    topic: zigbee2mqtt/Mi Boxer
    payload: ('on', 101)
    value_template: "{{ value_json.action , value_json.action_group }}"
    id: 1on
  - platform: mqtt
    topic: zigbee2mqtt/Mi Boxer
    payload: ('off', 101)
    value_template: "{{ value_json.action , value_json.action_group }}"
    id: 1off

Et pour la luminosité (il me reste à trouver comment ne prendre en compte que la charge action_level) :

- platform: mqtt
    topic: zigbee2mqtt/Mi Boxer
    id: bright

Ensuite on modifie dans le chose: coté action (à dupliquer bien entendu :

      - conditions:
          - condition: template
            value_template: >-
              {{ is_state('input_select.mi_boxer_select', '2') and trigger.id in ['bright'] }}
        sequence:
          - service: light.turn_on
            data_template:
              entity_id: light.shellydimmer_d3e57c
              brightness_pct: "{{ trigger.payload_json.action_level }}"

EDIT 11/01/2024

Le problème avec la solution de mon EDIT précédent est que le trigger sur la charge MQTT globale génère trop de bruit. On va donc faire deux sensor: basés sur MQTT afin d'isoler la valeur de la luminosité (merci Mathieu) ainsi que le groupe (bouton) correspondant à la light: active, ce qui va nous permettre d'éliminer l'input_select: dans le filtrage à venir :

mqtt:
  sensor:
    - name: "Mi Boxer Bright"
      unique_id: "mi_boxer_bright"
      state_topic: "zigbee2mqtt/Mi Boxer"
      value_template: "{{ value_json.action_level |int }}"

    - name: "Mi Boxxer Group"
      unique_id: "mi_boxer_group"
      state_topic: "zigbee2mqtt/Mi Boxer"
      value_template: "{{ value_json.action_group |int }}"

En trigger: on utilisera l'action : action_brightness_move_to_level qui remonte dans HA :

trigger:
  - platform: device
    domain: mqtt
    device_id: 86c1403c24491ce021ac3ee081a86308
    type: action
    subtype: brightness_move_to_level
    discovery_id: 0x003c84fffec29c71 action_brightness_move_to_level
    id: bright

Et on complète notre action déclenchée par l'id: bright et filtrée sur le sensor: correspondant au bon bouton, et j'ai ajouté le light: actif afin de ne pas risquer une fausse manœuvre :

      - conditions:
          - condition: template
            value_template: >-
              {{ is_state('sensor.mi_boxer_group', '101') and trigger.id in ['bright'] and is_state('light.shellydimmer_f3d426', 'on') }}
        sequence:
          - service: light.turn_on
            data_template:
              entity_id: light.shellydimmer_f3d426
              brightness_pct: "{{ states('sensor.mi_boxer_bright') | int}}"

Il ne reste plus qu'à attendre que Z2M remonte les informations liées à la température du blanc et la roue chromatique et on pourra les traiter avec la même méthode.

 
 

 

 

Home Assistant & Arrosage

Je n'ai pas vraiment la main verte et et mon jardin ressemble souvent à un no man's land, mais au printemps dernier une amie m'a convaincu de créer un carré d'herbes aromatiques, l'idée à fait son chemin et au centre on a même planté des plants de tomates qui produisent bien cet été ! Bon, vous imaginez bien que je ne vais pas ici vous conter ma vie privée mais plutôt la part domotique de cette réalisation !

En Provence, si on veut un peu de verdure et espérer manger ses propres tomates, il n'y a pas de secret, il faut arroser ! J'ai donc commencé par disposer un tuyau poreux sur mon carré. Ensuite j'ai acheté un robinet Zigbee et préparé un petit scheduler qui me permet facilement d'activer ou pas la plage journalière d'arrosage. J'ai fait quelque chose de simple en m'inspirant de ce que j'avais fait pour mon chauffe eau. Je publie ici le code suite à quelques demandes, même si ça n'a pas une grande valeur ajoutée.

Pour l'instant ça ne prends pas en compte les valeurs remontés sur les capteurs de plantes car je n'en suis pas satisfait.

L'offre en matière de capteurs de plantes n'est pas énorme :

  • Le capteur Xiaomi (ou ses copies) qui fonctionnent en Bluetooth et que l'on trouve sur Amazon ou Ali Express : C'est ce qui fonctionne le "mieux", mais cela nécessite un proxy BLE à l'extérieur que l'on peut facilement se bricoler avec un ESP sous ESPHome.
  • Le capteur Rehent en Zigbee que j'ai acheté chez Domadoo : Et là c'est la déconvenue. Il est un peu encombrant mais ne pose pas de soucis particulier pour l'appairer en ZHA ou Z2M (ou sur une passerelle Tuya), sauf que si ce capteur remonte parfaitement la température du sol, l'humidité du sol reste invariablement entre 75% et 85% (voir les sources au bas de cet article). Et bien sur chez Domadoo on est pas chez Amazon, le client a tort et l'éventuel retour est à votre charge. Bref capteur et fournisseur à éviter ! Il semblerait qu'autres séries de ce capteur que l'on trouve chez AliExpress fonctionnent, mais là il faudra aussi oublier le retour...

J'utilise une vanne Zigbee Woox qui alimente le tuyaux poreux (que je devrais remplacer par un un goute à goute). Pour avoir un réseau Zigbee fiable à l'extérieur, j'ai installé sous la toiture de la terrasse des prises Zigbee dont le relais est HS mais qui continuent à remplir parfaitement leur rôle de routeur Zigbee.

Tout cela reste très expérimental... Coté intégration j'utilise Home Assistant Plant et la carte qui va avec.

Automation

Une automation de schedule simple et visuelle :

input_datetime:
  watering_start:
    has_date: false
    has_time: true
  watering_stop:
    has_date: false
    has_time: true

input_boolean:
  watering_day_monday:
    name: "WATERING : Lundi"
    icon: mdi:toggle-switch
  watering_day_tuesday:
    name: "WATERING : Mardi"
    icon: mdi:toggle-switch
  watering_day_wednesday:
    name: "WATERING : Mercredi"
    icon: mdi:toggle-switch
  watering_day_thursday:
    name: "WATERING : Jeudi"
    icon: mdi:toggle-switch
  watering_day_friday:
    name: "WATERING : Vendredi"
    icon: mdi:toggle-switch
  watering_day_saturday:
    name: "WATERING : Samedi"
    icon: mdi:toggle-switch
  watering_day_sunday:
    name: "WATERING : Dimanche"
    icon: mdi:toggle-switch

automation:

- id: 'xx8d0e1-fcb6-4412-abvxx-99c4d37be5xx'
  alias: 'WATERING ON'
  trigger:
  - platform: template
    value_template: '{{ states.sensor.time.state == states.input_datetime.watering_start.state[0:5] }}'
  condition:
    condition: or
    conditions:
      - '{{ (now().strftime("%a") == "Mon") and is_state("input_boolean.watering_day_monday", "on") }}'
      - '{{ (now().strftime("%a") == "Tue") and is_state("input_boolean.watering_day_tuesday", "on") }}'
      - '{{ (now().strftime("%a") == "Wed") and is_state("input_boolean.watering_day_wednesday", "on") }}'
      - '{{ (now().strftime("%a") == "Thu") and is_state("input_boolean.watering_day_thursday", "on") }}'
      - '{{ (now().strftime("%a") == "Fri") and is_state("input_boolean.watering_day_friday", "on") }}'
      - '{{ (now().strftime("%a") == "Sat") and is_state("input_boolean.watering_day_saturday", "on") }}'
      - '{{ (now().strftime("%a") == "Sun") and is_state("input_boolean.watering_day_sunday", "on")}}'
  action:
  - service: switch.turn_on
    entity_id: switch.vanne_woox_switch
  - service: notify.slack_hass_canaletto
    data:
      message: "{{now().strftime('%d/%m/%Y, %H:%M')}} > WATERING | START | Soil : {{ states.sensor.soil_01_soil_moisture.state }}%" 


- id: 'zz9csdfsef-76dd-4fdd-9dzz-40bfsdq158zz'
  alias: 'WATERING OFF'
  trigger:
  - platform: template
    value_template: '{{ states.sensor.time.state == states.input_datetime.watering_stop.state[0:5] }}'
  action:
  - service: switch.turn_off
    entity_id: switch.vanne_woox_switch
  - service: notify.slack_hass_canaletto
    data:
      message: "{{now().strftime('%d/%m/%Y, %H:%M')}} > WATERING | STOP | Soil : {{ states.sensor.soil_01_soil_moisture.state }}%"   

La carte Lovelace :

type: vertical-stack
cards:
  - type: entities
    entities:
      - entities:
          - entity: automation.watering_on
            name: false
          - entity: sensor.energy_total_yearly_1pm_watering
            name: false
            unit: kWh
            format: precision2
          - entity: sensor.soil_01_soil_moisture
            name: false
        entity: switch.vanne_woox_switch
        name: Arrosage
        icon: mdi:watering-can-outline
        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.watering_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.watering_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.watering_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.watering_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.watering_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.watering_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.watering_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.watering_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> Arrosage'
            - type: markdown
              content: '#### <center> Heure de Fin'
        - type: horizontal-stack
          cards:
            - entity: input_datetime.watering_start
              type: custom:time-picker-card
              name: Début
              layout:
                align_controls: center
                embedded: true
              hide:
                name: true
                icon: true
            - type: glance
              show_state: true
              show_name: false
              entities:
                - switch.vanne_woox_switch
            - entity: input_datetime.watering_stop
              type: custom:time-picker-card
              layout:
                align_controls: center
                embedded: true
              hide:
                name: true
                icon: true
  - color_thresholds:
      - color: '#039BE5'
        value: 0
      - color: '#0da035'
        value: 19
      - color: '#e0b400'
        value: 25
      - color: '#e45e65'
        value: 2400
    color_thresholds_transition: hard
    entities:
      - entity: sensor.plant_01_moisture
        name: Humidité du sol
      - entity: sensor.plant_01_temperature
        name: Températire du sol
      - color: rgba(0,0,255,1)
        entity: binary_sensor.night_reworked
        name: Nuit
        show_line: false
        y_axis: secondary
    group: false
    hour24: true
    hours_to_show: 24
    line_width: 2
    name: Humidité et température du sol
    points_per_hour: 4
    show:
      extrema: true
      fill: fade
      icon: true
      labels: false
      name: true
      state: true
    state_map:
      - label: Day
        value: 'off'
      - label: Night
        value: 'on'
    type: custom:mini-graph-card
  - type: custom:flower-card
    entity: plant.jardin
    show_bars:
      - illuminance
      - humidity
      - moisture
      - conductivity
      - temperature
      - dli
    battery_sensor: sensor.demo_battery

Souces

Capteur Rehent

 

Home Assistant & Plug Security

On utilise souvent des prises commandées uniquement pour mesurer la consommation de certains appareils. Ces prises sont toujours en position ON. Ces prises, si elles ne sont pas de trop mauvaise qualité un mécanisme de sécurité intégré qui les passent OFF en cas de surtension ou de surcharge. Et dans la pratique ça arrive parfois et on constate plus tard que le lave linge ou le lave vaisselle s'est arrêté, ou pire dans le cas d'un congélateur.

Pour palier à cet inconvénient on va créer une automation qui va réarmer la prise après quelques minutes en OFF. Jusque là c'est simple, mais il se peut également que le problème soit plus grave et qu'il ne faille pas forcer indéfiniment ce réarmement automatique. Etant donné qu'on ne dispose généralement pas d'information sur la cause de ce passage en sécurité on va considérer qu'au bout de "n" tentatives sur un temps donné on ne réarme plus. Pour ça on va utiliser des compteurs (à créer avec un minimum à 0 et un maximum à 10) et on fera un reset de ceux-ci toutes les nuits...

J'ai fixé ici arbitrairement à 5 le nombre de réarmements possibles.

Attention : Je vous explique ici comment j'ai fait, mais un risque existe toujours et la mise en œuvre de cette solution est à vos risques (et périls). Je me dégage ainsi de toute responsabilité.

On va faire ça avec une seule automation que j'ai voulue la plus concise et qui servira également à notifier :

automation:
- id: '2bd0ertyyf-2687-45f98f-aed0-to-off'
  alias: "Notify Plug Off"
  description: "Notification mise en sécurité des prises"
  mode: single
  trigger:
    - platform: state
      entity_id: switch.bw_1
      to: "off"
      id: "Lave Vaisselle"
    - platform: state
      entity_id: switch.bw_2
      to: "off"
      id: "Lave Linge"
    - platform: state
      entity_id: switch.bw_3
      to: "off"
      id: "Seche Linge"
    - platform: state
      entity_id: switch.bw_4
      to: "off"
      id: "Congelateur"
    - platform: time
      at: "00:05:00"
      id: "Reset"
  condition: []
  action:
  - delay:
      hours: 0
      minutes: 5
      seconds: 0
      milliseconds: 0  
  - choose:
      - conditions: "{{ trigger.id in ['Lave Vaisselle'] and states('counter.plug_bw_1') | int < 5 }}"
        sequence:
          - service: switch.turn_on
            target:
              entity_id: switch.bw_1
          - service: counter.increment
            target:
              entity_id: counter.plug_bw_1
          - service: notify.slack_hass_mondon
            data:
              message: "{{now().strftime('%d/%m/%Y, %H:%M:%S')}} > Réarmement n°{{ states('counter.plug_bw_1')}} du {{ trigger.id }}"               

      - conditions: "{{ trigger.id in ['Lave Linge'] and states('counter.plug_bw_2') | int < 5 }}"
        sequence:
          - service: switch.turn_on
            target:
              entity_id: switch.bw_2
          - service: counter.increment
            target:
              entity_id: counter.plug_bw_2
          - service: notify.slack_hass_mondon
            data:
              message: "{{now().strftime('%d/%m/%Y, %H:%M:%S')}} > Réarmement n°{{ states('counter.plug_bw_2')}} du {{ trigger.id }}" 

      - conditions: "{{ trigger.id in ['Seche Linge'] and states('counter.plug_bw_3') | int < 5 }}"
        sequence:
          - service: switch.turn_on
            target:
              entity_id: switch.bw_3
          - service: counter.increment
            target:
              entity_id: counter.plug_bw_3
          - service: notify.slack_hass_mondon
            data:
              message: "{{now().strftime('%d/%m/%Y, %H:%M:%S')}} > Réarmement n°{{ states('counter.plug_bw_3')}} du {{ trigger.id }}" 

      - conditions: "{{ trigger.id in ['Congelateur'] and states('counter.plug_bw_4') | int < 5 }}"
        sequence:
          - service: switch.turn_on
            target:
              entity_id: switch.bw_4
          - service: counter.increment
            target:
              entity_id: counter.plug_bw_3
          - service: notify.slack_hass_mondon
            data:
              message: "{{now().strftime('%d/%m/%Y, %H:%M:%S')}} > Réarmement n°{{ states('counter.plug_bw_4')}} du {{ trigger.id }}" 

      - conditions: 
          - condition: or
            conditions:
              - "{{ trigger.id in ['Lave Vaisselle'] and states('counter.plug_bw_1') | int >= 5 }}"
              - "{{ trigger.id in ['Lave Linge'] and states('counter.plug_bw_2') | int >= 5 }}"
              - "{{ trigger.id in ['Seche Linge'] and states('counter.plug_bw_3') | int >= 5 }}"
              - "{{ trigger.id in ['Congelateur'] and states('counter.plug_bw_4') | int >= 5 }}"
        sequence:
          - service: notify.slack_hass_mondon
            data:
              message: "{{now().strftime('%d/%m/%Y, %H:%M:%S')}} > Problème avec le {{ trigger.id }}. On ne réarme plus, une vérification s'impose." 

      - conditions: "{{ trigger.id in ['Reset'] }}"
        sequence:
          - service: counter.reset
            target:
              entity_id:
                - counter.plug_bw_1
                - counter.plug_bw_2
                - counter.plug_bw_3
                - counter.plug_bw_4

Home Assistant & Alarm, encore...

J'ai souvent parlé d'alarme ici, de télécommandes, de claviers et de sirènes. Je pense cette fois être parvenu à quelque chose de fonctionnel et je vais vous en parler. Pour faire face aux différentes contraintes, notamment la gestion bizarre des télécommandes sous ZHA, il va falloir un peu ruser. Avant que vous me demandiez pourquoi je m'obstine sous ZHA alors que ces mêmes télécommandes sont bien gérées sous Z2M, je vous répondrait que mon objectif est la simplification et donc d'éviter le plus possible les adons qui complexifient une installation, pas forcément pour moi ou je dispose de toutes les passerelles possibles, mais sur des installations que je gère à distance ou je me dois de faire simple et fiable.

Attention :  on parle ici d'un système de sécurité DIY, donc non agrée par les assurances et autres... Ca ne veut pas dire que ça ne fonctionne pas, juste que ça ne répond pas aux normes NF en vigueur dans ce domaine

Afin de gérer toutes les contraintes, je vais principalement m'appuyer sur deux automations:, quelques scripts: et un alarm_control_panel: virtuel.

L'objectif est de gérer :

  • Un mode absent (away) (avec une variable pour simuler un peu de présence).
  • Un mode présent/nuit armé (home) (plus ou moins utile mais mais ça fait partie de tout système d'alarme face au home jacking.
  • Un mode alerte (emergency) qui va permettre de déclencher une action d'urgence en appuyant sur un bouton de télécommande ou médaillon (je pense à mes vieux jours).

De l'armement / désarmement vont également découler :

  • La fermeture complète ou partielle des volets roulants, leur réouverture cohérente avec la programmation en cours et éventuellement le passage en manuel des volets des chambres des enfants / invités s'ils sont présents.
  • La gestion du climatiseur et des convecteurs avec passage en mode éco ou absent en fonction du type d'armement).
  • La gestion du chauffe eau en cas d'absence de longue durée.
  • La gestion des éclairages et la mise en veille des écrans et ordinateurs.
  • ...

Etant donné que j'ai deux systèmes d'alarme qui fonctionnent en parallèle (Alarmo et Visonic), j'ai commencé par créer une alarme virtuelle et je me servirait d'elle pour les différentes actions.

alarm_control_panel:
  - platform: manual
    name: Home Alarm Command
    code: 1234
    code_arm_required: false
    disarm_after_trigger: false
    arming_time: 0
    delay_time: 0
    trigger_time: 600
    disarmed:
      trigger_time: 0
    armed_home:
      arming_time: 0
      delay_time: 0

L'Armement / Désarmement

L'armement / désarmement va pouvoir se faire de plusieurs façons :

  • Clavier à code
  • Télécommande dédiée
  • Application mobile
  • Tag RFID (pour désarmer dans mon cas)

J'ai volontairement écarté les télécommandes radio en 433 Mhz dont j'avais parlé ici, d'une part parce qu'elles sont facilement piratables, et d'autre part parce qu'elles ne permettent pas de savoir qui a désarmé le système.

Je vais utiliser deux automations et les découper ce dessous/

  • La première pour gérer les armements / désarmements
  • La seconde pour gérer le désarmement des télécommandes, claviers à code afin d'intégrer les particularités ZHA (en attendant une amélioration de la part des développeurs de Home Assistant qui pour l'instant font la sourde oreille, parfois plus concentrés sur les nouveautés que l'affinage de l'existant).

Gestion de l'armement / désarmement et des boutons d'urgence

Les déclencheurs : Ici on va empiler nos déclencheurs d'armement, qu'ils soient gérés par ZHA, Z2M ou toute autre source. 3 par fonction, armed_away, armed_home, triggered, mais cela peut également être un bouton virtuel pour Lovelace, voire n'importe quelle télécommande ou bouton poussoir.

- id: 078fsdg412-3545-4f37-ba02-bccsfds5646sf
  alias: "Alarm : Global ON/OFF"
  mode: restart
  trigger:
    - platform: device
      device_id: c036b6547a0347878dcc6d06152267ab
      domain: alarm_control_panel
      entity_id: alarm_control_panel.lk_zb_keypad
      type: armed_away
      id: "KeyPad Away"
    - platform: device
      device_id: c036b6547a0347878dcc6d06152267ab
      domain: alarm_control_panel
      entity_id: alarm_control_panel.lk_zb_keypad
      type: armed_home
      id: "KeyPad Home"
    - platform: device
      device_id: c036b6547a0347878dcc6d06152267ab
      domain: alarm_control_panel
      entity_id: alarm_control_panel.lk_zb_keypad
      type: triggered
      id: "KeyPad Triggered"
    - platform: state
      entity_id: input_button.arm_alarm_away
      id: "Push Button Away"

Et un déclencheur de désarmement activé par une seconde automation que l'on verra plus loin :

    - platform: state
      entity_id:
        - alarm_control_panel.home_alarm_command
      to: disarmed
      id: "Remote or Keypad"

Les conditions : Dans mon cas je n'en ai pas utilisé. Chacun adaptera.

  condition:

Les actions

Je commence par arrêter les éventuels scripts en cours. Cela est principalement utile quand on a fermé la porte, armé le système et que l'on doit rapidement désarmer car on a oublié quelque chose dans la maison.

  action:
    - service: homeassistant.turn_off
      target:
        entity_id:
          - script.privacy_on
          - script.privacy_off
          - script.alarmo_after_arm
          - script.alarmo_after_disarm
          - script.alarm_actions_on_leave_common_tasks
          - script.alarm_actions_on_return_common_tasks

Ensuite on va utiliser chose: pour exécuter les différentes actions en fonction de la source et de l'état.

On commence par l'armement en mode absent :

  • On envoie les bips d'armement sur la sirène. En MQTT pour l'instant car elle aussi est très mal gérée sous ZHA
  • On arme Alarmo
  • On arme Visonic (en mode Home sans les PIR qui déraillent).
  • On arme notre panneau d'alarme virtuel qui nous servira au désarmement
  • On arme également le clavier, ça ne change rien à l'usage mais quand on se présentera pour désarmé il apparaitra comme étant armé. Dans la pratique ça relance de fait l'automation avec la même automation, et donc ça fausse le message, j'ai donc retiré cette option pour l'instant. (D'ailleurs si vous êtes sous Z2M il le développeur d'Alarmo a fait un joli travail pour ce clavier).
  • On inscrit un message texte dans un input_text: que l'on affiche dans Lovelace et qui nous permet de savoir qui fait quoi. A terme je remplacerait les id: par le prénom de l'utilisateur pour plus de lisibilité.
    - choose:
        - conditions: "{{ trigger.id in ['KeyPad Away', 'Heiman RC1 Away', 'Heiman RC2 Away', 'Woox RC1 Away'] }}"
          sequence:
            - service: mqtt.publish
              data:
                topic: zigbee2mqtt/Sirène SMaBiT/set
                payload: >-
                  {"squawk": {"state": "system_is_armed", "level": "veryhigh", "strobe": "true"}}     
            - service: alarm_control_panel.alarm_arm_away # ALARMO Away
              data:
                code: !secret alarm_code
              entity_id: alarm_control_panel.alarmo
            - service: alarm_control_panel.alarm_arm_home # VISONIC Home (ne jamais armer AWAY à cause des détecteurs HS)
              data:
                code: !secret alarm_code_visonic
              entity_id: alarm_control_panel.visonic_alarm
            - service: script.alarm_actions_on_leave_common_tasks
            - service: alarm_control_panel.alarm_arm_away # FAKE ALARM PANEL Away
              target:
                entity_id: alarm_control_panel.home_alarm_command
            - service: alarm_control_panel.alarm_arm_away # On arme le clavier afin qu'il apparaisse armé
              data:
                code: !secret alarm_code_zha
              target:
                entity_id: 
                  - alarm_control_panel.lk_zb_keypad
            - service: input_text.set_value
              target:
                entity_id: input_text.last_arm
              data:
                value: "{{now().strftime('%d/%m/%Y, %H:%M:%S')}} > Arm Away by : {{ trigger.id }}"                            

On fait la même chose pour le mode home. Je fais l'impasse sur Visonic, par contre j'émet un son de confirmation sur un buzzer (on verra plus loin l'utilisation possible d'un TTS sur une enceinte.

        - conditions: "{{ trigger.id in ['KeyPad Home', 'Heiman RC1 Home', 'Heiman RC2 Home', 'Woox RC1 Home'] }}"
          sequence:
            - service: alarm_control_panel.alarm_arm_home # ALARMO Home
              data:
                code: !secret alarm_code
              entity_id: alarm_control_panel.alarmo
            - service: alarm_control_panel.alarm_arm_home # FAKE ALARM PANEL Home
              target:
                entity_id: alarm_control_panel.home_alarm_command
            - service: alarm_control_panel.alarm_arm_home # On arme le clavier afin qu'il apparaisse armé
              data:
                code: !secret alarm_code_zha
              target:
                entity_id: 
                  - alarm_control_panel.lk_zb_keypad

            - service: button.press
              target:
                entity_id: button.up_chime_play_buzzer
            - service: input_text.set_value
              target:
                entity_id: input_text.last_arm
              data:
                value: "{{now().strftime('%d/%m/%Y, %H:%M:%S')}} > Arm Home by : {{ trigger.id }}"                                    

Ensuite on passe au désarmement du mode absent. On remarque que l'on écrit pas d'input_text: car ici on ne sait pas qui a fait quoi car l'ordre vient de la seconde automation que l'on verra plus loi. C'est donc elle qui écrira. Par contre on émet le son de désarmement sur la sirène.

        - conditions: "{{ trigger.id in ['Remote or Keypad'] and is_state('alarm_control_panel.alarmo', 'armed_away') }}"
          sequence:              
            - service: alarm_control_panel.alarm_disarm
              data:
                code: !secret alarm_code
              entity_id:  
                - alarm_control_panel.alarmo
                - alarm_control_panel.visonic_alarm
            - service: mqtt.publish
              data:
                topic: zigbee2mqtt/Sirène SMaBiT/set
                payload: >-
                  {"squawk": {"state": "system_is_disarmed", "level": "veryhigh", "strobe": "true"}}       

Pareil pour le mode Home. Ici on joue un son sur le buzzer.

        - conditions: 
            - "{{ trigger.id in ['Push Button'] }}"
            - "{{ is_state('alarm_control_panel.alarmo', 'armed_home') }}"
          sequence:              
            - service: alarm_control_panel.alarm_disarm
              data:
                code: !secret alarm_code
              entity_id:  
                - alarm_control_panel.alarmo
            - service: button.press
              target:
                entity_id: button.up_chime_play_buzzer

On continue avec la gestion du bouton d'urgence :

  • On joue un son pour confirmer
  • On arme notre panneau d'alarme virtuel dans un mode inutilisé (Nuit) pour ensuite pouvoir désarmer.
  • On envoie un SMS d'Au Secours, ou toute autre action à imaginer.
  • On écrit un input_text: pour avoir une trace.
        - conditions: "{{ trigger.id in ['KeyPad Triggered', 'Heiman RC1 Triggered', 'Heiman RC2 Triggered', 'Woox RC1 Triggered'] and is_state('alarm_control_panel.alarmo', 'disarmed') }}"
          sequence:              
            - service: button.press
              target:
                entity_id: button.up_chime_play_chime
            - service: alarm_control_panel.alarm_arm_night # FAKE ALARM PANEL Night pour pouvoir désarmer
              target:
                entity_id: alarm_control_panel.home_alarm_command
            - service: notify.free_mobile_andre
              data:
                message: "{{ states.sensor.date_time.state}} > TEST Alerte au secours par télécommande TEST"
            - service: input_text.set_value
              target:
                entity_id: input_text.last_arm
              data:
                value: "{{now().strftime('%d/%m/%Y, %H:%M:%S')}} > Triggered by : {{ trigger.id }}"   

Enfin, on désarme cette alerte. A ce stade on peut aussi envoyer un SMS pour dire que c'était une fausse alerte... Je vais me contenter de jouer du buzzer...

        - conditions: 
            - "{{ trigger.id in ['Remote or Keypad'] and is_state('alarm_control_panel.alarmo', 'disarmed') }}"
          sequence:              
            - service: button.press
              target:
                entity_id: button.up_chime_play_buzzer

Le cas des tag RFID

Les tags RFID c'est sympa, mais hormis en coller deux cote à cote on ne pourra basiquement gérer qu'un seule information : tag_scanned

Hors, l'idée est de se servir d'un même tag collé à la porte pour armer ou désarmer. S'il s'agissait d'un switch on ferait un switch.toggle, mais pas pour une alarme. Il va falloir ruser.

Bien sur on pourrait mettre une condition pour savoir connaitre l'état de l'alarme, mais je ne veux pas utiliser de condition et uniquement un trigger avec son id:. J'ai passé des heures à essayer de faire un trigger template pour combiner le scan du tag + l'état de alarm_control_panel, mais sans succès car le tag n'est pas un device ou un entity ou l'on peut facilement lire l'état.

La solution consiste à créer un binary_sensor: temporisé :

template:
  - trigger:
      - platform: event
        event_type: tag_scanned
        event_data:
          tag_id: bf5018c7-c1a9-4220-a01a-66f885176795
#          name: Tag Green
#          device_id: 1d3ddfgy45805354ce090774e3f22510
    binary_sensor:
      name: Tag Green
      state: "{{ trigger.platform == 'event' }}"
      auto_off:
        seconds: 1

A noter que name: peut être utilisé à la place de tag_id: et que l'on peut jouer avec les autres informations que l'on trouve dans l'event afin par exemple de savoir quel appareil a scanné le tag et ainsi savoir qui a armé ou désarmé... 

Et ensuite il sera facile de créer un trigger basé sur un petit template et se servir de son id: pour action dans le chose:

  - platform: template
    value_template: "{{ is_state('binary_sensor.tag_green', 'on') and is_state('alarm_control_panel.alarmo', 'armed_home') }}"
    id: tag_to_disarm

Gestion des commandes de désarmement

Au risque de me répéter, cette partie aurait pu être intégrée à la première automation, si ZHA ne posait pas problème... Je le ferait probablement, mais pour l'exemple on va pour l'instant scinder cette partie.

Donc, pour les télécommandes d'alarme sous ZHA on va devoir écouter un event:, s'en servir pour désarmer nos système d'alarme mais également pour la désarmer et désarmer ses copines ainsi que le clavier. Simple ! J'avais exploré dans un précédent article plusieurs pistes afin de ne prendre en compte qu'un seul de ses "events" (en + elle en envoie 3 ou 4 identiques selon l'humeur et le modèle). J'ai un peu avancé et on ne va pas les écouter dans l'automation, mais se servir de l'état temporaire d'un binary_sensor: que l'on va pouvoir temporiser et ainsi ne prendre en compte qu'un seul event:

template:
  - trigger:
      - platform: event
        event_type: zha_event
        event_data:
          device_ieee: a4:c1:38:96:0b:cf:c9:61 # Woox RC1
          command: 'arm'
          args:
            arm_mode: 0
            arm_mode_description: 'Disarm'
            code: ''
            zone_id: 0
    binary_sensor:
      name: Woox RC1 to Disarmed
      icon: "{{ (trigger.platform == 'event') | iif('mdi:remote-off', 'mdi:remote') }}"
      state: "{{ trigger.platform == 'event' }}"
      auto_off:
        seconds: 5

Les déclencheurs : Ici on va écouter des actions de désarmement, que cela provienne d'un clavier, une télécommande (via leur binary_sensor:), ou en direct pour une source classique (tag RFID, MQTT, ...).

- id: '2bd0ertyyf-43fa-45f98f-aed0-disarm'
  alias: "Alarm @ Remote Disarm"
  description: 'Disarm Remote Control panel'
  mode: single
  trigger:
    - platform: state
      entity_id: binary_sensor.heiman_rc1_to_disarmed
      to: "on"
      id: "Heiman RC 1"  
    - platform: tag
      tag_id: 2b855225-c999-4b5558123-6c7d12345641
      id: "TAG Jaune"
    - platform: device
      domain: mqtt
      device_id: 139eb4ce27246465b0d5aa7560a953601
      type: action
      subtype: disarm
      discovery_id: 0x84fd27fffe915905 action_disarm
      id: "Heiman RC2"

Les conditions : Aucune, mais on pourrait imagine un flag de blocage activé à distance après une alerte...

condition:

Les actions : On utilise un chose: et on commence par :

  • Désarmer le panneau d'alarme virtuel, ce qui déclenchera l'action idoine dans la première automation.
  • Désarmer les télécommandes et claviers qui sont également des panneaux d'alarme, avec cette particularité ZHA qui fait qu'il faut un code pour désarmer une télécommande...
  • Ecrire un input_text: afin de savoir qui a désarmé.
  • J'envoie un message dans Slack qui me sert de log. Ca va également m'informer à distance que quelqu'un est arrivé et a désarmé 
  action:
    - choose: # DISARM
        - conditions: "{{ trigger.id in ['Push Button', 'KeyPad', 'Linkind RC', 'Heiman RC 1', 'Woox RC 1', 'TAG Porte', 'TAG Jaune', 'TAG Clé', 'TAG Carte', 'Heiman RC2' ] }}"
          sequence:
            - service: alarm_control_panel.alarm_disarm   # FAKE ALARM PANEL to Command
              data:
                code: "1234"
              target:
                entity_id: alarm_control_panel.home_alarm_command
            - service: alarm_control_panel.alarm_disarm
              data:
                code: !secret alarm_code_zha
              target:
                entity_id: 
                  - alarm_control_panel.lk_zb_keypad
                  - alarm_control_panel.lk_zb_remote
                  - alarm_control_panel.heiman_remote
                  - alarm_control_panel.woox_01
            - service: input_text.set_value
              target:
                entity_id: input_text.last_disarm
              data:
                value: "{{now().strftime('%d/%m/%Y, %H:%M:%S')}} > Disarm by : {{ trigger.id }}"
            - service: notify.slack_hass_canaletto
              data:
                message: "{{now().strftime('%d/%m/%Y, %H:%M:%S')}} > Disarmed by : {{ trigger.id }}" 

On peut également faire une place à une fonctionnalité détournée du clavier. En effet au delà de l'amenée Home, Away et Emergency, on peut également lui faire avaler n'importe quel code et effectuer une action précise en fonction de celui ci. Ici si je saisit 33 et valide je vais allumer les projecteurs.

    - choose:
        - conditions: "{{ trigger.id in ['KeyPad 33'] }}"
          sequence:
            - service: light.turn_off
              target:
                entity_id:
                  - light.groupe_de_projecteurs          

Enfin j'ai quelques actions par défaut, comme éteindre les sirènes au cas ou... (ça sent le debug...).

    - service: mqtt.publish
      data:
        topic: zigbee2mqtt/Sirène SMaBiT/set
        payload: >-
          {"warning": {"mode": "stop"}}
    - service: siren.turn_off
      target:
        entity_id:
          - siren.heiman_sirene_1
          - siren.heiman_sirene_2
          - siren.sirene_terrasse                

Et surtout on ajoute un délai car ces télécommandes ont la fâcheuse idée d'envoyer 3 ou 4 event: identique pour chaque appui. Donc combiné au mode: single de cette automation, on va en capturer qu'un seul. Ce délais est bien sur à ajuster plus ou moins au pif... Maintenant que je passe par des binary_sensor: temporisés, je pense que ce délai est devenu inutile.

    - delay : '00:00:08'

Les actions POST Armement / Désarmement

On va utiliser deux scripts qui seront exécutés par Alarmo après l'armement et après le désarmement. Chez moi ils vont servir à :

  • Jouer une annonce vocale en TTS sur une enceinte extérieure pour annoncer l'état armé ou désarmé.
  • Activer ou désactiver la vidéo surveillance.
  • Passer les volets en mode manuel et automatique au retour (voir l'automatisation des volets).
  • Gérer le mode ECO/Absent du climatiseur et des convecteurs.
  • Gérer les éclairages et le mode veille de mon PC.
  • Et plus encore selon votre imagination....

Les notifications d'alarme et sirènes

  • Notifications SMS pour l'intrusion, la détection incendie ou fuite d'eau. (notifications gérées par Alarmo).
  • Sirènes et projecteurs en cas d'intrusion :
    • la législation autorise au maximum 3 minutes pour chaque lancement de sirène.
    • Pas de limite pour les projecteurs. Et éclairer comme en plein jour peut également faire fuir et permettra d'avoir des images bien nettes sur les caméras.
    • Si vous avez un système audio multi room bien puissant, vous pouvez également envoyer une bonne salve d'AC-DC, ça peu perturber... ou inciter les voisins à appeler la maréchaussée.
    • Les armes, explosifs et autres gaz toxiques sont à proscrire.

Pour cela on va utiliser des scripts: qui seront lancés par Alarmo selon les conditions choisies (voir ici).

Etant donné que j'ai des détecteurs d'ouverture sur mes volets roulants, je peux également imaginer l'éclairage extérieur avant de déclencher l'alarme si un de ces volets est touché. Il faut être prévenant et éclairer l'intrus. Cela peut se gréer dans une partition secondaire d'Alarmo, ou simple une automation. Les caméras Unifi Protect (G4) font également de la détection de personne qui remonte dans Home Assistant.

Voilà, ce n'est pas exhaustif, vos idées son les bienvenues, ici ou sur HACF.

 

 

Home Assistant & Sirènes

J'ai déjà parlé ici du système d'alarme Alarmo pour Home Assistant et notamment des commandes possibles pour armer et désarmer le système. Aujourd'hui il s'agira des sirènes.

J'ai trois modèles

  • La plus basique est juste branchée sur une prise commandée Zigbee ondulée. Donc un switch: vu en tant que siren:.
  • Une sirène Heiman qui sous ZHA décroche régulièrement et que j'ai du appairer à ma seconde passerelle sous Z2M.
    • Sous ZHA elle était vue en tant que siren:.
    • Sous Z2M elle est vue en tant que rien du tout...
  • Une sirène SMaBit sous Z2M également vue en rien du tout...

Pour commander une sirène via ZHA HA propose :

    - service: siren.turn_on
      target:
        entity_id:
          - siren.heiman_sirene

Pour commander une sirène sous Z2M il faut lui envoyer le payload correspondant :

    - service: mqtt.publish
      data:
        topic: zigbee2mqtt/Sirène SMaBiT/set
        payload: >-
          {"warning": {"mode": "fire", "level": "very_high", "strobe_level": "low", "strobe": "false", "strobe_duty_cycle": "10", "duration": "360"}}

Un peu plus compliqué et moins facile à mémoriser, mais ça fonctionne.

Mon objectif a donc été de faire en sorte qu'une sirène Z2M soit vue par Home Assistant comme une sirène ZHA et ainsi pouvoir la commander par un siren.turn_on comme les autres... Et voici ce que ça donne :

mqtt:
  siren:
    - unique_id: heiman_sirene_1s
      name: Heiman Sirène 1s
      state_topic: "zigbee2mqtt/Heiman Sirène 1/set"
      command_topic: "zigbee2mqtt/Heiman Sirène 1/set"
      optimistic: false
      qos: 0
      retain: true
      state_value_template: "{{ value_json.warning['mode'] }}"
      command_template: '{"warning": {"duration": 2, "mode": "burglar", "level": "very_high", "strobe": true}}'
      command_off_template: '{"warning": {"duration": 2, "mode": "stop", "level": "very_high", "strobe": true}}'
      state_on: "burglar"
      state_off: "stop"

Problème, car il y en a un, la sirène Heiman ne retourne aucun état sous Z2M. Il y a beaucoup d'articles à ce sujet, et l'intégration ne semble pas vraiment terminée. Hors dans l'activation il y a une durée, le principe étant d'activer la sirène pour x secondes. Et donc comme elle ne retourne pas son état, son commutateur restera sur ON alors que le délais d'activation est terminé et la prochaine action n'aura aucun effet... De plus certaines sirènes proposent plusieurs fonctions et tonalités (les bips de délais d'armement et désarmement par exemple).

Donc contrairement à ZHA ou l'intégration siren: peut gérer tous les modes, chercher à gérer une sirène Z2M en tant que siren: n'a pas de sens... (hormis peut être de bricoler un template: qui en fonction de la durée d'activation repasserait le commutateur à off, on peut aussi le faire via une automation: mais là ça va alourdir la chose...).

Alternative

S'agissant d'envoyer un payload à une sirène Z2M, ce que j'ai trouvé de plus logique est un mqtt:/button: et il faut donc en faire deux :

mqtt:
  button:
    - unique_id: siren_heiman_1_warning
      name: "Sirène Heiman 1 : Waraning"
      command_topic: "zigbee2mqtt/Heiman Sirène 1/set"
      payload_press: '{"warning": {"duration": 2000, "mode": "burglar", "level": "very_high", "strobe": true}}'
      qos: 0
      retain: false
      entity_category: "config"
      device_class: "restart"

    - unique_id: siren_heiman_1_stopped
      name: "Sirène Heiman 1 : Stopped"
      command_topic: "zigbee2mqtt/Heiman Sirène 1/set"
      payload_press: '{"warning": {"duration": 2, "mode": "stop", "level": "very_high", "strobe": true}}'
      qos: 0
      retain: false
      entity_category: "config"
      device_class: "restart"   

Ou plusieurs pour gérer les différents mode de la SMaBit :

mqtt:
  button:
    - unique_id: siren_smabit_armed
      name: "Sirène SMaBit : Armed"
      command_topic: "zigbee2mqtt/Sirène SMaBiT/set"
      payload_press: '{"squawk": {"state": "system_is_armed", "level": "veryhigh", "strobe": "true"}}'
      qos: 0
      retain: false
      entity_category: "config"
      device_class: "restart"

    - unique_id: siren_smabit_disarmed
      name: "Sirène SMaBit : Disarmed"
      command_topic: "zigbee2mqtt/Sirène SMaBiT/set"
      payload_press: '{"squawk": {"state": "system_is_disarmed", "level": "veryhigh", "strobe": "true"}}'
      qos: 0
      retain: false
      entity_category: "config"
      device_class: "restart"

    - unique_id: siren_smabit_warning
      name: "Sirène SMaBit : Warning"
      command_topic: "zigbee2mqtt/Sirène SMaBiT/set"
      payload_press: '{"warning": {"mode": "fire", "level": "very_high", "strobe_level": "low", "strobe": "false", "strobe_duty_cycle": "10", "duration": "360"}}'
      qos: 0
      retain: false
      entity_category: "config"
      device_class: "restart"

    - unique_id: siren_smabit_stopped
      name: "Sirène SMaBit : Stopped"
      command_topic: "zigbee2mqtt/Sirène SMaBiT/set"
      payload_press: '{"warning": {"mode": "stop"}}'
      qos: 0
      retain: false
      entity_category: "config"
      device_class: "restart"

Vous l'aurez compris, il s'agit plus d'un exercice de style qu'autre chose. Mais ça m'aura permis de comprendre comment créer des devices MQTT non reconnus par le discovery:. Merci @Mathieu pour ta participation !

Sources

 

Sony mon amour !

Amour déçu comme on le verra plus loin ! Depuis très jeune j'ai toujours été un afficionado de la marque Sony. Et si d'aucune pourraient trouver cela déplacé, il est des domaines ou je suis fidèle ! Coté TV après avoir eu quelques Trinitrons cathodiques célèbres tel le Profeel, je me suis offert jadis le premier moniteur plasma pro de la marque, qui avait du me couter un an de salaire et que je me suis résolu récemment à le porter à la déchetterie, le pauvre il trainait dans le garage depuis bientôt 20 ans !

Je ne vais pas vous faire la liste des écrans Sony que j'ai eu successivement, ni leur cout car ça me déprimerait, mais juste vous parler des déboires de mon dernier TV. Il y a un an j'ai craqué pour le A80J, un Oled ! Un TV intelligent sous Google TV, même si d'intelligence il n'a que la pub et les traqueurs que cela induit !

Je considère Google TV / Android TV comme étant le meilleur choix car il me permet d'installer les application qui me sont utiles, en opposition aux écosystèmes fragmentés de la concurrence qui ne proposent que l'essentiel bien commercial ! C'est du business, on le sait et on l'accepte car ça nous apporte du confort.

Autres points que les pseudo journalistes testeurs oublient souvent, c'est la télécommande et l'implémentation du CEC. Celles de Sony m'ont toujours satisfait alors que je déteste certaines d'autres marques. Mais Sony est radin, et sur le A80J la télécommande n'est pas même rétroéclairée ! Qu'à cela ne tienne il suffit de commander sur Amazon celle du A90J qui elle est premium et rétro éclairée... Quant au CEC il a toujours été très bien chez Sony.

Je ne vais pas vous raconter ce que vaut cette TV, plusieurs sites l'ont déjà fait plus ou moins bien. Enfin, je parle de TV, mais je ne regarde même pas la télé et aucune antenne n'y est raccordée. Je rêve d'une version moniteur !

Domotique

Oh le gros mot ! Encore un client qui veut faire des choses louches ! Louches car je ne me plie pas à la domotique commerciale et forcément limitative (Amazon, Google, Apple, Tuya, etc...) mais que ma domotique est libre, Home Assistant.

Et pourquoi faire ? Pas grand chose, juste inclure ce TV dans me scénarios (scènes). Et on va faire simple !

  • Play / Pause : on éteint et on rallume progressivement les éclairages comme au cinéma.
  • On / Off : on mets sur pause la musique qui était diffusée.

Ce n'est pas sorcier et très simple à faire. Mais pour ca il faut pouvoir extraire les informations du TV. Ca tombe bien ce TV est reconnu sous Home Assistant par l'intégration Bravia ou HomeKit. Dès lors on dispose des information idoines et ça fonctionne très bien, il suffit d'agir en fonction de l'état remonté.

Mais alors il se plaint de quoi le râleur ? Et oui il y a un "mais" !

Consommation

Pour accéder à ces fonctions il faut que le contrôle à distance ou / et HomeKit soit activé. C'est d'ailleurs le cas par défaut. Et les ingés de chez Sony, dont je ne doute pas des prouesses en audio / vidéo s'avèrent des brèles en réseau ! Pour que ces fonctions soient accessibles ils laissent simplement la partie réseau allumée quand le téléviseur est en veille. Et comme leur carte réseau doit dater du siècle dernier (elle a du mal à accrocher le réseau et est limitée à du 10/100 en 2022 ou on est censés passer de la full 4K !), elle consomme en veille pas moins de 26/28 W alors que si ces fonctions sont désactivées la veille est à seulement 0.9 W.

Cela pose deux constats :

  • Il est inacceptable qu'un TV en veille consomme 27 W en 2022 (c-a-d + ou - 50 € / an).
  • Il est inacceptable que ces fonctionnalités soient activées par défaut et induisent cette surconsommation ! Et certainement contraire aux directives européennes en la matière. Il est en effet peu probable qu'un consommateur lambda ne pense à mesurer et désactiver ces fonctions.

En l'état il est donc inenvisageable d'utiliser ces fonctionnalités qui ajoutent un plus au téléviseur. Et il faut noter que tout pilotage par les applis standard, Chromecast, Alexa, etc pose le même problème...

Le pire étant que si on désactive ces fonctions, quand on allume le téléviseur il mets bien trop longtemps pour accrocher le réseau, que ce soit en Ethernet ou en WI-FI. Je sais c'est fait pour regarder la télé avec un râteau sur la toiture !

Alternative

Avant j'utilisait un LCD Sony bien plus bête et une NVidia Shield que je pilotait très bien sans que cela ne grève mon budget. Le Shield allumait le TV en CEC et tout était parfait. Je vais peut être en racheter un, encore que mon Shield de 2015 fonctionne encore très bien dans ma chambre. Un bel exemple de non obsolescence qui mérite d'être signalé !

Et les autres ?

Je ne sais pas comment ça se passe chez les autres constructeurs de TV car je ne suis pas client. On me remonte 1 à 2 W avec les options de pilotage activées chez Samsung ou Panasonic, mais je n'ai pas vérifié. Par contre j'ai un bel élevage de Sonos, et là non plus ce n'est pas très joli à voir : aucun progrès depuis le début, conso identique en veille sur les anciens Play 1 et 3 et les nouveaux One (gen 1 et 2), entre 3 et 4 W voire plus de 8 à 11 W sur un AMP ou 6 W pour une Beam !

Moralité

La domotique n'est pas vraiment écologique, elle crée un énorme talon de consommation de part son infrastructure (caméras, nvr, switches, micro modules et autres prise connectées). Elle apporte plus de confort qu’elle ne fait réaliser des économies (les économies c’est le bobard que racontent certains à leur compagne quand c’est elle qui tiens les cordons de la bourse, car, hélas, la domotique reste un truc de mec, comme le BBQ, n’en déplaise à Sandrine).

Un panneau solaire va devoir s'imposer à moi !

Sources

 

 

Home Assistant & Volets roulants, automatismes !

Dans un précédent article je vous avait parlé de la partie matérielle de mes volets roulants et de leur commande via des interrupteurs et des télécommandes. Mais avec une installation domotique ouverte, on peut faire bien mieux !

L'objectif ici sera pour chaque volet (ou groupe de volets) d'automatiser leur ouverture, position intermédiaire et fermeture en fonction de :

  • Le jour et la nuit
  • La température extérieure et intérieure et les contraintes météo (canicule, etc.)
  • La position du soleil
  • Les contraintes de vie (lever tardif, soirée, etc...)
  • Les contraintes de présence / absence plus ou moins longue (alarme et simulation de présence)
  • Le débrayage de l'automatisme afin de laisser en paix un invité ou les enfants en vacances avec leur propres horaires et mode de vie.

Vous allez me dire tout de suite que la tache est complexe et vous aurez raison, mais pas tant que ça car je vais m'appuyer sur le travail de Fabien qui a créé un superbe moteur pour gérer tout ça, moteur que chacun pourra adapter à ses propres besoin en lui fournissant des prérequis (inputs, binary, etc..) qui permettront de gérer les différents conditions de positionnement. Vous trouverez les détails sur son blog, mais l'installation est relativement aisée, on commence par créer les entités nécessaires, ensuite on ajoute les BluePrint's de configuration et on termine avec un peu de yaml pour la configuration des conditions.

Facile, mais ça prends un peu de temps. Et surtout commencez par mettre sur le papier le résultat que vous voulez obtenir. Dans cet article je vais vous parler de ce que j'ai mis autour de son travail, plus que de son travail. 

Avant de chercher à intégrer mes exemples, prenez le temps d'avoir une installation vraiment fonctionnelle avec les Blueprint's de Fabien.

De base et sans rien ajouter le moteur de Fabien va permettre d'ouvrir le volet au lever de soleil et de le fermer au coucher du soleil grâce à l'intégration sun.sun. Bon, pour faire ça on a pas besoin d'une usine, c'est pour ça que je vais vous montrer que son moteur permet bien d'autres choses.

Température extérieure et position du soleil

Voici un exemple des plus basic de ce que j'avais fait au départ, mais je vous conseille d'adapter la version de Fabien que j'ai moi même adoptée et qui permet d'aller plus loin, avec différentes sources et notamment un capteur de luminosité.

Exemple

J'utilise les Packages et je vais commencer par créer deux sensor: en fonction de ma localisation en m'appuyant sur sun.sun

sensor: 
  - platform: template
    sensors:
      sunelevation:
        friendly_name: "Elevation du soleil"
        value_template: "{{ state_attr('sun.sun', 'elevation') }}"
      sunazimuth:
        friendly_name: "Azimut du soleil"
        value_template: "{{ state_attr('sun.sun', 'azimuth') }}"

Ensuite je vais me servir de ce site pour trouver les bonnes valeurs et je vais créer 3 binary_sensor: avec les les valeurs basses et hautes d'azimut, ces binary passeront à ON quand le soleil tapera sur la façade concernée. Bien sur il faudra un peu tâtonner pour trouver les bonnes valeurs et les ajuster.

binary_sensor:
  - platform: threshold
    name: "Soleil : Ouest"
    upper: 170
    lower: 80
    entity_id: sensor.sunazimuth
  - platform: threshold
    name: "Soleil : Sud"
    upper: 270
    lower: 100
    entity_id: sensor.sunazimuth
  - platform: threshold
    name: "Soleil : Est"
    upper: 299
    lower: 260
    entity_id: sensor.sunazimuth

Et pour terminer je vais créer un dernier binary_sensor: pour déterminer les conditions météo. Il s'appuie sur les infos de l'intégration Météo France (ou avec un capteur de luminosité en s'appuyant sur l'exemple de Fabien) et dépend de seuils définis dans des input_number: qui pourront s'afficher dans Lovelace. (je n'ai pas pris en compte la température intérieure pour l'instant).

binary_sensor:
  - platform: template
    sensors:
      vr_sun_hot:
        device_class: heat
        delay_on: 
          seconds: 300
        delay_off:
          seconds: 300
        value_template: "{{states('sensor.avignon_temperature')|float(0) >= states('input_number.vr_sun_hot') |float(0)
                        and states('sensor.avignon_cloud_cover')|float(0) <= states('input_number.vr_cloud_cover')|float(0) }}" 
        friendly_name: "Alerte haute température"

input_number:
  vr_sun_hot:
    name: Seuil haute température
    min: "10"
    max: "40"
    step: "1"
    unit_of_measurement: "°"
    icon: mdi:sun-thermometer-outline
  vr_cloud_cover:
    name: Seuil couverture nuageuse
    min: "0"
    max: "100"
    step: "5"
    unit_of_measurement: "%"
    icon: mdi:cloud-lock-outline 

Et voici le travail :

Exploitation

Vous aurez remarqué que j'ai deux seuils. Il y aura donc deux conditions possibles dans le choose: de l'automation de positionnement qui exploite les Blueprint's de Fabien. On retrouve dans l'ordre :

  1. La ou les conditions (and), ici la température est hot et le soleil au sud.
  2. La séquence de services à lancer :
    1. Je positionne le volet dans un input_number: à partir d'un input_number: de préréglage.
    2. A des fin de debug je note la dernière condition utilisée et la position du volet dans un input_text: que je peux afficher dans Lovelace
        - conditions:
            - condition: state
              entity_id: binary_sensor.vr_sun_hot
              state: "on"
            - condition: state
              entity_id: binary_sensor.soleil_sud
              state: "on"
          sequence:
            - service: input_number.set_value
              target:
                entity_id: input_number.vr_cuisine_planned_position
              data:
                value: "{{ states('input_number.vr_global_position_sun_alert') }}"
            - service: input_text.set_value
              target:
                entity_id: input_text.vr_cuisine_condition
              data:
                value: "Condition : Ensoleillement excessif - {{ states('input_number.vr_global_position_sun_alert') }} %"

Attention : Dans un choose: l'ordre de priorité des différentes conditions est important. Il y a aussi une option par défaut si aucune des conditions n'est valide mais que l'automation est tout de même déclenchée. On placera donc en premier celles qui doivent prendre le dessus, par exemple la condition issue de l'activation de l'alarme sera en premier car il est évident que l'on se moque de position du soleil si on est en voyage et que la maison est fermée.

Cela n'empêchera éventuellement pas une solution de simulation de présence qui agirait sur les volets de fonctionner. Je suis en train de tester cette intégration, attention à bien valider avant de partir en vacances...

Contraintes de vie

Réveil

Ici c'est au petit bonheur de chacun. En ce qui me concerne je n'ai pas d'horaires réguliers et je suis un lève tard. J'ai donc créé un script qui se déroule quand je dis bonjour (ou bonne nuit) à Alexa ou que j'appuis simplement sur un bouton de télécommande. Ce script va entre autres choses faire un push, si je suis présent dans la maison, sur un input_button: que je vais utiliser ici pour entrouvrir le volet de ma chambre et de la baie du séjour (une baie vitrée coulissante s'ouvre très facilement si elle n'est pas protégée, je vais donc éviter d'ouvrir son volet quand je dors). On a donc deux input_button: :

input_button:
  lionel_up:
    name: Je me lève
    icon: mdi:human-greeting-variant
  lionel_down:
    name: Je me couche
    icon: mdi:bed

La condition correspondant au réveil (la position du volet est stockée dans un input_number: afin d'être facilement ajustée depuis l'interface)

        - conditions:
            - "{{ as_timestamp(states('input_button.lionel_up'))|timestamp_custom('%H:%M', true) == states('sensor.time')  }}"
          sequence:
            - service: input_number.set_value
              target:
                entity_id: input_number.vr_lionel_planned_position
              data:
                value: "{{ states('input_number.vr_global_position_reveil') }}"

Il faut bien sur ne pas oublier de déclarer input_button: dans le BluePrint correspondant au volet (en condition immédiate) et l'actionner dans le script ou l'interface :

  - service: input_button.press
    target:
      entity_id: input_button.lionel_up

Et pour ce volet je vais modifier l'action par défaut d'ouverture dans l'automation afin qu'il ne se passe rien avant 13 heures... mais également rien si le volet est déjà un peu ouvert par une condition fugitive (button.press).

            - conditions:
                - condition: numeric_state # On ouvre le volet si le soleil est au dessus de l'horizon
                  entity_id: sun.sun
                  attribute: elevation
                  above: 0
                - condition: time
                  after: "13:00:00"
                - condition: state
                  entity_id: cover.vr_baie
                  state: "closed"

Soirée

En soirée, surtout en été, je n'ai pas envie que les volets soient complètement fermés car c'est l'heure ou on ouvre les fenêtres pour faire entrer un air plus frais. Je vais donc définir un binary-sensor: correspondant à ma soirée (merci Fabien) :

binary_sensor:
  - platform: template
    sensors:
      vr_evening:
        value_template:  "{{ (state_attr('sun.sun', 'azimuth') | float(0.0)) > 180 and (state_attr('sun.sun', 'elevation') | float(0.0)) < 0 and ('12:00:00' < states('sensor.time')) }}"
        friendly_name: "Soirée"

J'aurais pu y ajouter un and supplémentaire afin de valider la saison en me basant sur l'intégration Season. Sauf que les saisons tout le monde sait que ça ne veut plus dire grand chose et qu'ici depuis le début du mois de mail il fait plus ou moins 30° en journée, je testerais donc un input_boolean: de plus pour valider cette condition (que j'aurais également pu valider dans le binary-sensor:) :

        - conditions:
            - condition: state
              entity_id: binary_sensor.vr_evening
              state: "on"
            - condition: state
              entity_id: input_boolean.vr_evening
              state: "on"
          sequence:
            - service: input_number.set_value
              target:
                entity_id: input_number.vr_cuisine_planned_position
              data:
                value: "{{ states('input_number.vr_global_position_soir') }}"
            - service: input_text.set_value
              target:
                entity_id: input_text.vr_cuisine_condition
              data:
                value: "Condition : Soirée - {{ states('input_number.vr_global_position_soir') }} %"

On peut aussi faire la même chose avec d'autres conditions et sans binary_sensor: et tout mettre dans les conditions...

        - conditions:
            - condition: numeric_state
              entity_id: sun.sun
              attribute: azimuth
              above: 180
            - condition: numeric_state
              entity_id: sun.sun
              attribute: elevation
              below: 0
            - condition: time
              after: "18:00:00"            
            - condition: state
              entity_id: input_boolean.vr_evening
              state: "on"     

Présence / Absence et Alarme 

Pour ce chapitre c'est un peu plus compliqué. J'utilise en parallèle deux système. Ma centrale Visonic indépendante mais que je pilote dans Home Assistant et l'intégration Alarmo. Lors de mes test je me suis aperçu que tester uniquement l'état "armed" pouvait conduire à des surprises. Lors de l'armement et surtout le déclenchement d'une alarme (possiblement fausse) l'état de alarm_control_panel: passe par des phases successives peu simple à gérer, et ça peu conduire à une ouverture des volets non souhaitée.

Chez moi j'ai différentes possibilités pour armer ces deux systèmes (télécommande, code, tag), je vais donc me servir d'Alarmo qui permet de lancer un script selon un état pour basculer un input_boolean: qui me servira dans la condition immédiates du volet :

  • On passe tous les volets en mode automatique (certains auraient pu êtres débrayés comme on le verra plus loin)
  • On active l'input_boolean: qui va nous servir dans la condition de l'automatisation
  • Sur le mode Vacances (absence de longue durée) je rebascule tous les volets en mode manuel. Une sécurité de plus afin d'empêcher qu'ils soient ouverts par erreur.
script:
  alarmo_after_arm:
    alias: Alarmo - after Arm
    sequence:
      - if:
        then:
        - service: input_select.select_option
          target:
            entity_id:
              - input_select.vr_cuisine_immediate
              - input_select.vr_marie_immediate
              - input_select.vr_baie_immediate
              - input_select.vr_sejour_immediate
              - input_select.vr_antoine_immediate
              - input_select.vr_lionel_immediate
          data:
            option: Automatique
      - if: "{{ is_state('alarm_control_panel.alarmo', 'armed_away') }}"
        then:
          - service: input_boolean.turn_on
            target:
              entity_id: input_boolean.alarmo_armed_away
      - if: "{{ is_state('alarm_control_panel.alarmo', 'armed_vacation') }}"
        then:
          - service: input_boolean.turn_on
            target:
              entity_id: input_boolean.alarmo_armed_vacation
          - delay: "00:02:30"
          - service: input_select.select_option
            target:
              entity_id:
                - input_select.vr_cuisine_immediate
                - input_select.vr_marie_immediate
                - input_select.vr_baie_immediate
                - input_select.vr_sejour_immediate
                - input_select.vr_antoine_immediate
                - input_select.vr_lionel_immediate
            data:
              option: Manuel              
      - if: "{{ is_state('alarm_control_panel.alarmo', 'armed_home') }}"
        then:
          - service: input_boolean.turn_on
            target:
              entity_id: input_boolean.alarmo_armed_home
                entity_id: input_boolean.alarmo_armed_away

Et au retour sur le disarm :

  • Je repasse les input_boolean: à off
  • Je repasse tous les volets en automatique afin que les conditions courantes prennent effet.
  • Et si ma fille ou mon fils sont présents présente je vais basculer leurs volets en manuel afin qu'ils soient gérés comme bon leur semble. Mais j'attend un peu afin que l'automatisation du volet  le place dans la position de la contrainte en cours.
script:
  alarmo_after_to_disarm:
    alias: Alarmo - after Disarm
    sequence:
      - if:
        then:
        - service: input_boolean.turn_off
          target:
            entity_id: 
              - input_boolean.alarmo_armed_away
              - input_boolean.alarmo_armed_vacation
              - input_boolean.alarmo_armed_home
        - service: input_select.select_option
          target:
            entity_id:
              - input_select.vr_cuisine_immediate
              - input_select.vr_marie_immediate
              - input_select.vr_baie_immediate
              - input_select.vr_sejour_immediate
              - input_select.vr_antoine_immediate
              - input_select.vr_lionel_immediate
          data:
            option: Automatique
      - if: "{{ is_state('input_boolean.presence_marie', 'on') }}"
        then:
          - delay: "00:01:30"
          - service: input_select.select_option
            target:
              entity_id:
                - input_select.vr_marie_immediate
            data:
              option: Manuel
      - if: "{{ is_state('input_boolean.presence_antoine', 'on') }}"
        then:
          - delay: "00:01:30"
          - service: input_select.select_option
            target:
              entity_id:
                - input_select.vr_antoine_immediate
            data:
              option: Manuel

Ensuite il me reste qu'à configurer la contrainte dans l'automatisation, et surtout de la placer en priorité (la première).

        - conditions:
            - condition: state
              entity_id: input_boolean.alarmo_armed_away
              state: "on"
          sequence:
            - service: input_number.set_value
              target:
                entity_id: input_number.vr_cuisine_planned_position
              data:
                value: "{{ states('input_number.vr_global_position_alarm') }}"
            - service: input_text.set_value
              target:
                entity_id: input_text.vr_cuisine_condition
              data:
                value: "Condition : Alarmo Away - {{ states('input_number.vr_global_position_alarm') }} %"

J'ai deux positions d'alarme, ARMED_AWAY et ARMED_VACATION. J'utilise la première quand que pars quelques heures, ça laisse certains volets entrouverts afin de simuler une présence avec d'autres artifices, et la seconde pour une fermeture totale.

Le débrayage

Les automatismes c'est bien, mais si mes enfants sont ici en vacances ou des amis occupent leurs chambres, je ne voudrais pas qu'un automatisme les sortent du lit à l'aube. Pour mes enfants qui disposent de l'application Home Assistant je pourrait imaginer un automatisme, mais on va oublier et leur laisser de la liberté, d'autant plus que s'agissant d'amis je ne vais pas leur imposer ma domotique.

Par contre je dispose facilement de leur date de départ et d'arrivée et une automation bascule déjà un input_boolean: qui sert au chauffage. Je vais donc m'en servir pour passer le volet de leur chambre en mode manuel :

automation:
- id: a800970c-9bf4-48ce-aedg-a67c29093eb3
  description: Change vr_marie_immediate
  alias: VR Global - Change mode VR Marie
  mode: restart
  trigger:
    - platform: state
      entity_id: input_boolean.presence_marie
  action:
    - choose:
        - conditions:
            - condition: state
              entity_id: input_boolean.presence_marie
              state: "on"
          sequence:
            - service: input_select.select_option
              target:
                entity_id:
                  - input_select.vr_marie_immediate
              data:
                option: Manuel
        - conditions:
            - condition: state
              entity_id: input_boolean.presence_marie
              state: "off"
          sequence:
            - service: input_select.select_option
              target:
                entity_id:
                  - input_select.vr_marie_immediate
              data:
                option: Automatique

Enfants et amis commanderont donc leur volets avec la petite télécommande de leur chambre.

Suspension

Si vous êtes arrivés là et que vous avez lors de la configuration des BluePrint's la durée de suspension vous vous demandez peut être de quoi il s'agit. En fait configure là le durée pendant laquelle l'automatisme sera suspendu. Imaginons que celle ci est réglée du 60 minutes, il est 13 heures, le soleil frappe fort et vous décidez de déjeuner dans le cuisine. Vous remontez le volet avec la télécommande (ou l'application), il restera levé le temps de manger (suspension de 60 minutes) et l'automatisme reprendra le dessus ensuite avec la contrainte idoine... Magique !

Conclusion

Tout cela m'a pris un peu de temps et j'ai servi de cobaye dans la phase de test d'appréhension de la solution que Fabien viens de mettre à disposition. Quand vous aurez parfaitement validé le fonctionnement souhaité sur un volet pour pourrez le dupliquer sur l'ensemble des volets.

Pour cela il faut :

  1. Dupliquer le fichier qui contient les informations propres à chaque volet (mes fichiers sont ici) et changer l'ID: de l'automation.
  2. Repérer les informations des 3 BluePrint's du premier volet dans le fichier /config/automations.yaml, les dupliquer à la suite en changeant les informations propres à chaque volet et les ID: . Si vous ne le sentez pas configurez les BluePrint's individuellement à la main.
  3. Vérifier une fois de plus que tous les ID: ont bien été changés. J'insiste sur ce point car ça peut être la cause de bien des dysfonctionnements. Vous trouverez ici un générateur d'UUID.
  4. Redémarrer Home Assistant
  5. S'armer de patience car ce que l'on imagine parfait pour le premier volet ne le sera pas nécessairement pour les autres.
  6. Et surtout tester toutes les conditions car il sera désagréable de voir s'ouvrir le volet de la chambre quand on dort (vécu) voir certains volets ne pas se fermer sur l'alarme enclenchée quand on part que l'on est déjà en retard (vécu avec obligation de tous les passer en mode manuel et de les baisser à la main).

Avec plaisir pour en parler, ici ou mieux sur ce fil HACF.

Sources

Home Assistant & Wake on Lan

L'heure est aux économies d'énergies et il parait qu'il faut éteindre "la" Wi-Fi ! Ce n'est peut être pas ce qui consomme le plus et notre domotique fait certainement mieux.... Par contre nombre d'entre nous ont des PC énergivores qui mériteraient de passer en veille. Sauf que dans la pratique on laisse souvent ce PC allumé en se disant que l'on pourra en avoir besoin depuis l'extérieur... Ce que j'ai longtemps fait !

Un PC moderne avec un SSD sait sortir de veille en quelques secondes. Alors pourquoi ne pas ressortit une vieille fonctionnalité très peu utilisée, le Wake on Lan.

Sous Linux, Windows, voire même les mobiles il existe des application GUI ou CLI. Il faut tout de même savoir que Wake on Lan n'a pas été conçu à l'époque pour fonctionner à distance, et même s'il existe des possibilités ça reste compliqué. Alors pourquoi ne pas simplement utiliser Home Assistant qui lui sera toujours disponible, voire même simplement ajouter ça à un ESP existant, sous ESP Home par exemple..

La première chose pour vos tests sera de vérifier en local que le Wake on Lan fonctionne (il y a pas mal d'utilitaires et d'explications sur ce site).

Avec Home Assistant

On commence par ajouter l'intégration au fichier de configuration et on redémarre :

wake_on_lan:

Ensuite on peur créer un switch: , mais aujourd'hui on préfèrera un button: dans Lovelace qui actionnera le service correspondant :

  - action_name: 'Sleep/UnSleep'
    double_tap_action:
      action: call-service
      service: button.press
      target:
        entity_id: button.lia_monitors_off
      action: call-service
      confirmation:
        text: Etes vous sur ?
    tap_action:
      action: call-service
      service: wake_on_lan.send_magic_packet
      data:
        mac: 00-0C-29-FB-77-97
    icon: mdi:microsoft-windows
    name: 'Windows 10 PC'
    type: button

Vous remarquerez que j'ai configuré ça sur le tap_action:, le double_tap_action: étant lui configuré pour la commande de mise en veille via Home Assistant Agent.

Avec ESP Home

J'ai un client full cloud qui dispose d'une douzaine de PC, mais pas de serveur et encore moins de Home Assistant. La première solution était de laisser un PC allumé et y installer de quoi faire du Wake on Lan en remote. Il y a également des possibilités via TeamViewer et hélas pas de base dans l'UDM Pro d'Ubiquiti. On verra ce l'on utilisera mais j'ai également pensé faire ça via un simple ESP qui lui pourrait resté allumé...

Dans ESP Home on va éditer la config et simplement ajouter (attention, ici les séparateurs ne sont pas des "-" mais ":") :

web_server:    

button:
- platform: wake_on_lan
  name: "VM Annia"
  target_mac_address: 00:0C:29:FB:77:97

A partir de là il est possible de presser notre button: qui remonte dans Home Assistant, sur la page web de l'Esp, mais également via la commande curl (la doc est ici) :

curl -X POST http://192.168.210.153/button/vm_annia/press

Et comme on ne va pas demander à l'utilisateur final de faire un "curl" on va lui emballer tout ça dans une page web accessible que l'on sécurisera afin que lui seul puisse y accéder, en partant de cette base :

<HTML>
<center>

	<form name="myform" action="http://192.168.210.153/button/vm_annia/press" method="post">
  	<button>PC Annia ON</button>
	</form>

</center>
</HTML>

Variante avec un WebHook

L'ESP remonte sur Home Assistant via ESPHome. De fait on peu presser un bouton pour réveiller le PC distant, voire même intégrer ce bouton sur un autre Home Assistant en remote. Mais il est également possible de créer un WebHook et de déclencher avec un raccourcis depuis le PC de l'utilisateur distant :

- alias: Réveil PC Carole (WebHook)
  description: ""
  trigger:
    - platform: webhook
      webhook_id: "secret-id"
  condition: []
  action:
    - service: button.press
      data: {}
      target:
        entity_id: button.reveil_pc_carole
  mode: single

Et le raccourcis à créer sur le PC de l'utilisateur qui doit réveiller son PC de bureau...

curl -X POST https://ha-online.suptel.org/api/webhook/<secret-id>

Infos

  • Attention, Microsoft a introduit un concept de veille moderne qui souvent demandera à être désactivé. Des infos ici et .
  • Il n'est pas possible de réveiller un PC connecté en Wi-Fi ou en Ethernet via USB.
  • A noter que la Freebox dispose d'une option permettant de laisser passer du Wake on Lan. Ce n'est pas le cas pour tous les routeurs et il faut parfois ruser avec le port 9.

Sources

 

Home Assistant & BLE Proxy

J'entend souvent parler d'esp32, je ne suis pas un crack du fer à souder, ma vue baisse et je ne me lance généralement pas dans des montages au delà de mes compétences. De plus j'aime que ce que je déploie soit maintenable le plus facilement possible par des tiers. Je reste donc dans les standards, par exemple tous mes Shelly ont leur firmware d'origine, là ou ils seraient plus simples à gérer en ESP Home...

Ceci étant, j'aime aussi bricoler et j'avais un problème à résoudre. Si les petits capteurs de température Aqara en Zigbee sont parfaits, pas mal d'utilisateurs ont une préférence pour des sondes avec afficheur. Et le marché ne nous propose quasiment que des sondes afficheur en Bluetooth (BLE), Xiaomi, Aqara, Swithboot, Goovee ou encore InkBird... En général on peut facilement les gérer avec BLE Monitor et un bon dongle Bluetooth. Mais.

Depuis la version 2022.09 Bluetooth est géré nativement dans Home Assistant, il travaillent avec le développeur de BLE Monitor, une intégration qui devrait disparaitre à terme. Dans la pratique tout n'est pour l'instant pas reconnu mais les premiers résultats avec la version intégrée sont très honorables et j'ai obtenu des meilleurs résultats en BLE Proxy qu'avec des dongles USB ou du Bluthoot de base (NUC).

Pour autant se pose toujours le problème de la porté des sondes, dans notre cas d'usage BLE est uni directionnel et l'intégration se contente d'écouter, de décoder les trames et d'intégrer ce qui est exploitable. Sauf que s'il ne reçoit rien il n'intègre rien, et tout le monde sait que la portée du Bluetooth n'est pas phénoménale.

Lors de mes débuts avec HA j'avais essayé avec des potes des passerelles BLE, l'idée était de dédier des RPI Zero judicieusement placés à cet usage. Ca c'était soldé par un échec, certainement trop débutant que nous étions.

Mais avec la release 2022.09 une nouvelle option intégrée à HA a vu le jour : BLE Proxy

L'idée géniale est de se servir d'un module ESP configuré sous ESP Home et disposant du Bluetooth afin de capter les trames et les présenter à HA qui les décode. Et bien sur ce module sera judicieusement placé là ou on a besoin (cave, dépendance, aile gauche du château, etc...), pour peu que l'endroit dispose de Wi-Fi ou à minima d'Ethernet.

Le plus basic

La méthode la plus simple consiste à acheter un ESP-WROOM-32 (ici ou encore moins cher sur Ali), de le connecter au PC en USB après avoir installé les drivers et de le configurer à partir de cette page qui en fin de configuration vous proposera de l'intégrer à Home Assistant. 

Pour ceux qui jouent déjà avec l'add-on ESP Home il est aussi possible d'intégrer directement ce module et d'ajouter le code que vous trouverez ici et qui ressemble à ça :

esphome:
  name: "esp32-ble-proxy"
esp32:
  board: esp32dev
  framework:
    type: arduino
logger:
api:
  encryption:
    key: "AnUMyESfgsfgsdfhgjfjhhjkhg1WJLcGZHTiD7NoOEog="
ota:
  password: "3fb1ee84wgfsdhgsgh46sgh869d9a4856"
wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password
  ap:
    ssid: "Ss Fallback Hotspot"
    password: "ws7qfgsfgh6Bl"
captive_portal:
web_server:
dashboard_import:
  package_import_url: github://esphome/bluetooth-proxies/esp32-generic.yaml@main
esp32_ble_tracker:
  scan_parameters:
    interval: 1100ms
    window: 1100ms
    active: true
bluetooth_proxy:
button:
- platform: safe_mode
  name: Safe Mode Boot
  entity_category: diagnostic

Une fois que le module est intégré à Home Assistant celui ci décodera les trames et vous proposera les sondes qu'il reçoit. Il est également possible de les ajouter avec leur adresse MAC.

Le plus compact

L'Atom Lite me plait bien (détails ici). D'une part il n'est pas plus gros qu'une pièce de monnaie, et d'autre part il est livré dans un petit boitier plastique ce qui sera toujours plus élégant que le précédent. De plus on peut avec un coupleur USB-A / USB-C le brancher directement sur un chargeur, à défaut un câble court fera l'affaire. On trouve l'Atom Lite sur Ali ou sur le site de son fabricant.

La mise en œuvre est identique, et en bonus on peut piloter la led multicolore qui pourra éventuellement servir à notifier visuellement des états de ce que vous voulez via HA :

light:
  - platform: fastled_clockless
    chipset: WS2812B
    pin: 27
    num_leds: 1
    rgb_order: GRB
    id: status_led
    name: Stack Light
    effects:
      - random:
      - flicker:
      - addressable_rainbow:  

Et comme il expose des ports GPIO rien n'empêche, comme pour les autres de s'en servir pour faire autre chose. Je vous conseille d'ailleurs d'aller explorer l'univers M5STACK ou vous pourrez piocher des idées, comme ce lecteur RFID par exemple....

Le plus complet

L'Olimex (qui s'achète ici) est le plus complet car il dispose en plus du Wi-Fi d'un port Ethernet ce qui permettra de le placer là ou le Wi-Fi est absent. De plus il est disponible dans une version avec antenne externe pour une meilleure réception, ainsi qu'une version industrielle garantie de -40 + 85C. Le sports GPIO sont présents mais il faudra sortit le fer à souder...

Coté configuration c'est identiques au autres. On peut commence par configurer en USB en Wi-Fi et ensuite on active le port Ethernet en mettant à jour en Wi-Fi. Il semble déconseillé d'utiliser le port USB et le port Ethernet en même temps et on ne va pas jouer... Par contre je vous conseille de figer l'adresse affectée par la résa DHCP dans les deux cas car dans le cas contraire ESP Home a un peu de mal à le retrouver via son adresse en .local qui par définition changera.

# wifi:
#   ssid: !secret wifi_ssid
#   password: !secret wifi_password
#   ap:
#     ssid: "Esp-Test Fallback Hotspot"
#     password: "MHPdiJUnJ8i8"
#   use_address: 192.168.210.69
  
ethernet:
  type: LAN8720
  mdc_pin: GPIO23
  mdio_pin: GPIO18
  clk_mode: GPIO17_OUT
  phy_addr: 0
  power_pin: GPIO12
  use_address: 192.168.210.108

Alternatives

Il existe d'autre options que je n'ai pas testé, comme le GL-Inet GL-S10, que je n'ai pas pu me procurer. Dans un petit boitier on a du Wi-FI, de l'Ethernet et une antenne externe pour $ 25.

Shelly

Les modules Plus de Shelly permettent également d'activer le mode proxy et ainsi jouer ce rôle. Donc si on a des Shelly+ ou des Prises commandées Plusg S+ on pourra se passer d'un ESP dédié. J'ai validé ça avec succès sur un appartement en duplex de 175 m² avec 2 Shelly+ 1PM. C'est la solution la plus simple.

Usages

En fait tout cela nous ouvre pas mal de portes. Imaginons que je veuille faire des relevés de température / humidité sur un site distant ou il n'y a pas de domotique. J'ai alors le choix d'un ESP du genre Atom Lite sur une prise USB qui va remonter les informations des sondes locale sou Bluetooth vers un site Home Assistant distant qui le verra dès lors que j'ai ouvert le port 6053. Mon ESP communiquera alors via l'API avec Home Assistant comme s'il était local et je pourrais le maintenir, changer sa config et le mettre à jour avec ESPHome. L'alternative consiste à monter MQTT et à le faire communiquer avec un brooker. Ca évite d'avoir un port à ouvrir, mais je ne pourrais plus le maintenir.

On peut échanger ici ou sur HACF.

Alternative

Si le mode proxy est intéressant pour couvrir une surface plus grande, il est également possible de déclarer les sondes BLE dans ESPHome et ainsi les voir remonter dans Home Asistant :

sensor:
  - platform: xiaomi_cgg1
    mac_address: "58:2D:34:10:F8:22"
    temperature:
      name: "Cuisine Temperature"
    humidity:
      name: "Cuisine Humidity"
    battery_level:
      name: "Cuisine Battery Level"    

  - platform: xiaomi_lywsd03mmc
    mac_address: "A4:C1:38:AB:59:F7"
    bindkey: "93f19252bcdfhfdqshdhb4f9957424"
    temperature:
      name: "Square Temperature"
    humidity:
      name: "Square Humidity"
    battery_level:
      name: "Square Battery Level"  

  - platform: xiaomi_lywsdcgq
    mac_address: "4C:65:A8:D1:DB:22"
    temperature:
      name: "Terrasse Temperature"
    humidity:
      name: "Terrasse Humidity"
    battery_level:
      name: "Terrasse Battery Level"

  - platform: xiaomi_lywsd02
    mac_address: "3F:59:C8:82:70:2A"
    temperature:
      name: "Hall Temperature"
    humidity:
      name: "Hall Humidity"
    battery_level:
      name: "Hall Battery Level"

EDIT 15/08/2023

Deux choses :

  • Un composant pour les sondes qui ne sont pas reconnues de base.
  • Un constat, beaucoup d'ESP en WIFI donnent des résultats parfois aléatoires. Dans tous mes tests les résultat le plus fiable et constant est celui obtenu avec un ESP Olimex en Ethernet (POE), et avec son antenne externe il couvre toute la maison, terrasse et jardin compris.
  • Eviter plus de 3 proxy (attention avec les Shelly qui jouent ce rôle).
  • Pour les équipements Xiaomi / Aqara cette passerelle Xiaomi peut constituer la bonne alternative avec l'intégration locale idoine. Elle remonte BLE + Zigbee et de plus elles est compatible Homekit.