Une télécommande Zigbee maison avec ESP32-H2 et OLED

Une télécommande maison à cinq boutons et écran OLED pour commander deux volets et une lampe avec Home Assistant. Schéma, programme ESPHome et automatisations inclus.
Illustration de la télécommande à cinq boutons et écran OLED ; les photographies du montage réel figurent dans l’article.

Sommaire

Commander deux volets et une lampe sans sortir son téléphone : c’est le besoin à l’origine de cette petite télécommande faite maison. J’ai réuni une carte ESP32-H2-Zero, cinq boutons poussoirs et un écran OLED dans un boîtier à façade métallique. Le résultat est une commande physique qui rejoint mon réseau Zigbee et déclenche des actions dans Home Assistant.

Dans cet article, je partage la réalisation, son câblage et son programme ESPHome complet. Vous trouverez aussi les huit automatisations Home Assistant à adapter à vos équipements.

Le contenu de cet article a été testé et validé avec la version 2026.10.4 de Home Assistant.

Cette réalisation est alimentée par USB. Le câble fournit l’énergie ; les commandes passent par Zigbee. Une version sur batterie est envisageable, mais demande une autre gestion de l’alimentation et du réveil : nous y reviendrons en fin d’article.

La télécommande réalisée : deux volets et une lampe accessibles depuis cinq boutons et un écran OLED.
La télécommande réalisée : deux volets et une lampe accessibles depuis cinq boutons et un écran OLED.

Cinq boutons pour huit commandes

L’écran présente trois équipements : Jardin, Véranda et Lampe. Les boutons gauche et droite déplacent la sélection. Les trois autres boutons agissent sur l’équipement choisi.

  • Gauche : Volet sélectionné : Équipement précédent ; Lampe sélectionnée : Équipement précédent
  • Droite : Volet sélectionné : Équipement suivant ; Lampe sélectionnée : Équipement suivant
  • Haut : Volet sélectionné : Monter ; Lampe sélectionnée : Allumer
  • Centre : Volet sélectionné : Arrêter ; Lampe sélectionnée : Aucune action
  • Bas : Volet sélectionné : Descendre ; Lampe sélectionnée : Éteindre

La sélection tourne en boucle : Jardin → Véranda → Lampe. Un trait sous le nom de l’équipement indique la cible. Après une commande, un pictogramme et un texte apparaissent pendant environ 1,5 seconde.

Ce que confirme l’écran : l’appui a été pris en compte localement. L’OLED n’affiche pas la position réelle des volets ni l’état réel de la lampe. Une indication « LAMPE ON » ne confirme donc pas que la lampe s’est effectivement allumée.

Du bouton à Home Assistant

Dans cette installation, le chemin suivi est le suivant :

  1. Boutons et écran OLED : sélection et commande de l’équipement sur la télécommande.
  2. ESP32-H2 : réception de l’appui et transmission en Zigbee.
  3. Coordinateur Zigbee : réception du message et transmission à Zigbee2MQTT.
  4. Zigbee2MQTT et MQTT : mise à disposition de la commande pour Home Assistant.
  5. Home Assistant : déclenchement de l’automatisation qui commande le volet ou la lampe.

Le programme crée huit commandes : montée, arrêt et descente pour chacun des deux volets, puis allumage et extinction pour la lampe. Chaque commande est représentée par un capteur binaire : il passe à ON pendant 500 ms, puis revient à OFF. Home Assistant réagit au passage de OFF à ON.

ESPHome sert ici à programmer la carte. La télécommande ne se connecte ni en Wi-Fi ni à l’API native ESPHome. Son intégration à Home Assistant passe par Zigbee2MQTT et MQTT.

Le programme configure un appareil terminal Zigbee (router: false). Il ne joue pas le rôle de répéteur. Les actions passent par Home Assistant : le fonctionnement décrit dépend donc du coordinateur, de Zigbee2MQTT, de MQTT et des automatisations.

Le composant Zigbee d’ESPHome prend également en charge ZHA, mais la procédure et les exemples de cet article concernent mon installation Zigbee2MQTT.

Le matériel nécessaire

  • 1 : Carte Waveshare ESP32-H2-Zero, ou Zero-M avec connecteurs soudés
  • 1 : Écran OLED SH1106 I²C, 128 × 64, 1,3 pouce, compatible avec une alimentation et des signaux en 3,3 V
  • 5 : Boutons poussoirs momentanés à contact normalement ouvert
  • 1 : Câble USB-C permettant le transfert de données pour la programmation
  • 1 : Alimentation USB 5 V adaptée au montage
  • Selon réalisation : Plaque à pastilles, fils, connecteurs, entretoises, isolants et boîtier

