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.

Home Assistant & Entity Controler

Vous voilà content, vous pouvez allumer, gérer et éteindre votre ampoule via Home Assistant. Je suis sur que vous savez également faire ça via un bouton sans fil, par exemple via ControlerX etc... Mais on peut mieux faire et automatiser en détectant la présence dans une pièce avec quelques lignes et un capteur d'occupation genre Aqara ou Xiaomi... Et pour ça il y a plusieurs méthodes.

EDIT 25/10/2022 : Finalement le problème majeur de Entity Controler est que toute modification impose un redémarrage complet de Home Assistant. J'ai donc basculé sur AD-Automoli qui lui est sous AppDaemon, donc dynamique car les modifications sont prises en compte immédiatement. A considérer également Light Automation pour des éclairages  heures fixes.

L'option classique en YAML

- id: 'fab33b5b-b526-4f6c-a23f-ee98c300012c'
  alias: "PRESENCE : Eclairage Bureau"
  description: ''
  trigger:
    - platform: device
      type: occupied
      id: enter
      device_id: a65b2d5c507be85f0964131f26f8d31e
      entity_id: binary_sensor.motion_bureau_occupancy
      domain: binary_sensor
    - platform: device
      type: not_occupied
      id: leave
      device_id: a65b2d5c507be85f0964131f26f8d31e
      entity_id: binary_sensor.motion_bureau_occupancy
      domain: binary_sensor
  condition:
  action:
    - if:
      - condition: trigger
        id: enter
      then:
        - service: light.turn_on
          data:
            entity_id: light.shellydimmer_db2f18
    - if:
        - condition: trigger
          id: leave
      then:
        - service: light.turn_off
          data:
            entity_id: light.shellydimmer_db2f18

Alternatives

Pour les plus flémards on peu aussi faire ça avec des BluePrint's, ou même en NodeRed si on veut se compliquer la vie (je déteste mais ça vous le savez). Mais j'ai encore plus simple à vous proposer.

Entity Controler

EC pour les intimes. EC est un moteur qui va contrôler des entités (light, switch, scènes, etc...) en fonction d'informations binaires. Donc les sensor: d'entrée seront de détecteurs de présence ou de mouvement, des capteurs d'ouverture ou dans l'absolu n'importe quel binary_sensor: , et pourquoi pas ceux que vous aurez créé dans un usage détourné... A ce contrôle on va bien sur pouvoir appliquer diverses contraintes (horaires, nuit/jour) ou simplement dire que dès lors qu'une lampe s'est allumée automatiquement elle ne s'étendra que manuellement. C'est assez souple et adaptable à quasiment tous les besoins. En tous cas les miens.

Le Git est ici et la doc très complète .

Voici un exemple pour allumer une lampe pendant 180 secondes (durée par défaut) : 3 lignes

motion_light:
  sensor: binary_sensor.motion_sejour
  entity: light.shellydimmer_d3e57c

On peut bien sur régler la durée :

  delay: 320

Faire en sorte que ça ne s'allume que la nuit, avec ici un offset :

  start_time: sunset - 00:30:00
  end_time: sunrise + 00:30:00

Et appliquer des valeurs à la lampe :

  service_data:
    brightness: 255
    color_name: blue
  service_data_on: 
    transition: 5
  service_data_off: 
    transition: 10

Faire en sorte que ça ne s'éteigne pas !

  stay_mode: on

Ou applique une contrainte externe. Ici j'ai mis un input_boolean: mais ça aurait pu être l'état d'une autre lampe, voire un media_player:. A noter que l'on peu également se servir des overrides pour simplement désactiver le contrôle. J'ai par exemple un éclairage extérieur avec des projecteurs que je ne veux pas voir s'allumer quand on dine et que l'éclairage via la guirlande est amplement suffisant et plus agréable.

  overrides:
    - input_boolean.auto_motion_salon

Pareil quand on regarde un film il ne faut pas que l'éclairage se mette en route si on bouge sur le canapé. Pour ça j'ai déjà une automation qui fonctionne sur le play/pause et qui augmente lentement l'éclairage quand je fait pause et inversement (comme au cinéma). Pour l'instant ça ne fonctionne que sur Emby, il faut que je l'intègre à Android TV et EC., je métrais à jour ici quand je le ferait).

Conclusion

Un peu comme ControlerX, voilà de quoi simplifier la chose et gagner des lignes de code. A noter que sous AppDaemon il y Wasp in a Box qui est un peu moins complet ou AD-Automoli qui lui est trop complet...

On peut en discuter ici ou sur HACF.

Home Assistant & Cloudflare Zero Trust

Une fois de plus on va parler de VPN. J'avais ici évoqué Zerotier (gratuit pour 25 nodes) que j'utilise toujours notamment pour interconnecter plusieurs sites entre eux en remplacement d'IPSEC. Ca fonctionne très bien et ça se fait oublier. Entre temps on a découvert Wireguard qui est très performant et peut être utilisé en natif (faut faire le taff) ou via des intégrations comme Tailsacle (entre autres) dont la version gratuite sera suffisante pour bien des usages.

Aujourd'hui on va parler de Cloudflare Zero Trust. Au départ je voulais juste tester sous Home Assistant car cela permet de publier son Home Assistant sans ouvrir de ports sur le routeur, de la même façon que l'on peut le faire si l'on dispose d'un abonnement Nabu Casa. J'ai cet abonnement, mais je ne veux pas l'imposer aux utilisateurs que j'aide.

