Aller au contenu

Assistant vocal

Vue d'ensemble

Le Tab5 fonctionne comme un satellite vocal local pour Home Assistant. Il exécute la détection de wake-word entièrement sur l'appareil, stream l'audio uniquement à la demande, et donne un retour visuel en temps réel via une icône dans l'UI.

Le pipeline enchaîne : modèle de wake-word local → capture audio I2S → pipeline Voice Home Assistant (Wyoming STT → agent conversation → Wyoming TTS) → lecture DAC ES8388.

L'agent de conversation est celui de l'assistant vocal de Home Assistant utilisé : deux modes de l'écran, Domo et Discu, choisissent quel assistant répond (Sélection du mode assistant). N'importe quel agent de conversation de Home Assistant convient.


Détection du wake-word

L'appareil fait tourner micro_wake_word — l'intégration TensorFlow Lite Micro d'ESPHome. Le modèle (okay_nabu) tourne en continu en arrière-plan, traitant les frames audio venant du microphone I2S. Ça tourne sur l'appareil, dans la mémoire locale de l'ESP32-P4. Aucun audio n'est transmis sur le réseau pendant l'écoute passive.

Quand le modèle détecte la phrase d'activation avec une confiance suffisante, il déclenche un événement interne qu'ESPHome transmet au pipeline Voice de Home Assistant.

Coût ressources : le modèle micro_wake_word tourne à environ 5–8% CPU sur l'ESP32-P4 à 360 MHz, en continu. C'est acceptable, mais ça signifie que les ~92% restants doivent couvrir le rendu LVGL, la gestion I2S et le Wi-Fi. Sur ce matériel, c'est confortable. Sur un ESP32-S3, la marge serait plus serrée.

La détection du wake-word peut être basculée via un bouton UI (switch tab5_wake_word_active). Quand désactivé, l'appareil fonctionne uniquement en mode push-to-talk manuel.

Second wake word : « Stop » (volet roulant)

Un second modèle microWakeWord (Stop) est déclaré à côté de okay_nabu dans tab5-assist.yaml, mais il n'est armé (micro_wake_word.enable_model) que pendant que le volet est en mouvement (globale volet_en_mouvement, poussée par HA) et désarmé dès la fin du mouvement. Dire « Stop » pendant que le volet bouge déclenche le script d'arrêt directement sur l'appareil — pas de phrase d'activation, pas d'aller-retour pipeline, pas de latence réseau. Deux pièges déjà rencontrés sur ce setup : ESPHome n'active automatiquement que le premier modèle déclaré (d'où l'enable/disable explicite), et la chaîne wake_word détectée est "Stop" avec un S majuscule.

Interrompre une réponse en cours

Taper l'icône micro pendant que le TTS joue arrête le pipeline (voice_assistant.stop, que Home Assistant suit) et le haut-parleur, interrompt la session en cours et relance immédiatement l'écoute (tab5_vocal_interrupt_and_listen, tab5-scripts.yaml). C'est la réponse côté firmware au barge-in : pendant la phase de réponse du pipeline Assist, le micro appartient au pipeline et le wake word est inactif — un « stop » vocal ne peut pas marcher là, le tap si.


Capture audio

Paramètre Valeur
Taux d'échantillonnage 16 kHz
Profondeur de bits 16-bit
Canaux Mono
Interface I2S
GPIO DIN GPIO 28
GPIO BCLK GPIO 27
GPIO LRCLK GPIO 29

16 kHz / 16-bit mono est le format standard pour les pipelines de traitement vocal. Le STT basé sur Whisper de Home Assistant attend ce format.