Le boîtier peut être adapté à vos moyens de fabrication. La façade métallique fait partie de ma réalisation ; elle n’est pas nécessaire au fonctionnement électronique.

Les prérequis logiciels

Avant de commencer, les deux volets et la lampe doivent déjà fonctionner dans Home Assistant. Il faut également disposer d’un coordinateur Zigbee, de Zigbee2MQTT, d’un serveur MQTT et de l’intégration MQTT dans Home Assistant.

Le programme fourni a été validé et compilé avec ESPHome 2026.8.2 et ESP-IDF 5.5.5 lors de la préparation du tutoriel. Ce sont les versions de référence pour reproduire cette vérification. Le YAML déclare un minimum de 2026.5.0 ; avec une autre version, refaire la validation et la compilation avant de programmer la carte.

La documentation Zigbee ESPHome demande Zigbee2MQTT 2.8.0 au minimum pour les noms contenant des espaces, comme ceux de ce programme. Une connexion Internet est nécessaire lors de la première compilation pour les outils et les polices Roboto.

Le schéma et le câblage

Schéma de câblage correspondant au programme : OLED sur GPIO10 et GPIO11, cinq boutons reliés à la masse.
Schéma de câblage correspondant au programme : OLED sur GPIO10 et GPIO11, cinq boutons reliés à la masse.

Ce schéma utilise les noms des GPIO. La disposition dessinée ne représente pas l’emplacement physique des broches de la carte. Pour les retrouver, consulter le brochage officiel Waveshare ESP32-H2-Zero.

Raccorder l’écran

  • VCC : 3V3
  • GND : GND
  • SDA : GPIO10
  • SCL : GPIO11

Le programme utilise le modèle SH1106 128x64 et l’adresse I²C 0x3C. La plateforme se nomme ssd1306_i2c : c’est normal, elle gère aussi les écrans SH1106.

❗
Vérifier les inscriptions sur le module avant de souder : l’ordre de ses quatre broches peut varier d’un écran à l’autre.

Raccorder les boutons

Chaque bouton relie son GPIO à GND pendant l’appui. Les cinq retours de masse sont communs. Les noms ci-dessous correspondent à la vue de face de la télécommande.

  • Gauche : GPIO : GPIO3 ; Autre borne : GND
  • Droite : GPIO : GPIO1 ; Autre borne : GND
  • Haut : GPIO : GPIO5 ; Autre borne : GND
  • Centre : GPIO : GPIO2 ; Autre borne : GND
  • Bas : GPIO : GPIO4 ; Autre borne : GND

Les résistances de tirage internes sont activées (pullup: true) et l’appui est détecté à l’état bas (inverted: true). Un filtre de 20 ms limite les rebonds. Avec les fils courts de ce montage, les boutons n’ont pas besoin de résistance de tirage externe. Cette remarque ne concerne pas le bus I²C, dont les résistances sont généralement présentes sur le module OLED.

L’arrière de la réalisation : carte ESP32-H2-Zero, plaque à pastilles et câblage des poussoirs.
L’arrière de la réalisation : carte ESP32-H2-Zero, plaque à pastilles et câblage des poussoirs.

Sur cette vue arrière, gauche et droite sont inversées par rapport à la face avant. Les couleurs de mes fils ne constituent pas un code électrique : se fier aux connexions et aux repères GPIO.

Avant d’alimenter

Câbler avec l’USB débranché. Vérifier la polarité de l’OLED, l’absence de court-circuit entre 3V3 et GND et le contact de chaque poussoir au multimètre. Pour un poussoir à quatre pattes, identifier les deux groupes de contacts.

Isoler les soudures de la façade métallique et maintenir les fils pour éviter toute traction. Les boutons vont uniquement à la masse, jamais au +5 V. L’alimentation de la carte se fait par son USB-C, sans ajouter une seconde source en parallèle.

Le montage reste en très basse tension : il ne se raccorde pas directement aux fils de puissance des volets.

Programmer l’ESP32-H2