Si Zero Trust est basé sur Wireguard il n'a rien d'open source. Certains n'aimeront pas quelque chose qui passe par Cloudflare qui comme beaucoup d'acteurs du marché pratique la collecte de donnée. C'est le business de l'époque, d'autres font pire mais ce n'est pas le débat ici, alors épargnez moi vos digressions sur ce sujet, ce n'est pas l'objet. Si vous choisissez cette solution c'est en connaissance de cause. Il existe des alternatives, moins simples à mettre en œuvre.

Zero Trust est un VPN orienté client qui permet notamment deux approches :

  • Publier et sécuriser, sans ouvrir de ports, un (ou plusieurs) site hébergés en premise (premise = chez vous, dans votre entreprise) et le rendre accessible publiquement avec des restrictions qui assureront sa sécurité (MFA) et surtout des restrictions géographiques qui sont quasi impossibles avec les autres solutions, notamment à cause de Let'Encrypt.
  • Rendre accessible des réseaux privés (VPN) en passant par le client Warp de Cloudflare) et ainsi accéder à toutes les machines du réseau (RDP, SMB, SSH, etc...).

Ce qui fait la force de cette solution ce sont les policies qui permettent une très grande granularité. Cloudflare Zero Trust est gratuit jusqu'à 50 utilisateurs. Et dans l'absolu pour sécuriser Home Assistant on a même pas besoin du moindre utilisateur.

Home Assistant

Un Add-on est disponible ici et sa mise œuvre est 'une simplicité enfantine mais il y a une petite subtilité. En effet il y a deux façons pour gérer un tunnel :

  • En local et en CLI
  • Depuis le dashboard de Zero Trust

On part du principe que vous avez un compte Cloudflare et qu'un de vos domaines y est géré. Vous avez également activé Zero Trust. A faire avant toute chose.

Dans les deux cas on ajoute ou modifie le fichier de configuration de Home Assistant ainci :

http:
  use_x_forwarded_for: true
  trusted_proxies:
    - 172.30.33.0/24

Option 1, gestion en local

On installe l'add-on sur Home Assistant, on choisit un host pour Home Assistant, ha.domaine.com par exemple et on lance l'add-on. Il suffit simplement ensuite d'aller copier dans le log l'url de validation et de la copier dans votre navigateur.

Et ça fonctionne, installation du certificat intermédiaire comprise. On pourra ensuite sécuriser et jouer avec les options, la doc de l'add-on est ici et cette  de Zero Trust . Si vous ne pensez pas utiliser Zero Trust pour autre chose c'est la solution la plus simple.

Option 2, gestion en remote

Vu que je compte utiliser Zero Trust à d'autres fin, c'est l'option que j'ai choisit.

  • Dans la console (Access/Tunnels) je crée un nouveau tunnel que ne configure pas.
  • Je copie le token que je viens reporter dans l'add-on et je lance l'add-on
  • Je vais dans le log et je copie l'url de validation que je colle dans le navigateur afin de finaliser l'installation et valider le certificat. 
  • Dans la gestion du tunnel je vais créer un Public Hots Name qui va correspondre à mon serveur et sera exposé avec le nom de domaine choisit :

Votre Home Assistant est maintenant accessible, ici https://test-ha.canaletto.fr (sans le port).

Aller plus loin...

En premier lieu je vous conseille de définir une policie dans Access/Applications afin que votre serveur ne soit accessible que depuis votre pays et surtout pas depuis des états plein de black hat's ...

Ensuite il faut savoir que vous avez installé sur votre serveur une passerelle VPN qui va vous permettre d'accéder à tout votre réseau local en passant par le client Warp. Pour cela il faut dans la configuration du tunnel déclarer un Private Network.

Ensuite dans les SettingsGeneral on va définir le nom de (Team) :

Dans Settings/Network on va changer le mode des tunnels. De base, dès lors que le client Warp est activé, Zero Trust va faire passer tout ce qui ne ressemble pas à une adresse privée dans ses tuyaux, c-a-d tout votre trafic, probablement à des fins de collecte de donnée... On va donc remplacer le mode Exclude par le mode Include et y déclarer uniquement notre réseau privé. Ainsi seul le trafic tunnelisé transitera par Cloudflare, et comme il est dans un tunnel ce sera théoriquement incognito. Je dis théoriquement car dans cette solution ce n'est pas vous mais Cloudflare qui a les clés... Mais nous sommes dans le cadre d'un service de classe entreprise et les CGU garantissent la confidentialité des données...

Dernier point, déclarer une méthode d'authentification. De base une authentification par code PIN est proposée, vous déclarez un domaine ou une adresse mail et vous revenez un code PIN à 6 chiffres qu'il suffit de rentrer... En option il est possible de configurer un SSO en utilisant une authentification que l'on exploite déjà (Azure AD, Centrify, Facebook, GitHub, Google Workspace, Google, LinkedIn, Okta, OneLogin, Saml, OpenID Connect, etc...). Plus complet, mais ça peut répondre à certains besoins en entreprise.

A partir de la on installe le client et on se connecte avec le nom que l'on a défini plus haut. On teste un RDP, SMB ou SSH sur une IP du réseau privé, et ça marche. Ca veut dire qu'à ce stade tout est ouvert dès lors que l'on a connecté le client, pourquoi pas dans le cadre d'une utilisation personnelle, mais je ne saurait trop vous conseiller de tout interdire et de n'autoriser que ce qui est utile (Gateway/Policies/Network).