Étage de gain — prendre le gain en analogique, pas en numérique. Tout le gain d'entrée est réglé sur le préampli ES7210 (mic_gain: 30dB, réglable de 0 à 37,5 dB), avant le convertisseur, et les deux gain_factor (wake word et assistant vocal) restent à 1. Le gain_factor d'ESPHome est une multiplication entière suivie d'un clamp dur (microphone_source.cpp) : il amplifie le bruit de fond du convertisseur exactement autant que la voix — donc aucun gain de rapport signal/bruit — et il écrête les crêtes en signaux carrés, ce que Whisper transcrit le plus mal. Déplacer 6 dB du numérique vers l'analogique le 05/08/2026 a conservé le même niveau de sortie et le même seuil d'écrêtage, mais a mis l'amplification là où elle apporte vraiment du signal. noise_suppression_level est à 0 pour la même raison : il est bien appliqué par HA (MicroVadSpeexEnhancer dans assist_pipeline/pipeline.py), mais un débruiteur déforme la parole et Whisper est entraîné sur de l'audio naturel. Le remonter à 1-2 seulement si la reconnaissance se dégrade dans une pièce bruyante.

Une fois le wake-word déclenché, l'audio est streamé en temps réel via la connexion Wi-Fi existante vers le pipeline Voice HA. Le pipeline gère le STT (Whisper), le traitement d'intention (agent Conversation), et le TTS (Piper ou cloud). Le résultat revient comme flux audio routé vers l'entité media_player du Tab5.


Lecture audio — ES8388

Les réponses TTS sont jouées via le DAC ES8388 → amplificateur → haut-parleur intégré. L'entité media player du Tab5 est un composant media player I2S ESPHome standard.

Séquence de boot : le switch d'activation de l'amplificateur est activé après le rétroéclairage et le réglage du volume du media_player (et c'est seulement ensuite que l'appareil attend la connexion API HA). Activer l'ampli avant que l'horloge I2S soit stable produit un pop audible. La séquence on_boot dans tab5-ha-hmi.yaml garantit cet ordre.

Poussées pendant le TTS : Home Assistant n'espace plus ses envois (les pauses d'une seconde entre les blocs ont été retirées le 01/10/2026, voir docs/architecture.md). Une poussée complète, c'est 8 envois groupés de quelques centaines d'octets ; hors reconnexion, elle part toutes les 10 minutes.

Format de lecture — c'est un réglage Home Assistant, pas un réglage de sortie. Le bloc announcement_pipeline du media_player déclare le format préféré annoncé à HA, c'est-à-dire ce vers quoi HA transcode le TTS avant de l'envoyer. Il doit donc coller au moteur TTS, pas au DAC :

Paramètre Valeur Pourquoi
Format FLAC Sans perte, ~deux fois moins d'octets sur le WiFi que le WAV brut. Défaut ESPHome, et ce qu'utilise le firmware officiel Home Assistant Voice PE
Taux d'échantillonnage 24000 Hz Le plus haut des deux moteurs désormais en service : « Domotique » reste sur Piper en local (fr_FR-upmc-medium, 22050 Hz), « Discussion LLM » est passé sur HA Cloud / AlainNeural (24000 Hz) le 05/08/2026. À 24000 le cloud est natif et Piper est simplement suréchantillonné, ce qui ne coupe aucun contenu ; demander 22050 ferait l'inverse et raboterait le cloud au-dessus de 11 kHz. Demander 16 kHz — le réglage d'origine — jetait silencieusement tout ce qui est au-dessus de 8 kHz, sifflantes comprises. À revoir à chaque changement de moteur TTS
Canaux Mono Un seul haut-parleur

Le haut-parleur I2S en mode primary accepte 16000 à 48000 Hz et reconfigure son horloge sur le flux qui arrive : le sample_rate: 48000 du composant speaker: n'est qu'un défaut, jamais une contrainte sur le pipeline.

Tampon de lecture : buffer_duration: 500ms sur le haut-parleur (le défaut ESPHome). Ne pas le baisser. Le TTS est streamé en HTTP depuis HA pendant sa génération, via le WiFi porté par le co-processeur C6 en SDIO, et quand ce tampon se vide la tâche du haut-parleur ne met pas le flux en pause — elle remplit le buffer DMA de silence. Chaque hoquet réseau devient un micro-trou audible.