Le programme complet figure dans l’annexe en fin d’article et dans le fichier telecommande-chambre-h2.yaml du dossier de partage. Il comprend l’interface OLED, les cinq entrées et les huit commandes Zigbee.

  1. Dans ESPHome Device Builder, créer une configuration nommée telecommande-chambre-h2, puis ouvrir son éditeur YAML.
  2. Remplacer tout le contenu généré par le programme fourni. Les sections Wi-Fi, API ou OTA éventuellement créées par l’assistant ne font pas partie de cette configuration.
  3. Valider et compiler le programme.
  4. Programmer la carte par USB. Si elle est branchée à votre ordinateur plutôt qu’à la machine qui héberge ESPHome, télécharger l’image de première installation factory et utiliser un outil USB compatible ESP32-H2, par exemple ESPHome Web.
  5. Après l’écriture, redémarrer normalement la carte et vérifier l’affichage de l’OLED.

Si le chargement ne démarre pas, maintenir BOOT, appuyer sur RESET, puis relâcher BOOT. Après programmation, faire un RESET sans maintenir BOOT. Cette séquence, décrite par Waveshare, sert au chargement USB ; elle n’efface pas l’appairage Zigbee.

Utiliser un câble de données et fermer les logiciels qui occupent déjà le port série. Le programme fourni ne contient pas de mise à jour sans fil : ses modifications nécessitent un nouvel accès USB.

Appairer la télécommande

Pour une carte neuve ou sans réseau Zigbee mémorisé :

  1. Ouvrir temporairement l’autorisation d’appairage dans Zigbee2MQTT.
  2. Alimenter ou redémarrer la télécommande, à proximité du coordinateur.
  3. Attendre sa détection et la fin de l’interrogation. Le modèle déclaré est Telecommande Chambre H2.
  4. Vérifier la présence des huit commandes et donner un nom reconnaissable à l’appareil.
  5. Fermer l’autorisation d’appairage.

Aucun des cinq boutons n’est affecté à une remise à zéro Zigbee dans ce programme. Un appui long sur le bouton central ou un débranchement ne supprime donc pas un ancien réseau mémorisé.

Pour transférer une carte déjà appairée vers un autre réseau, il faudra une procédure de retrait et de réinitialisation adaptée à cet appareil. L’effacement complet de la mémoire n’est pas une étape normale de ce tutoriel. Après modification des noms ou des composants exposés, consulter les consignes de reconfiguration d’ESPHome.

Retrouver les huit commandes

Chaque commande possède un numéro de point de terminaison Zigbee, appelé endpoint. Les boutons gauche et droite ne sont pas exposés : ils changent seulement la sélection locale.

  • 1 : Nom dans le programme : Jardin Montee ; Appui à effectuer : Jardin, puis Haut ; Action Home Assistant : cover.open_cover
  • 2 : Nom dans le programme : Jardin Stop ; Appui à effectuer : Jardin, puis Centre ; Action Home Assistant : cover.stop_cover
  • 3 : Nom dans le programme : Jardin Descente ; Appui à effectuer : Jardin, puis Bas ; Action Home Assistant : cover.close_cover
  • 4 : Nom dans le programme : Veranda Montee ; Appui à effectuer : Véranda, puis Haut ; Action Home Assistant : cover.open_cover
  • 5 : Nom dans le programme : Veranda Stop ; Appui à effectuer : Véranda, puis Centre ; Action Home Assistant : cover.stop_cover
  • 6 : Nom dans le programme : Veranda Descente ; Appui à effectuer : Véranda, puis Bas ; Action Home Assistant : cover.close_cover
  • 7 : Nom dans le programme : Lampe ON ; Appui à effectuer : Lampe, puis Haut ; Action Home Assistant : light.turn_on
  • 8 : Nom dans le programme : Lampe OFF ; Appui à effectuer : Lampe, puis Bas ; Action Home Assistant : light.turn_off

Avant de créer les automatisations, observer les changements dans Zigbee2MQTT en effectuant les huit appuis. Attendre environ une seconde entre deux commandes : chacune doit passer à ON, puis revenir à OFF.

Dans Home Assistant, ouvrir l’appareil correspondant dans l’intégration MQTT et relever les identifiants réels des huit entités. Un appui permet de confirmer chaque correspondance. Les états bruts d’un capteur binaire sont on et off.

Créer les automatisations

Les huit exemples de l’annexe sont destinés à l’éditeur YAML d’une nouvelle automatisation, un exemple à la fois. Leurs identifiants sont volontairement génériques : ce ne sont pas des exports de mon installation.

Dans chaque exemple, remplacer :

  • binary_sensor.a_remplacer_commande_1 à binary_sensor.a_remplacer_commande_8 par les commandes réellement identifiées ;
  • cover.a_remplacer_volet_jardin et cover.a_remplacer_volet_veranda par vos volets ;
  • light.a_remplacer_lampe par votre lampe.