Cet outil étant avant tout destiné à une utilisation en entreprise les possibilités sont immenses. Pour autant l'administration n'est pas très compliquée, avec quelques connaissance de base dans la gestion des réseaux.

Echanger

J'ai créé un sujet sur HACF, plus pratique qu'ici pour échanger.

 

Home Assistant & ECS

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

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

Interface

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

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

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

Automation

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

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

Et des input_boolean:

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

Et ensuite une automations :

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

        - conditions: "{{ trigger.id in ['to_off'] }}"
          sequence:
            - service: switch.turn_off
              entity_id: switch.legrand_contactor

Bonus

Pour suivre la période de chauffe on peut se servir du switch associé dans un history graph. Mais il y a plus malin à faire, en se basant sur la consommation, on va créer un binary afin de connaitre la période pour le chauffe eau chauffe réellement, et pas seulement la période ou il est on :

template:
  - binary_sensor:
      - name: "ECS Power On"
        delay_on:
          minutes: 1
        delay_off:
          minutes: 1
        state: >
          {{ states('sensor.legrand_contactor_power')|float > 0 }}

Et on aura une seconde ligne afin de savoir quand le chauffe eau était en chauffe.

 

Home Assistant, ZHA & Legrand

Il existe des devices plus ou moins bien intégrés et c'est le cas du contacteur Legrand 412170 (16A) ou 412171 (20A) (doc) qui de base est configuré en HP/HC via le signal fourni par le compteur et ou rien dans l'intégration ne permet à ce jour de la passer en mode on/off. On verra plus loin qu'il existe également une sortie de câble connectée avec ses particularités...

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

L'intégration du contacteur sous ZHA se fait avec le petit bouton qui passe en rouge.

  • Assurez-vous que la passerelle (ZHA) n'est pas en mode d'appairage
  • Appuyez 5 à 8 secondes environ jusqu'à ce qu'il devienne rouge (reset)
  • Passez maintenant votre passerelle ZHA en mode d'appairage
  • Appuyez sur le bouton de réinitialisation 5 à 8 secondes environ pour lancer la procédure d'appairage
  • Effectuez des clics successifs sur ce bouton toutes les secondes, jusqu'à ce que la LED devienne verte

La légende dit que les produits Legrand ne fonctionneraient que sur le canal 11, mon réseau est en 15 et d'autres l'on fait fonctionner en 25...

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

Pour info le cluster 64513 (en décimal) = FC01 (en hexa)

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

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

Mise en place

Très simple !

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

Bonus

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

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

Consommation