États du pipeline et retour visuel

L'icône microphone dans l'UI change de couleur pour refléter l'état courant du pipeline :

État Couleur icône Signification
Veille — écoute wake word Gris Détection en arrière-plan
Wake word détecté Vert Capture vocale active
Traitement Orange Pipeline HA analyse la commande
Synthèse Bleu Audio TTS en lecture
Erreur / non compris Rouge Pipeline n'a retourné aucun résultat
Wake word désactivé Gris sombre Détection coupée

Les callbacks du composant assistant vocal ESPHome (on_listening, on_stt_end, on_tts_start, on_end, on_error) sont déclarés dans tab5-assist.yaml ; chacun appelle assist_set_pipeline_state() (tab5_assist.cpp), qui règle l'icône, sa couleur et le libellé d'état depuis une seule table — anciennement, chaque callback réglait directement la couleur via lv_obj_set_style_text_color avec un token UIColor::. Il n'y a pas de variable d'état séparée — la couleur de l'icône est l'indicateur d'état.


Sélection du mode assistant

Deux boutons, sur l'accueil et dans le popup assistant, choisissent l'assistant vocal de Home Assistant qui répond :

  1. Domo (icône Home Assistant) — l'assistant préféré de Home Assistant. Pour commander la maison, son agent de conversation est « Home Assistant » (appareils, capteurs, automatisations).
  2. Discu (icône robot) — l'assistant choisi dans « Tab5 · pipeline de discussion », avec n'importe quel agent de conversation : un modèle de langage en ligne, un Ollama local, une autre intégration. Dans ce mode, chaque demande ouvre le popup assistant.

Les régler dans Home Assistant : Réglages de la tablette → Assistant vocal : les deux modes.

Le Discu de l'auteur passe par son propre moteur, vromvrom-engine, pour trois raisons : - un raccourci domotique rapide : les commandes courtes de la maison sont reconnues et exécutées directement, sans passer par un LLM ; - un LLM avec des agents dédiés selon la demande (spécialistes) ; - le remplacement du LLM choisi s'il n'est pas disponible, ou par exemple pour une meilleure intelligence, en local ou dans le cloud.

Ce moteur reste un gros brouillon, que l'auteur gère uniquement par l'IA : ce n'est pas un prérequis, ni quelque chose qu'il peut conseiller. N'importe quel agent de conversation de Home Assistant convient.

Fonctionnement : le mode est stocké dans la globale booléenne conversation_mode (gardée d'un démarrage à l'autre) ; les deux paires de boutons le montrent par une bordure colorée. Côté HA, le select de pipeline de la tablette (« Assistant », select.m5stack_tab5_home_assistant_hmi_assistant avec le nom d'appareil par défaut) porte le pipeline utilisé. La tablette ne le règle pas elle-même : elle envoie un événement esphome.tab5_mode_assistant (option preferred ou Discussion LLM) à chaque appui, et à chaque démarrage une fois Home Assistant connecté, et packages/tab5_evenements.yaml choisit dans le select de cette tablette le pipeline préféré, ou pour Discussion celui de « Tab5 · pipeline de discussion » (jusqu'à la 3.2, un pipeline nommé exactement « Discussion LLM »), seulement s'il existe (ADR-0025). C'est pourquoi un changement d'« Assistant » à la main ne tient pas. « Aucun » dans la liste choisit le pipeline préféré, et l'automatisation des emplacements signale alors la zone « discussion » absente : les boutons sont masqués et la tablette repasse en Domo (tab5-zones.yaml). Le même package arrête le lecteur de la tablette quand une réponse est interrompue (esphome.tab5_voix_stop) et prononce les annonces (esphome.tab5_annonce : rendez-vous, « Volet arrêté »). Aucune option « autoriser les actions Home Assistant » n'est nécessaire.

→ Contexte plus large : related_projects.md