Dans Paramètres → Automatisations et scènes, créer une automatisation vide, passer en mode YAML, coller un seul exemple, adapter les identifiants et enregistrer. Répéter pour les autres commandes.

Voici l’exemple de la lampe :

# Exemple pour une seule automatisation dans l’éditeur YAML Home Assistant.
# Remplacer les identifiants a_remplacer avant utilisation.
alias: "Télécommande H2 - Lampe ON"
description: "Endpoint 7. Remplacer les identifiants avant activation."
triggers:
  - trigger: state
    entity_id: binary_sensor.a_remplacer_commande_7
    from: "off"
    to: "on"
conditions: []
actions:
  - action: light.turn_on
    target:
      entity_id: light.a_remplacer_lampe
mode: single

Le déclenchement exige off → on. Cela évite une commande provoquée par un simple retour depuis unknown ou unavailable. En contrepartie, le premier appui après une indisponibilité peut être ignoré si OFF n’a pas encore été reçu ; attendre le retour à OFF puis réessayer. La documentation des déclencheurs Home Assistant explique ces transitions.

Le fichier groupé home-assistant-automatisations.yaml est une alternative pour ceux qui gèrent manuellement automations.yaml : sauvegarder puis fusionner les blocs nécessaires dans le fichier existant. Choisir une seule méthode, afin de ne pas installer les mêmes actions en doublon.

Tester le montage

Commencer sans automatisation active : vérifier la sélection à l’écran et les huit impulsions dans Zigbee2MQTT. Activer ensuite seulement les deux automatisations de la lampe, puis essayer l’allumage et l’extinction depuis les boutons.

Tester enfin un volet à la fois, en vérifiant la cible, en dégageant sa zone de mouvement et en gardant une commande d’arrêt accessible. Essayer montée, arrêt et descente, puis passer au second volet.

Après un redémarrage de la télécommande, vérifier qu’elle retrouve son réseau et qu’une nouvelle commande est reçue. La sélection revient à Jardin : elle n’est pas mémorisée dans ce programme.

Le bouton « Exécuter les actions » de Home Assistant vérifie la partie action, mais pas la réception du bouton Zigbee. Pour tester toute la chaîne, utiliser la télécommande physique et consulter la trace de l’automatisation.

Si quelque chose ne répond pas

  • Écran noir : Alimentation 3,3 V, masse, SDA/SCL, adresse I²C et modèle SH1106
  • Image décalée : Modèle d’écran et résolution 128 × 64
  • Bouton inactif : Continuité, paire de contacts et GPIO correspondant
  • Gauche et droite inversés : Repères vus de face, et non côté soudures
  • Écran réactif, aucun message Zigbee : Appairage, journaux et états reçus dans Zigbee2MQTT
  • Appareil en ligne, équipement sans réaction : Identifiants des entités, automatisations actives et traces
  • Commande exécutée deux fois : Automatisations en doublon ou déclenchement sur les deux transitions
  • Premier appui ignoré après reconnexion : Attendre le retour de la commande à OFF
  • Port USB absent : Câble de données, port disponible, séquence BOOT/RESET

Avec ces impulsions, laisser le temps au retour à OFF entre deux appuis. L’affichage OLED reste local, même lorsque le réseau ou Home Assistant est indisponible.

Et une version sur batterie ?

L’USB simplifie cette première réalisation et permet de garder l’écran disponible en permanence. Une version sur batterie est possible, avec une gestion du sommeil et de l’écran, mais il ne suffit pas de remplacer le câble par une cellule lithium.

Il faudrait ajouter une batterie adaptée, un circuit de charge et de protection, une alimentation régulée et une gestion de l’extinction de l’écran. La cellule ne doit pas être branchée directement sur le 3V3. Il faudrait aussi adapter le programme et le réveil des boutons.

Le sommeil profond est une piste, mais le câblage actuel n’a pas été conçu pour ce mode : le choix des broches et du circuit de réveil doit être revu selon les possibilités de l’ESP32-H2. Le réveil entraîne un redémarrage et il faut attendre le rétablissement de la communication Zigbee avant la commande ; l’exemple Espressif de terminal dormant fournit une base d’étude.

❗
Ces composants supplémentaires et l’adaptation de l’alimentation augmenteraient le coût par rapport à la version USB. L’autonomie devra être mesurée sur le montage complet. Le code partagé ici reste celui de la version USB, sans sommeil profond.

Conclusion : fabriquer, apprendre et partager

Cette télécommande est ma première réalisation de ce type. Elle réunit ce que j’aime dans la domotique : fabriquer un objet, lui donner une fonction utile et le faire évoluer au fil des essais.