Pour l'instant l'intégration ne présente que la puissance en W ou A (encore que je n'ai pas testé). Je vais voir quand j'aurais du temps si on peut ajouter le cumul en kWh, mais c'est toujours possible sous HA avec Reiman ou Powercalc. Voici avec Powercalc qui s'il est bien configuré a l'avantage de créer les utilility_meter: à coller dans Energy...

sensor:
  - platform: powercalc
    name: ECS
    entity_id: switch.legrand_ecs
    power_sensor_id: sensor.legrand_ecs_active_power

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

Le cas de la sortie de câble connectée

Dans le même genre on trouve une sortie de câble connectée sous différentes références (0 648 79/82/83/98). Ici encore l'intégration ZHA est très incomplète, contrairement à Z2M comme on peut le voir ici. En effet ce module supporte plusieurs modes, alors que le seul appairage est géré directement sous ZHA, ainsi que la puissance instantanée en watts.

Mode dimmer : (ce mode nécessite un firmware récent)

  • 0x0100 : dimmer_on :
  • 0x0100 : dimmer_off

Mode contacteur :

  • 0x0003 : mode switch : Fonctionnement en on/off
  • 0x0004 : mode auto : Fonctionnement en auto, je suppose en HP/HC mais il n'y a pas d'entrée pour le pilotage. Ou domotique Legrand...

Mode fil pilote : (pilot wire)

  • 0x0002 : pilot_on :  Attente des commandes
  • 0x0001 : pilot_off :  Désactivation, on repasse en on/off ?

L'idée est bien sur de contourner ce manque, et si c'est faisable avec Z2M, il n'y a pas de raisons de ne pas y parvenir avec ZHA afin de pouvoir soutenir cet objet sur des installations les plus légères possible.

Changer de mode

service: zha_toolkit.attr_write
data:
  ieee: 00:04:74:00:00:23:3d:b5
  cluster: 64513
  attribute: 0
  manf: 4129
  attr_type: 9
  attr_val:
    - 2      #  Pilot Wire mode
    - 0
  event_done: legrand_done

Piloter un convecteur disposant d'un fil pilote

Le fil pilote est un truc bien Français né de l'abondance de l'électricité atomique qu'il fallait alors promouvoir. Pour faire simple on envoie des commandes qui vont faire adopter une consigne pré réglée sur le convecteurs et qui interagira avec la sonde interne. Bien sur la sonde interne est nécessairement faussée car trop proche des éléments d chauffe. C'est pourquoi sous Home Assistant on dispose de plusieurs composant de thermostats virtuel (le tout dernier est ici) plus ou moins évolués, mais qui se basent sur une sonde externe bin plus réaliste de la température ambiante.

Avec un convecteur ainsi piloté, on peu don soit envoyer des commandes prédéfinie, soit faire un fake switch avec confort (une sorte de on avec un préréglage haut sur le convecteur) et off et le piloter avec un thermostat virtuel.

Il faut savoir qu'un convecteur avec fil pilote ne répond pas à un simple on/off (sauf à insérer une diode dans le circuit). Il va donc falloir sous ZHA pouvoir envoyer les bonnes commandes, et à minima Confort et Off.

Commandes disponibles :

  • 0x00 : Confort
  • 0x01 : Confort -1
  • 0x02 : Confort -2
  • 0x03 : Eco
  • 0x04 : Hors Gel
  • 0x05 : Off

Il ne s'agit pas ici d'écrire un attribut comme pour changer le mode, mais d'envoyer une commande. Et c'est ici que ça se complique

service: zha_toolkit.zcl_cmd
data:
  ieee: 00:04:74:00:00:23:3d:b5
  cmd: 0
  args: [3]
  cluster: 64576
  endpoint: 1
  manf:  0x1021
  event_done: legrand_done

Ensuite on peut lire l'attribut afin de considérer que la commande est acceptée :

service: zha_toolkit.attr_read
data:
  ieee: 00:04:74:00:00:23:3d:b5
  endpoint: 1
  cluster: 64576
  attribute: 0
  event_done: legrand_done

Ce qui va nous retourner :

event_type: legrand_done
data:
  zha_toolkit_version: v0.8.35
  zigpy_version: 0.53.0
  zigpy_rf_version: 0.9.2
  ieee_org:
    - 181
    - 61
    - 35
    - 0
    - 0
    - 116
    - 4
    - 0
  ieee: 00:04:74:00:00:23:3d:b5
  command: attr_read
  command_data: null
  start_time: "2023-02-17T14:54:41.625317+00:00"
  errors: []
  params:
    cluster_id: 64576
    attr_id: 0
    dir: 0
    manf: 4129
    tries: 1
    expect_reply: true
    args: []
    state_id: var.legrand
    state_attr: unique_attr_name_for_ieee
    allow_create: true
    event_done: legrand_done
    read_before_write: true
    read_after_write: true
  attr_type: "0x30"
  write_is_equal: false
  result_read:
    - "0": 5
    - {}
  success: true
origin: LOCAL
time_fired: "2023-02-17T14:54:41.737600+00:00"
context:
  id: 01GSFXXJJ9GYVB5E0S54DVB2YP
  parent_id: null
  user_id: null

Afin de pouvoir l'exploiter on va écrire le résultat dans un état :

service: zha_toolkit.attr_read
data:
  ieee: 00:04:74:00:00:23:3d:b5
  cluster: 0xFC40
  attribute: 0
  manf:  0x1021
  event_done: legrand_done
  state_id: var.legrand
  state_attr: unique_attr_name_for_ieee
  allow_create: true          

Que l'on pourra plus facilement lire :

"{{ is_state_attr('var.legrand', 'unique_attr_name_for_ieee', 5) }}"

Fake Switch

Etant donné que je ne vais pas utiliser le fil pilote autrement que pour faire du on/off afin de commander mon convecteur par un thermostat virtuel associé à une sonde externe, il me faut un switch: à associer au thermostat.

Je vais commencer par faire deux scripts: ON et OFF. Ceux ci ont deux fonction :

  • Envoyer la commande Confort ou OFF
  • Lire l'état afin de confirmer sa prise en compte
script:
  pilot_wire_on:
    alias: "Pilot Wire ON"
    sequence:
      - service: zha_toolkit.zcl_cmd
        data:
          ieee: 00:04:74:00:00:23:3d:b5
          cmd: 0
          args: '0'
          cluster: 64576
          endpoint: 1
          manf:  0x1021
          event_done: legrand_done     
      - service: zha_toolkit.attr_read
        data:
          ieee: 00:04:74:00:00:23:3d:b5
          cluster: 0xFC40
          attribute: 0
          manf:  0x1021
          event_done: legrand_done
          state_id: var.legrand
          state_attr: unique_attr_name_for_ieee
          allow_create: true

Ensuite je crée mon switch: virtuel (pour l'instant je n'ai pas trouvé mieux que de l'associer à un input_boolean: ... à retravailler) :

input_boolean:
  dummy:

switch:
  - platform: template
    switches:
      pilot_wire_sdb:
        friendly_name: 'Convecteur : Salle de Bain'
        # value_template: "{{ is_state('switch.pilot_wire_sdb', 'on') }}"
        value_template: "{{ is_state('input_boolean.dummy', 'on') }}"
        turn_on:
          - service: input_boolean.turn_on
            entity_id: input_boolean.dummy
          - service: script.pilot_wire_on

        turn_off:
          - service: input_boolean.turn_off
            entity_id: input_boolean.dummy          
          - service: script.pilot_wire_off

Et pour terminer je vais créer un binary_sensor: qui va me permettre d'afficher l'état réel en fonction de la lecture :

template:
  - trigger:
      - platform: event
        event_type: legrand_done
        event_data:
          ieee: 00:04:74:00:00:23:3d:b5
          command: attr_read
      - platform: state
        entity_id: binary_sensor.pilot_wire_3
        to: "off"
    binary_sensor:
      name: pilot_wire_3
      icon: "{{ (is_state_attr('var.legrand', 'unique_attr_name_for_ieee', 5)) | iif('mdi:radiator', 'mdi:radiator-off') }}"
      state: "{{ is_state_attr('var.legrand', 'unique_attr_name_for_ieee', 5) }}"

Pour l'instant la puissance en watts (entre autres mais c'est celle ci qui serait utile) ne remonte pas. Je continue à chercher, mais sur une charge fixe, un convecteur par exemple, le contournement simple consiste à utiliser PowerCalc pour la déduire...

Tout cela est certainement perfectible, mais ça nous donne les base pour exploiter totalement ce couteux objet ! En attendant une hypothétique réelle intégration, comme cela a été fait sous Z2M.

Un grand merci à Mario, l'auteur de ZHA Toolkit, pour sa grande patience !

Liens

En vrac, mes sources :

 

Home Assistant & Remote Alarm

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

Pourquoi pas, il doit y avoir une raison à ce choix (probablement le bouton du bas qui passe en mode "triggered" quelque soit l'état). Sauf que pour gérer les clavier on configure un code dans ZHA, code que va attendre cette télécommande pour se désarmer. Dans ZHA on peu se passer de code pour armer, mais pas pour désarmer. Et comme le bouton désarmer de la télécommande n'envoie pas ce code elle ne désarme pas. De fait on ne peut pas se servir de sont état "disarmed" pour désarmer Alarmo...

Il va donc falloir passer par une petite automation intermédiaire afin de lui faire manger ce code et ensuite avoir un comportement normal de ce panneau d'alarme pour le désarmement... enfin je pensais que ça suffirait....

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

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

A noter que si on utilise plusieurs télécommandes (celle ci ou celle de chez Woox, ou un clavier), il conviendra de tout désarmer toutes les autres unités qui auraient pu êtres utilisées pour l'armement.

Bon, c'est une solution, mais on ne peut pas dire que ce soit très propre...

A noter que ces mêmes télécommandes sous Z2M transmettent des informations exploitables...

> arm_day_zones
> arm_all_zones
> disarm
> emergency

Alternative

En attendant que les développeurs prennent en compte nos demandes, j'ai peut être trouvé une alternative un peu moins sale. J'utilise ControllerX sous AppDaemon pour gérer toutes mes télécommandes et il se trouve qu'il sait traiter les Events sous forme de template. Je peux donc facilement remplacer mon automation par ce code :

remote_alarm:
  module: controllerx
  class: Controller
  controller: 
    - "a4:c1:48:96:0c:cf:c9:68:1:0x0501"  # Woox RC
    - "58:8e:81:zz:fe:26:12:64:1:0x0501"  # Linkind RC
    - "5c:02:72:xx:fe:e9:2f:9a:1:0x0501"  # Heiman RC
    - "68:0a:e2:af:fe:ea:89:22:1:0x0501"  # Linkind Keypad
  light: light.my_fake_light
  integration:
    name: event
    event_type: zha_event
    controller_key: unique_id
    action_template: "{command}_{args[arm_mode]}_{args[arm_mode_description]}_{args[code]}#"    # Ici il n' a pas de code reçu
  mapping:
    arm_0_Disarm_#:   # Ici il n' a pas de code reçu
      - service: input_button.press
        target:
          entity_id: input_button.disarm_alarm    
      - service: alarm_control_panel.alarm_disarm
        data:
          code: !secret alarm_code_zha
          entity_id:
            - alarm_control_panel.lk_zb_keypad 
            - alarm_control_panel.lk_zb_remote
            - alarm_control_panel.heiman_remote
            - alarm_control_panel.woox_01

    arm_0_Disarm_1234#:   # Ici on a le code du clavier défini dans ZHA
      - service: input_button.press
        target:
          entity_id: input_button.disarm_alarm          
      - service: alarm_control_panel.alarm_disarm
        data:
          code: !secret alarm_code_zha
          entity_id: 
            - alarm_control_panel.lk_zb_remote
            - alarm_control_panel.heiman_remote
            - alarm_control_panel.woox_01

    arm_0_Disarm_314#:   # Ici on peut traiter n'importe quel code reçu par le clavier et générer une action...
      - service: notify.slack_hass_canaletto
        data:
          message: "{{now().strftime('%d/%m/%Y, %H:%M:%S')}} > Keyboard 314" 

Ici je désarme en plus le sirènes (une sous Z2M qui ne l'expose pas en tant que sirène et les autres en ZHA. Ensuite je notifie Slack qui me sert de log et me permettra de savoir quelle télécommande à désarmé le système. En ce qui concerne le clavier il est également possible d'utiliser plusieurs codes pour désarmer, les traiter via les events et ainsi savoir quel code (confié à une seule personne) à désarmé le système...

    - 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        
    - delay : '00:00:08'
    - service: notify.slack_hass_canaletto
      data:
        message: "{{now().strftime('%d/%m/%Y, %H:%M:%S')}} > Disarm all remotes by : {{ trigger.id }}" 

Exploitation

Armement de l'alarme

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

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

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

Désarmement de l'alarme

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

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

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

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

  1. Elle se déclenche à partir des event's des différents devices
  2. Elle change l'état de l'entité Alarm Control Panel des différents devices
  3. Elle envoie un input_button.press qui va déclencher l'automation de désarmement.

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

Bref, voilà comment perdre un après midi... J'ai réédité cet article plusieurs fois et j'y reviendrait certainement.

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

EDIT du 27/07/2023

Toujours pour ces télécommandes sous ZHA j'ai depuis trouvé une autre solution en passant par un template: qui va changer l'état d'un binary_sensor:. L'idée est d'écouter un event et de basculer l'état du binary_sensor: quelque secondes. Bien sur la télécommande passe en mode triggered, mais on peut annuler facilement cet état dans l'automation de traitement ou on se sert de l'état du binary_sensor: pour désactiver :

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

Ensuite une automation sur trigger/state (j'en mets qu'un seul ici mais dans la pratique il y a toutes les télécommandes, claviers et RFID qui me servent à désactiver mes alarmes Alarmo et Visonic)

automation:
- id: '2bd0ertyyf-43fa-45f98f-aed0-disarm'
  alias: "Alarm @ Remote Disarm"
  description: 'Disarm Remote Control panel'
  trigger:
    - platform: state
      entity_id: binary_sensor.woox_rc1_to_disarmed
      to: "on"
      id: "Woox RC 1"

Et dans les actions la première chose à faire est de désactiver le triggered de la télécommande. On peut conditionner avec un tigger.id ou le faire pour toutes :

  action:
    - choose: # DISARM
        - conditions: "{{ trigger.id in ['Woox RC 1'] }}"
          sequence:
            - service: alarm_control_panel.alarm_disarm
              data:
                code: !secret alarm_code_zha
              target:
                entity_id: 
                  - alarm_control_panel.woox_01  # Ici on désactive la télécommande

                  - alarm_control_panel.alarmo   # Ici on désactive Alarmo
                  - alarm_control_panel.visonic  # Ici on désactive Visonic

J'ai simplifié pour l'exemple car dans la pratique je désactive un faux alarm_control_panel: qui me sert à commander les deux alarmes et d'autres choses liées aux personnes, volets, etc...

            - service: alarm_control_panel.alarm_disarm
              data:
                code: !secret alarm_code_zha
              target:
                entity_id: 
                  - alarm_control_panel.home_alarm_command # Fake Alarm       

L'idéal serait de pouvoir modifier le comportement de ces télécommandes avec un Quirk sous ZHA afin qu'elles se comportent comme sous Z2M. D'après mes recherches c'est faisable, mais je ne sais pas faire.

Depuis j'ai également testé les petits télécommandes Shelly en BLE avec un seul bouton multifonction, ça fait le job et c'est plus simple. Avantage on évite le temps de reconnexion au réseau.

J'ai passé beaucoup de temps (trop) à tester toutes les possibilités dans tous les sens avec l'objectif que cela fonctionne sur une base ZHA afin de le rendre facile duplicable. De fait j'ai pas mal de choses empilée et la prochaine étape consistera à simplifier au maximum et d'écrire un article plus clair...

Sources

 

Home Assistant, boost ponctuel du chauffage

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

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

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

Ensuite la liste des courses...

timer:
  shower:
    name: Boost SdB

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

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

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

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

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

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

Voilà !

 

Home Assistant, nommage, Energy & SAV.

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

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

Le nommage

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

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

Energy

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

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

EDIT 06/02/2023 : Les utility_meter: (UM) sont entre autre destinés à aller dans le module Energy. Leur fonction est de mesurer la consommation d'un appareil et non d'un module ou prise. Donc contrairement à ce que j'ai expliqué et fait ci dessous, un UM pour un lave linge doit s'appeler Lave Linge et son entity_id: être cohérent. Pour ne pas mélanger je métrais un préfixe, donc sensor.um_lave_linge et non le nom de la prise comme je l'ai fait.

En cas de problème avec une source de consommation, il est maintenant possible de changer celle ci en cliquant sur le bouton options. Ca n'a pas toujours été le cas, notamment en yaml ou il m'est arrivé de perdre des données. Pareil en YAML pour les UM précédemment crées, on change la source mais jamais le nom de l'entité. Exemple ou j'avais un Shelly que j'ai remplacé par un Legrand.

  energy_total_yearly_1pm_ecs:
    source: sensor.legrand_contactor_energy_integration
    cycle: yearly

 Et si on veut changer le nom qui s'affiche dans le module Energy, on passe par customize.yaml :

sensor.energy_total_yearly_1pm_ecs:
  friendly_name: 'ECS'

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

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

 

Home Assistant, planification, encore...

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Et enfin l'automation qui gère le mode Manuel / Auto : Si on passe en manuel on désactive l'automation et j'ai fait le choix de passer le thermostat en off. Il est bien sur possible de le réactiver, mais il ne sera plus géré par la planification.

- id: 56dd275e-3f52-4d8ffa862-sejour
  description: Comfort - Sejour - Mode Auto
  alias: "Comfort - Sejour - Mode Auto"
  trigger:
    - platform: state
      entity_id: automation.comfort_sejour_immediate
      to: "on"
      id: "on"
    - platform: state
      entity_id: automation.comfort_sejour_immediate
      to: "off"
      id: "off"
  action:
    - choose:
        - conditions: "{{ trigger.id in ['on', 'xx'] }}"
          sequence:
            - service: automation.trigger
              target:
                entity_id: "automation.comfort_sejour_immediate"
    - choose:
        - conditions: "{{ trigger.id in ['off', 'xx'] }}"
          sequence:
            - service: climate.set_hvac_mode
              data:
                hvac_mode: "off"
              target:
                entity_id: climate.thermostat_sejour

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

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

Bonus :

La carte avec tous les modes : (je sais c'est long, à dupliquer selon le nombre de convecteurs).

type: vertical-stack
cards:
  - type: custom:simple-thermostat
    entity: climate.thermostat_sejour
    layout:
      mode:
        icons: true
        headings: false
        names: true
      step: row
    hide:
      temperature: true
      state: true
    sensors:
      - entity: sensor.mi_sejour_temp
        icon: mdi:thermometer
      - entity: binary_sensor.state_openings_life
        icon: mdi:door
      - entity: sensor.sejour_puissance
        icon: mdi:lightning-bolt
    header:
      toggle:
        entity: automation.comfort_sejour_immediate
        name: Manuel/Auto
      name: Bureau
    control:
      - hvac
  - type: history-graph
    entities:
      - entity: switch.sw13_sejour_1
        name: ' '
    hours_to_show: 24
    refresh_interval: 0
  - type: conditional
    conditions:
      - entity: automation.comfort_sejour_immediate
        state: 'on'
    card:
      type: entities
      entities:
        - entities:
            - entity: input_boolean.heating_sejour_enabled_1
              name: NUIT
              toggle: true
            - entity: input_boolean.heating_sejour_enabled_2
              name: MATIN
              toggle: true
            - entity: input_boolean.heating_sejour_enabled_3
              name: MIDI
              toggle: true
            - entity: input_boolean.heating_sejour_enabled_4
              name: SOIR
              toggle: true
          entity: climate.thermostat_sejour
          name: Semaine
          show_state: false
          toggle: false
          icon: mdi:calendar-range
          type: custom:multiple-entity-row
          state_color: true
        - entities:
            - entity: input_boolean.heating_sejour_enabled_1_d
              name: NUIT
              toggle: true
            - entity: input_boolean.heating_sejour_enabled_2_d
              name: MATIN
              toggle: true
            - entity: input_boolean.heating_sejour_enabled_3_d
              name: MIDI
              toggle: true
            - entity: input_boolean.heating_sejour_enabled_4_d
              name: SOIR
              toggle: true
          entity: climate.thermostat_sejour
          name: Week-End
          show_state: false
          toggle: false
          icon: mdi:calendar-range
          type: custom:multiple-entity-row
          state_color: true
        - entities:
            - entity: input_number.heating_sejour_temperature_eco
              type: custom:numberbox-card
              icon_plus: mdi:chevron-up
              icon_minus: mdi:chevron-down
            - entity: input_number.heating_sejour_temperature_away
              type: custom:numberbox-card
              icon_plus: mdi:chevron-up
              icon_minus: mdi:chevron-down
            - entity: binary_sensor.life_windows_and_doors_delayed
            - label: Présence dans la pièce
              type: section
            - entity: input_boolean.presence_sejour
              name: Présence
              show_state: true
            - entity: input_datetime.next_sejour_start
              type: custom:multiple-entity-row
              format: datetime
              name: Arrivée
            - entity: input_datetime.next_sejour_stop
              type: custom:multiple-entity-row
              format: datetime
              name: Départ
          head:
            label: OPTIONS >
            type: section
          padding: 30
          type: custom:fold-entity-row
      title: PLANIFICATION
      show_header_toggle: false
      state_color: true
  - type: conditional
    conditions:
      - entity: input_boolean.heating_sejour_enabled_1
        state: 'on'
      - entity: automation.comfort_sejour_immediate
        state: 'on'
    card:
      type: custom:vertical-stack-in-card
      title: Séjour / Semaine / Nuit
      cards:
        - entity: input_number.heating_sejour_temperature_confort_1
          type: custom:numberbox-card
          icon_plus: mdi:chevron-up
          icon_minus: mdi:chevron-down
          name: Température de confort
          icon: false
        - type: horizontal-stack
          cards:
            - entity: input_datetime.heating_sejour_start_1
              type: custom:time-picker-card
              layout:
                align_controls: center
                embedded: true
              hide:
                name: true
                icon: true
            - type: glance
              show_state: false
              entities:
                - binary_sensor.heating_sejour_1
            - entity: input_datetime.heating_sejour_end_1
              type: custom:time-picker-card
              layout:
                align_controls: center
                embedded: true
              hide:
                name: true
                icon: true
  - type: conditional
    conditions:
      - entity: input_boolean.heating_sejour_enabled_2
        state: 'on'
      - entity: automation.comfort_sejour_immediate
        state: 'on'
    card:
      type: custom:vertical-stack-in-card
      title: Séjour / Semaine / Matin
      cards:
        - entity: input_number.heating_sejour_temperature_confort_2
          type: custom:numberbox-card
          icon_plus: mdi:chevron-up
          icon_minus: mdi:chevron-down
          name: Température de confort
          icon: false
        - type: horizontal-stack
          cards:
            - entity: input_datetime.heating_sejour_start_2
              type: custom:time-picker-card
              layout:
                align_controls: center
                embedded: true
              hide:
                name: true
                icon: true
            - type: glance
              show_state: false
              entities:
                - binary_sensor.heating_sejour_2
            - entity: input_datetime.heating_sejour_end_2
              type: custom:time-picker-card
              layout:
                align_controls: center
                embedded: true
              hide:
                name: true
                icon: true
  - type: conditional
    conditions:
      - entity: input_boolean.heating_sejour_enabled_3
        state: 'on'
      - entity: automation.comfort_sejour_immediate
        state: 'on'
    card:
      type: custom:vertical-stack-in-card
      title: Séjour / Semaine / Midi
      cards:
        - entity: input_number.heating_sejour_temperature_confort_3
          type: custom:numberbox-card
          icon_plus: mdi:chevron-up
          icon_minus: mdi:chevron-down
          name: Température de confort
          icon: false
        - type: horizontal-stack
          cards:
            - entity: input_datetime.heating_sejour_start_3
              type: custom:time-picker-card
              layout:
                align_controls: center
                embedded: true
              hide:
                name: true
                icon: true
            - type: glance
              show_state: false
              entities:
                - binary_sensor.heating_sejour_3
            - entity: input_datetime.heating_sejour_end_3
              type: custom:time-picker-card
              layout:
                align_controls: center
                embedded: true
              hide:
                name: true
                icon: true
  - type: conditional
    conditions:
      - entity: input_boolean.heating_sejour_enabled_4
        state: 'on'
      - entity: automation.comfort_sejour_immediate
        state: 'on'
    card:
      type: custom:vertical-stack-in-card
      title: Séjour / Semaine / Soir
      cards:
        - entity: input_number.heating_sejour_temperature_confort_4
          type: custom:numberbox-card
          icon_plus: mdi:chevron-up
          icon_minus: mdi:chevron-down
          name: Température de confort
          icon: false
        - type: horizontal-stack
          cards:
            - entity: input_datetime.heating_sejour_start_4
              type: custom:time-picker-card
              layout:
                align_controls: center
                embedded: true
              hide:
                name: true
                icon: true
            - type: glance
              show_state: false
              entities:
                - binary_sensor.heating_sejour_4
            - entity: input_datetime.heating_sejour_end_4
              type: custom:time-picker-card
              layout:
                align_controls: center
                embedded: true
              hide:
                name: true
                icon: true
  - type: conditional
    conditions:
      - entity: input_boolean.heating_sejour_enabled_1_d
        state: 'on'
      - entity: automation.comfort_sejour_immediate
        state: 'on'
    card:
      type: custom:vertical-stack-in-card
      title: Séjour / Week-End / Nuit
      cards:
        - entity: input_number.heating_sejour_temperature_confort_1_d
          type: custom:numberbox-card
          icon_plus: mdi:chevron-up
          icon_minus: mdi:chevron-down
          name: Température de confort
          icon: false
        - type: horizontal-stack
          cards:
            - entity: input_datetime.heating_sejour_start_1_d
              type: custom:time-picker-card
              layout:
                align_controls: center
                embedded: true
              hide:
                name: true
                icon: true
            - type: glance
              show_state: false
              entities:
                - binary_sensor.heating_sejour_1_d
            - entity: input_datetime.heating_sejour_end_1_d
              type: custom:time-picker-card
              layout:
                align_controls: center
                embedded: true
              hide:
                name: true
                icon: true
  - type: conditional
    conditions:
      - entity: input_boolean.heating_sejour_enabled_2_d
        state: 'on'
      - entity: automation.comfort_sejour_immediate
        state: 'on'
    card:
      type: custom:vertical-stack-in-card
      title: Séjour / Week-End / Matin
      cards:
        - entity: input_number.heating_sejour_temperature_confort_2_d
          type: custom:numberbox-card
          icon_plus: mdi:chevron-up
          icon_minus: mdi:chevron-down
          name: Température de confort
          icon: false
        - type: horizontal-stack
          cards:
            - entity: input_datetime.heating_sejour_start_2_d
              type: custom:time-picker-card
              layout:
                align_controls: center
                embedded: true
              hide:
                name: true
                icon: true
            - type: glance
              show_state: false
              entities:
                - binary_sensor.heating_sejour_2_d
            - entity: input_datetime.heating_sejour_end_2_d
              type: custom:time-picker-card
              layout:
                align_controls: center
                embedded: true
              hide:
                name: true
                icon: true
  - type: conditional
    conditions:
      - entity: input_boolean.heating_sejour_enabled_3_d
        state: 'on'
      - entity: automation.comfort_sejour_immediate
        state: 'on'
    card:
      type: custom:vertical-stack-in-card
      title: Séjour / Week-End / Midi
      cards:
        - entity: input_number.heating_sejour_temperature_confort_3_d
          type: custom:numberbox-card
          icon_plus: mdi:chevron-up
          icon_minus: mdi:chevron-down
          name: Température de confort
          icon: false
        - type: horizontal-stack
          cards:
            - entity: input_datetime.heating_sejour_start_3_d
              type: custom:time-picker-card
              layout:
                align_controls: center
                embedded: true
              hide:
                name: true
                icon: true
            - type: glance
              show_state: false
              entities:
                - binary_sensor.heating_sejour_3_d
            - entity: input_datetime.heating_sejour_end_3_d
              type: custom:time-picker-card
              layout:
                align_controls: center
                embedded: true
              hide:
                name: true
                icon: true
  - type: conditional
    conditions:
      - entity: input_boolean.heating_sejour_enabled_4_d
        state: 'on'
      - entity: automation.comfort_sejour_immediate
        state: 'on'
    card:
      type: custom:vertical-stack-in-card
      title: Séjour / Week-End / Soir
      cards:
        - entity: input_number.heating_sejour_temperature_confort_4_d
          type: custom:numberbox-card
          icon_plus: mdi:chevron-up
          icon_minus: mdi:chevron-down
          name: Température de confort
          icon: false
        - type: horizontal-stack
          cards:
            - entity: input_datetime.heating_sejour_start_4_d
              type: custom:time-picker-card
              layout:
                align_controls: center
                embedded: true
              hide:
                name: true
                icon: true
            - type: glance
              show_state: false
              entities:
                - binary_sensor.heating_sejour_4_d
            - entity: input_datetime.heating_sejour_end_4_d
              type: custom:time-picker-card
              layout:
                align_controls: center
                embedded: true
              hide:
                name: true
                icon: true