Comme pour mes autres projets, je me suis appuyé sur ChatGPT pour m’accompagner dans la conception, le câblage et la programmation. Je garde le plaisir de réaliser le montage de mes mains, de l’essayer et d’apprendre en chemin. Partager cette expérience sur HACF est une manière de transmettre à mon tour les idées et les solutions trouvées.

Vous pouvez reprendre le principe avec vos propres volets, lampes ou autres équipements, en adaptant le programme et les automatisations. Les annexes ci-dessous rassemblent le code nécessaire pour reproduire cette première version.

Annexe : programme ESPHome complet

Copier le bloc entier dans une configuration ESPHome vide. Le fichier correspondant est telecommande-chambre-h2.yaml dans le dossier de partage. Le brochage est celui du schéma de cet article.

substitutions:
  # Broches de l'ESP32-H2-Zero Waveshare
  pin_oled_sda: GPIO10
  pin_oled_scl: GPIO11
  pin_bouton_gauche: GPIO3
  pin_bouton_droite: GPIO1
  pin_bouton_haut: GPIO5
  pin_bouton_centre: GPIO2
  pin_bouton_bas: GPIO4

esphome:
  name: telecommande-chambre-h2
  friendly_name: Telecommande Chambre H2
  # Le Zigbee natif ESP32-H2 est disponible a partir d'ESPHome 2026.5.
  min_version: 2026.5.0

  # Evite que les commandes Zigbee soient dans un etat inconnu au demarrage.
  on_boot:
    priority: -100
    then:
      - binary_sensor.template.publish:
          id: jardin_montee
          state: OFF
      - binary_sensor.template.publish:
          id: jardin_stop
          state: OFF
      - binary_sensor.template.publish:
          id: jardin_descente
          state: OFF
      - binary_sensor.template.publish:
          id: veranda_montee
          state: OFF
      - binary_sensor.template.publish:
          id: veranda_stop
          state: OFF
      - binary_sensor.template.publish:
          id: veranda_descente
          state: OFF
      - binary_sensor.template.publish:
          id: lampe_on
          state: OFF
      - binary_sensor.template.publish:
          id: lampe_off
          state: OFF

esp32:
  variant: esp32h2
  framework:
    type: esp-idf

logger:
  level: DEBUG

# Appareil Zigbee terminal, alimente par l'USB-C.
zigbee:
  id: zigbee_h2
  model: "Telecommande Chambre H2"
  router: false
  power_source: DC_SOURCE
  on_join:
    then:
      - logger.log:
          format: "%s au reseau Zigbee"
          args: ['x ? "Connexion" : "Reconnexion"']

# 0 = Jardin, 1 = Veranda, 2 = Lampe
globals:
  - id: selection
    type: int
    restore_value: false
    initial_value: '0'

  # Code affiche pendant 1,5 seconde sur l'ecran.
  - id: message_code
    type: int
    restore_value: false
    initial_value: '0'

font:
  - file: "gfonts://Roboto"
    id: font_petit
    size: 8

  - file: "gfonts://Roboto"
    id: font_status
    size: 10

# OLED SH1106 1,3 pouce, 128 x 64, adresse habituelle 0x3C.
i2c:
  id: bus_oled
  sda: ${pin_oled_sda}
  scl: ${pin_oled_scl}
  scan: true
  frequency: 100kHz

display:
  - platform: ssd1306_i2c
    id: oled
    i2c_id: bus_oled
    model: "SH1106 128x64"
    address: 0x3C
    update_interval: 150ms
    lambda: |-
      // Titres
      it.print(21, 0, id(font_petit), TextAlign::TOP_CENTER, "JARDIN");
      it.print(64, 0, id(font_petit), TextAlign::TOP_CENTER, "VERANDA");
      it.print(107, 0, id(font_petit), TextAlign::TOP_CENTER, "LAMPE");

      // Curseur de selection
      const int curseur_x[] = {3, 46, 89};
      it.filled_rectangle(curseur_x[id(selection)], 11, 36, 2);

      // Dessin des deux volets
      auto dessiner_volet = [&](int x) {
        it.rectangle(x, 17, 22, 25);
        for (int y = 22; y <= 38; y += 4) {
          it.line(x + 2, y, x + 19, y);
        }
      };
      dessiner_volet(7);
      dessiner_volet(50);

      auto fleche_haut = [&](int x) {
        it.line(x, 38, x, 21);
        it.line(x, 21, x - 4, 25);
        it.line(x, 21, x + 4, 25);
      };

      auto fleche_bas = [&](int x) {
        it.line(x, 21, x, 38);
        it.line(x, 38, x - 4, 34);
        it.line(x, 38, x + 4, 34);
      };

      // Action en cours sur les volets
      if (id(message_code) == 1) fleche_haut(35);
      if (id(message_code) == 2) it.filled_rectangle(32, 27, 7, 7);
      if (id(message_code) == 3) fleche_bas(35);
      if (id(message_code) == 4) fleche_haut(78);
      if (id(message_code) == 5) it.filled_rectangle(75, 27, 7, 7);
      if (id(message_code) == 6) fleche_bas(78);

      // Lampe
      if (id(message_code) == 7) {
        it.filled_circle(107, 27, 7);
        it.filled_rectangle(103, 34, 8, 2);
        it.filled_rectangle(104, 37, 6, 2);
        it.line(107, 15, 107, 19);
        it.line(96, 19, 100, 22);
        it.line(118, 19, 114, 22);
        it.line(94, 27, 99, 27);
        it.line(120, 27, 115, 27);
        it.line(97, 36, 101, 33);
        it.line(117, 36, 113, 33);
      } else {
        it.circle(107, 27, 7);
        it.line(103, 34, 111, 34);
        it.line(104, 37, 110, 37);
        it.line(105, 39, 109, 39);

        // Quelques rayons seulement pendant l'affichage normal.
        if (id(message_code) != 8) {
          it.line(107, 16, 107, 19);
          it.line(97, 20, 100, 22);
          it.line(117, 20, 114, 22);
        }
      }

      it.line(0, 46, 127, 46);

      const char *texte = "";
      switch (id(message_code)) {
        case 1: texte = "JARDIN MONTEE"; break;
        case 2: texte = "JARDIN STOP"; break;
        case 3: texte = "JARDIN DESCENTE"; break;
        case 4: texte = "VERANDA MONTEE"; break;
        case 5: texte = "VERANDA STOP"; break;
        case 6: texte = "VERANDA DESCENTE"; break;
        case 7: texte = "LAMPE ON"; break;
        case 8: texte = "LAMPE OFF"; break;
        default:
          if (id(selection) == 0) texte = "VOLET JARDIN";
          else if (id(selection) == 1) texte = "VOLET VERANDA";
          else texte = "LAMPE";
          break;
      }
      it.print(64, 50, id(font_status), TextAlign::TOP_CENTER, texte);

binary_sensor:
  # Huit impulsions exposees sur Zigbee. Les endpoints fixes facilitent
  # l'identification dans ZHA ou Zigbee2MQTT.
  - platform: template
    id: jardin_montee
    name: "Jardin Montee"
    endpoint: 1
    report: force

  - platform: template
    id: jardin_stop
    name: "Jardin Stop"
    endpoint: 2
    report: force

  - platform: template
    id: jardin_descente
    name: "Jardin Descente"
    endpoint: 3
    report: force

  - platform: template
    id: veranda_montee
    name: "Veranda Montee"
    endpoint: 4
    report: force

  - platform: template
    id: veranda_stop
    name: "Veranda Stop"
    endpoint: 5
    report: force

  - platform: template
    id: veranda_descente
    name: "Veranda Descente"
    endpoint: 6
    report: force

  - platform: template
    id: lampe_on
    name: "Lampe ON"
    endpoint: 7
    report: force

  - platform: template
    id: lampe_off
    name: "Lampe OFF"
    endpoint: 8
    report: force

  # Chaque bouton relie son GPIO a GND. Les resistances de tirage internes
  # sont activees : aucune resistance externe n'est necessaire.
  - platform: gpio
    id: bouton_gauche
    internal: true
    pin:
      number: ${pin_bouton_gauche}
      mode:
        input: true
        pullup: true
      inverted: true
    filters:
      - delayed_on_off: 20ms
    on_press:
      - lambda: |-
          id(selection) = (id(selection) + 2) % 3;
          if (id(selection) == 0) ESP_LOGI("telecommande", "SELECTION = JARDIN");
          else if (id(selection) == 1) ESP_LOGI("telecommande", "SELECTION = VERANDA");
          else ESP_LOGI("telecommande", "SELECTION = LAMPE");

  - platform: gpio
    id: bouton_droite
    internal: true
    pin:
      number: ${pin_bouton_droite}
      mode:
        input: true
        pullup: true
      inverted: true
    filters:
      - delayed_on_off: 20ms
    on_press:
      - lambda: |-
          id(selection) = (id(selection) + 1) % 3;
          if (id(selection) == 0) ESP_LOGI("telecommande", "SELECTION = JARDIN");
          else if (id(selection) == 1) ESP_LOGI("telecommande", "SELECTION = VERANDA");
          else ESP_LOGI("telecommande", "SELECTION = LAMPE");

  - platform: gpio
    id: bouton_haut
    internal: true
    pin:
      number: ${pin_bouton_haut}
      mode:
        input: true
        pullup: true
      inverted: true
    filters:
      - delayed_on_off: 20ms
    on_press:
      - lambda: |-
          int code = 0;
          if (id(selection) == 0) {
            code = 1;
            ESP_LOGI("telecommande", "JARDIN MONTEE");
          } else if (id(selection) == 1) {
            code = 4;
            ESP_LOGI("telecommande", "VERANDA MONTEE");
          } else {
            code = 7;
            ESP_LOGI("telecommande", "LAMPE ON");
          }
          id(envoyer_action)->execute(code);

  - platform: gpio
    id: bouton_centre
    internal: true
    pin:
      number: ${pin_bouton_centre}
      mode:
        input: true
        pullup: true
      inverted: true
    filters:
      - delayed_on_off: 20ms
    on_press:
      - lambda: |-
          if (id(selection) == 0) {
            ESP_LOGI("telecommande", "JARDIN STOP");
            id(envoyer_action)->execute(2);
          } else if (id(selection) == 1) {
            ESP_LOGI("telecommande", "VERANDA STOP");
            id(envoyer_action)->execute(5);
          } else {
            ESP_LOGI("telecommande", "LAMPE : CENTRE = AUCUNE ACTION");
          }

  - platform: gpio
    id: bouton_bas
    internal: true
    pin:
      number: ${pin_bouton_bas}
      mode:
        input: true
        pullup: true
      inverted: true
    filters:
      - delayed_on_off: 20ms
    on_press:
      - lambda: |-
          int code = 0;
          if (id(selection) == 0) {
            code = 3;
            ESP_LOGI("telecommande", "JARDIN DESCENTE");
          } else if (id(selection) == 1) {
            code = 6;
            ESP_LOGI("telecommande", "VERANDA DESCENTE");
          } else {
            code = 8;
            ESP_LOGI("telecommande", "LAMPE OFF");
          }
          id(envoyer_action)->execute(code);

script:
  # Le parametre conserve le bon code meme si plusieurs appuis sont rapproches.
  - id: envoyer_action
    mode: queued
    max_runs: 10
    parameters:
      code: int
    then:
      - lambda: |-
          id(message_code) = code;
          id(effacer_message).execute();

          switch (code) {
            case 1: id(jardin_montee).publish_state(true); break;
            case 2: id(jardin_stop).publish_state(true); break;
            case 3: id(jardin_descente).publish_state(true); break;
            case 4: id(veranda_montee).publish_state(true); break;
            case 5: id(veranda_stop).publish_state(true); break;
            case 6: id(veranda_descente).publish_state(true); break;
            case 7: id(lampe_on).publish_state(true); break;
            case 8: id(lampe_off).publish_state(true); break;
          }

      - delay: 500ms

      - lambda: |-
          switch (code) {
            case 1: id(jardin_montee).publish_state(false); break;
            case 2: id(jardin_stop).publish_state(false); break;
            case 3: id(jardin_descente).publish_state(false); break;
            case 4: id(veranda_montee).publish_state(false); break;
            case 5: id(veranda_stop).publish_state(false); break;
            case 6: id(veranda_descente).publish_state(false); break;
            case 7: id(lampe_on).publish_state(false); break;
            case 8: id(lampe_off).publish_state(false); break;
          }

  - id: effacer_message
    mode: restart
    then:
      - delay: 1500ms
      - lambda: |-
          id(message_code) = 0;

Annexe : les huit automatisations

Chaque bloc ci-dessous se colle séparément dans l’éditeur YAML d’une nouvelle automatisation. Remplacer tous les identifiants contenant a_remplacer avant utilisation. Le même ensemble est fourni sous forme de fichiers individuels dans le dossier automatisations/.

Jardin montée

# Exemple pour une seule automatisation dans l’éditeur YAML Home Assistant.
# Remplacer les identifiants a_remplacer avant utilisation.
alias: "Télécommande H2 - Jardin montée"
description: "Endpoint 1. Remplacer les identifiants avant activation."
triggers:
  - trigger: state
    entity_id: binary_sensor.a_remplacer_commande_1
    from: "off"
    to: "on"
conditions: []
actions:
  - action: cover.open_cover
    target:
      entity_id: cover.a_remplacer_volet_jardin
mode: single

Jardin stop

# Exemple pour une seule automatisation dans l’éditeur YAML Home Assistant.
# Remplacer les identifiants a_remplacer avant utilisation.
alias: "Télécommande H2 - Jardin stop"
description: "Endpoint 2. Remplacer les identifiants avant activation."
triggers:
  - trigger: state
    entity_id: binary_sensor.a_remplacer_commande_2
    from: "off"
    to: "on"
conditions: []
actions:
  - action: cover.stop_cover
    target:
      entity_id: cover.a_remplacer_volet_jardin
mode: single

Jardin descente

# Exemple pour une seule automatisation dans l’éditeur YAML Home Assistant.
# Remplacer les identifiants a_remplacer avant utilisation.
alias: "Télécommande H2 - Jardin descente"
description: "Endpoint 3. Remplacer les identifiants avant activation."
triggers:
  - trigger: state
    entity_id: binary_sensor.a_remplacer_commande_3
    from: "off"
    to: "on"
conditions: []
actions:
  - action: cover.close_cover
    target:
      entity_id: cover.a_remplacer_volet_jardin
mode: single

Véranda montée

# Exemple pour une seule automatisation dans l’éditeur YAML Home Assistant.
# Remplacer les identifiants a_remplacer avant utilisation.
alias: "Télécommande H2 - Véranda montée"
description: "Endpoint 4. Remplacer les identifiants avant activation."
triggers:
  - trigger: state
    entity_id: binary_sensor.a_remplacer_commande_4
    from: "off"
    to: "on"
conditions: []
actions:
  - action: cover.open_cover
    target:
      entity_id: cover.a_remplacer_volet_veranda
mode: single

Véranda stop

# Exemple pour une seule automatisation dans l’éditeur YAML Home Assistant.
# Remplacer les identifiants a_remplacer avant utilisation.
alias: "Télécommande H2 - Véranda stop"
description: "Endpoint 5. Remplacer les identifiants avant activation."
triggers:
  - trigger: state
    entity_id: binary_sensor.a_remplacer_commande_5
    from: "off"
    to: "on"
conditions: []
actions:
  - action: cover.stop_cover
    target:
      entity_id: cover.a_remplacer_volet_veranda
mode: single

Véranda descente

# Exemple pour une seule automatisation dans l’éditeur YAML Home Assistant.
# Remplacer les identifiants a_remplacer avant utilisation.
alias: "Télécommande H2 - Véranda descente"
description: "Endpoint 6. Remplacer les identifiants avant activation."
triggers:
  - trigger: state
    entity_id: binary_sensor.a_remplacer_commande_6
    from: "off"
    to: "on"
conditions: []
actions:
  - action: cover.close_cover
    target:
      entity_id: cover.a_remplacer_volet_veranda
mode: single

Lampe ON

# Exemple pour une seule automatisation dans l’éditeur YAML Home Assistant.
# Remplacer les identifiants a_remplacer avant utilisation.
alias: "Télécommande H2 - Lampe ON"
description: "Endpoint 7. Remplacer les identifiants avant activation."
triggers:
  - trigger: state
    entity_id: binary_sensor.a_remplacer_commande_7
    from: "off"
    to: "on"
conditions: []
actions:
  - action: light.turn_on
    target:
      entity_id: light.a_remplacer_lampe
mode: single

Lampe OFF

# Exemple pour une seule automatisation dans l’éditeur YAML Home Assistant.
# Remplacer les identifiants a_remplacer avant utilisation.
alias: "Télécommande H2 - Lampe OFF"
description: "Endpoint 8. Remplacer les identifiants avant activation."
triggers:
  - trigger: state
    entity_id: binary_sensor.a_remplacer_commande_8
    from: "off"
    to: "on"
conditions: []
actions:
  - action: light.turn_off
    target:
      entity_id: light.a_remplacer_lampe
mode: single

Fichiers et références

Les photos et le programme ESPHome correspondent à ma réalisation. Les automatisations sont des exemples à personnaliser. La batterie reste une évolution à concevoir et à valider.

Le dossier de partage joint à cet article contient : programme ESPHome, huit automatisations individuelles, version groupée et schéma PNG/SVG. Le code essentiel est aussi reproduit ci-dessus pour que l’article reste utilisable sans téléchargement.

Bon bricolage !

N’hésitez pas si vous avez des questions !