Projet d'un débutant : Voice Control korvo / afficheur lcd-tactile

Hello @MattG

Si tu as toujours le Korvo v1.1 sous le coude, la bonne nouvelle c’est que la sortie audio fonctionne enfin ( mode speaker mais pas media_player, c’est mieux que rien).
Vu que c’est un ESP32 Wrover , le wakeword ondevice est aussi possible.

@+

Hello,

J’utilise aussi un Korvo v1.1

Je me demandais quel était l’avantage d’utiliser la platform ESP-IDF au lieu d’Arduino si Arduino permet de faire Media Player ? Est ce que IDF fonctionne mieux ?

Et enfin, comment fais tu pour utiliser le wakeword on device dessus ? C’est Micro Wake Word ?

Merci !

Hello

Oui , il s’agit bien du micro wakeword.

Le pb avec ce device, c’est que ce soit avec le framework Arduino, idf ou adf, jusqu’à présent on n’arrivait pas à lui faire sortir un son via son ampli ou le jack 3.5.
Un développement spécifique pour cette carte d’un component est maintenant disponible mais que pour du speaker , pas du media player.

Je finalise quelques tests d’ici fin de semaine et publierai le code yaml ici

@+

Salut Krull56,

Merci beaucoup pour le retour.

J’avais un peu laissé tomber vus les soucis mais ça me donne envie de m’y remettre ! Reste le temps mais on va trouver !

A+,

Matt.

1 « J'aime »

Hello @MattG

Tu peux ressortir ton korvo V1 du placard :grin:

Désolé pour le retard mais j’attendais la sortie de la nouvelle version d’ESPhome intégrant la V2 des microwakeword.
Dispo depuis ce matin donc je me suis attelé tout de suite à la tache :crazy_face:

Voilà le code de mon proto , fait par agrégation de différentes sources ( je n’invente rien)

Au menu :

  • Du son ! Les sorties audio de la korvo sont enfin fonctionnelles , la gestion du volume par les 2 boutons ou directement dans HA est un peu capricieuse mais une fois réglé ça fonctionne correctement même si ce n’est pas très puissant. Les 4 autres boutons sont dispos pour y integrer d’autres fonctions
  • Microwakeword ( très très réactif) , les 3 sont implémentés dans le code ( Okay Nabu, Hey Jarvis, Alexa)
  • Ajout d’un selecteur pour choisir Microwakeword ou Wakeword défini du pipeline

En attente:

  • Media_player
  • Toujours obligé de faire un petit reset ( bouton latéral) après première mise sous tension
  • utilisation du paramètre VAD du MicroWakeword

A toi de jouer et de nous partager d’éventuelles améliorations

@+

substitutions:
  name: korvosat2
  friendly_name: korvosat2
  voice_assist_idle_phase_id: "1"
  voice_assist_listening_phase_id: "2"
  voice_assist_thinking_phase_id: "3"
  voice_assist_replying_phase_id: "4"
  voice_assist_not_ready_phase_id: "10"
  voice_assist_error_phase_id: "11"
  voice_assist_muted_phase_id: "12"

  
esphome:
  name: ${name}
  friendly_name: ${friendly_name}
  platformio_options:
    board_build.flash_mode: dio
  on_boot:
    - priority: -100
      then:
        - light.turn_on:
            id: led_ring
            blue: 0%
            red: 100%
            green: 0%
            effect: Fast Pulse
        - delay: 1s
        - wait_until:
            condition:
              wifi.connected:
        - light.turn_on:
            id: led_ring
            blue: 0%
            red: 100%
            green: 0%
            effect: Slow Pulse
        - wait_until: api.connected
        - light.turn_on:
            id: led_ring
            blue: 100%
            red: 0%
            green: 100%
            effect: Pulse
        - delay: 1s
        - if:
            condition:
              lambda: return id(init_in_progress);
            then:
              - lambda: id(init_in_progress) = false;
              - script.execute: reset_led
esp32:
  board: esp-wrover-kit
  framework:
    type: esp-idf
    version: recommended
    sdkconfig_options:
      CONFIG_IDF_TARGET_ESP32: y
      CONFIG_ESPTOOLPY_FLASHMODE_QIO: y
      CONFIG_ESPTOOLPY_FLASHFREQ_80M: y
      CONFIG_ESPTOOLPY_FLASHSIZE_16MB: y
      CONFIG_ESP32S3_DATA_CACHE_64KB: y
      CONFIG_ESP32S3_DATA_CACHE_LINE_64B: y
      CONFIG_PARTITION_TABLE_CUSTOM: y
      CONFIG_PARTITION_TABLE_CUSTOM_FILENAME: "default_16MB.csv" 
      CONFIG_PARTITION_TABLE_FILENAME: "default_16MB.csv" 
      CONFIG_PARTITION_TABLE_OFFSET: "0x8000"
      CONFIG_ESP32_DEFAULT_CPU_FREQ_240: y
      CONFIG_ESP32_SPIRAM_SUPPORT: y
      CONFIG_SPIRAM_SPEED_80M: y
      CONFIG_ESP_SYSTEM_PANIC_SILENT_REBOOT: y
      CONFIG_I2S_ENABLE_DEBUG_LOG: y
      CONFIG_AUDIO_BOARD_CUSTOM: y
      
    components:
      - name: esp32_korvo1_board 
        source: github://dwitgen/korvo_1@main
        refresh: 0s

external_components:
  - source: github://dwitgen/esphome@main
    components: [esp_adf]
    refresh: 0s


# Enable logging
logger:

# Enable Home Assistant API
api:
  encryption:
    key: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
  services:
    - service: volume_up
      then:
        - lambda: |-
            if (id(speaker_volume) < 100) {
              id(external_speaker).volume_up();
            }

    - service: volume_down
      then:
        - lambda: |-
            if (id(speaker_volume) > 0) {
              id(external_speaker).volume_down();
            }

ota:
  - platform: esphome
    password: "xxxxxxxxxxxx"

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "Korvosat Fallback Hotspot"
    password: "yyyyyyyyy"

captive_portal:

output:
  - platform: gpio
    id: pa_ctrl
    pin:
      number: GPIO12
      ignore_strapping_warning: true

esp_adf:
  board: esp32korvo1 


speaker:
  - platform: esp_adf
    id: external_speaker
    
microphone:
  - platform: esp_adf
    id: external_mic


micro_wake_word:
  vad:
  models:
    - model: okay_nabu
    - model: hey_jarvis
    - model: alexa
  on_wake_word_detected:
    then:
      - voice_assistant.start:
          wake_word: !lambda return wake_word;

voice_assistant:
  id: voice_asst
  microphone: external_mic
  speaker: external_speaker
  noise_suppression_level: 2
  auto_gain: 31dBFS
  volume_multiplier: 6

  on_listening:
    - lambda: id(voice_assistant_phase) = ${voice_assist_listening_phase_id};
    - script.execute: reset_led
  on_stt_vad_end:
    - lambda: id(voice_assistant_phase) = ${voice_assist_thinking_phase_id};
    - script.execute: reset_led
  on_tts_start:
    - light.turn_on:
        id: led_ring
        blue: 0%
        red: 100%
        green: 100%
        brightness: 60%
        effect: Working
  on_stt_end: 
    - homeassistant.service:
        service: media_player.play_media
        data:
          entity_id: media_player.mpd
          media_content_id: !lambda return x;
          media_content_type: music
          announce: "true"

  on_tts_stream_start:
    - output.turn_on: pa_ctrl
    - delay: 100ms
    - lambda: id(voice_assistant_phase) = ${voice_assist_replying_phase_id};
    - script.execute: reset_led

  on_end:
    - delay: 100ms
    - wait_until:
        not:
          speaker.is_playing:
    - lambda: id(voice_assistant_phase) = ${voice_assist_idle_phase_id};
    - script.execute: reset_led
    - if:
        condition:
          and:
            - switch.is_off: mute
            - lambda: return id(wake_word_engine_location).state == "On device";
        then:
          - wait_until:
              not:
                voice_assistant.is_running:
          - micro_wake_word.start:

  on_error:
    - if:
        condition:
          lambda: return !id(init_in_progress);
        then:
          - lambda: id(voice_assistant_phase) = ${voice_assist_error_phase_id};
          - script.execute: reset_led
          - delay: 2s
          - if:
              condition:
                switch.is_off: mute
              then:
                - lambda: id(voice_assistant_phase) = ${voice_assist_idle_phase_id};
              else:
                - lambda: id(voice_assistant_phase) = ${voice_assist_muted_phase_id};
          - script.execute: reset_led

  on_client_connected:
    - if:
        condition:
          switch.is_off: mute
        then:
          - if:
              condition:
                lambda: return id(wake_word_engine_location).state == "In Home Assistant";
              then:
                - lambda: id(voice_asst).set_use_wake_word(true);
                - voice_assistant.start_continuous:
          - if:
              condition:
                lambda: return id(wake_word_engine_location).state == "On device";
              then:
                - micro_wake_word.start
          - lambda: id(voice_assistant_phase) = ${voice_assist_idle_phase_id};
        else:
          - lambda: id(voice_assistant_phase) = ${voice_assist_muted_phase_id};
    - lambda: id(init_in_progress) = false;
    - script.execute: reset_led

  on_client_disconnected:
    - if:
        condition:
          lambda: return id(wake_word_engine_location).state == "In Home Assistant";
        then:
          - lambda: id(voice_asst).set_use_wake_word(false);
          - voice_assistant.stop:
    - if:
        condition:
          lambda: return id(wake_word_engine_location).state == "On device";
        then:
          - micro_wake_word.stop
    - lambda: id(voice_assistant_phase) = ${voice_assist_not_ready_phase_id};
    - script.execute: reset_led


script:
  - id: reset_led
    then:
      - if:
          condition:
            lambda: return !id(init_in_progress);
          then:
            - if:
                condition:
                  lambda: return id(voice_assistant_phase) == ${voice_assist_listening_phase_id};
                then:                     
                  - light.turn_on:
                      id: led_ring
                      blue: 0%
                      red: 100%
                      green: 0%
                      brightness: 100%
                      effect: wakeword
            - if:
                condition:
                  lambda: return id(voice_assistant_phase) == ${voice_assist_thinking_phase_id};
                then:                     
                  - light.turn_on:
                      id: led_ring
                      blue: 100%
                      red: 0%
                      green: 0%
                      brightness: 100%
                      effect: Working
                  - delay: 100ms
            - if:
                condition:
                  lambda: return id(voice_assistant_phase) == ${voice_assist_replying_phase_id};
                then:                     
                  - light.turn_on:
                      id: led_ring
                      blue: 0%
                      red: 0%
                      green: 100%
                      brightness: 100%
                      effect: Working
            - if:
                condition:
                  lambda: return id(voice_assistant_phase) == ${voice_assist_idle_phase_id};
                then:                     
                  - light.turn_on:
                      id: led_ring
                      blue: 100%
                      red: 0%
                      green: 0%
                      brightness: 40%
                      effect: none
                  - delay: 200ms
            - if:
                condition:
                  lambda: return id(voice_assistant_phase) == ${voice_assist_not_ready_phase_id};
                then:                     
                  - light.turn_on:
                      id: led_ring
                      blue: 40%
                      red: 100%
                      green: 0%
                      effect: Slow Pulse
            - if:
                condition:
                  lambda: return id(voice_assistant_phase) == ${voice_assist_error_phase_id};
                then:                     
                  - light.turn_on:
                      id: led_ring
                      blue: 0%
                      red: 100%
                      green: 0%
                      brightness: 100%
                      effect: none
            - if:
                condition:
                  lambda: return id(voice_assistant_phase) == ${voice_assist_muted_phase_id};
                then:                     
                  - light.turn_off: led_ring
          else:
            - light.turn_on:
                id: led_ring
                blue: 0%
                red: 100%
                green: 0%
                effect: Fast Pulse

light:
  - platform: esp32_rmt_led_strip
    id: led_ring
    name: "${friendly_name} Light"
    pin: GPIO33 #GPIO19
    num_leds: 12
    rmt_channel: 0
    rgb_order: GRB
    chipset: ws2812
    default_transition_length: 0s
    effects:
      - pulse:
          name: "Pulse"
          transition_length: 300ms
          update_interval: 300ms
          min_brightness: 50%
          max_brightness: 100%
      - addressable_twinkle:
          name: "Working"
          twinkle_probability: 5%
          progress_interval: 3ms
      - addressable_color_wipe:
          name: "Wakeword"
          colors:
            - red: 100%
              green: 0%
              blue: 0%
              num_leds: 12
          add_led_interval: 40ms
          reverse: false
      - pulse:
          name: "Slow Pulse"
          transition_length: 0.5s
          update_interval: 1s
          min_brightness: 0%
          max_brightness: 100%
      - pulse:
          name: "Fast Pulse"
          transition_length: 50ms
          update_interval: 100ms
          min_brightness: 50%
          max_brightness: 100%

switch:
  - platform: template
    name: Mute
    id: mute
    optimistic: true
    restore_mode: RESTORE_DEFAULT_OFF
    entity_category: config
    on_turn_off:
      - if:
          condition:
            lambda: return !id(init_in_progress);
          then:
            - lambda: id(voice_assistant_phase) = ${voice_assist_idle_phase_id};
            - if:
                condition:
                  not:
                    - voice_assistant.is_running
                then:
                  - if:
                      condition:
                        lambda: return id(wake_word_engine_location).state == "In Home Assistant";
                      then:
                        - lambda: id(voice_asst).set_use_wake_word(true);
                        - voice_assistant.start_continuous
                  - if:
                      condition:
                        lambda: return id(wake_word_engine_location).state == "On device";
                      then:
                        - micro_wake_word.start
            - script.execute: reset_led
    on_turn_on:
      - if:
          condition:
            lambda: return !id(init_in_progress);
          then:
            - lambda: id(voice_asst).set_use_wake_word(false);
            - voice_assistant.stop
            - micro_wake_word.stop
            - lambda: id(voice_assistant_phase) = ${voice_assist_muted_phase_id};
            - script.execute: reset_led
  - platform: restart
    name: "${name} Restart"
select:
  - platform: template
    entity_category: config
    name: Wake word engine location
    id: wake_word_engine_location
    optimistic: true
    restore_value: true
    options:
      - In Home Assistant
      - On device
    initial_option: On device
    on_value:
      - wait_until:
          lambda: return id(voice_assistant_phase) == ${voice_assist_muted_phase_id} || id(voice_assistant_phase) == ${voice_assist_idle_phase_id};
      - if:
          condition:
            lambda: return x == "In Home Assistant";
          then:
            - micro_wake_word.stop
            - delay: 500ms
            - if:
                condition:
                  switch.is_off: mute
                then:
                  - lambda: id(voice_asst).set_use_wake_word(true);
                  - voice_assistant.start_continuous:
      - if:
          condition:
            lambda: return x == "On device";
          then:
            - lambda: id(voice_asst).set_use_wake_word(false);
            - voice_assistant.stop
            - delay: 500ms
            - micro_wake_word.start

globals:
  - id: init_in_progress
    type: bool
    restore_value: false
    initial_value: "true"
  - id: voice_assistant_phase
    type: int
    restore_value: false
    initial_value: ${voice_assist_not_ready_phase_id}
  - id: speaker_volume
    type: int
    restore_value: yes
    initial_value: '50'  

button:
  - platform: template
    name: "${friendly_name} Volume Up"
    id: btn_volume_up
    on_press:
      then:
        - lambda: |-
            if (id(speaker_volume) < 100) {
              id(external_speaker).volume_up();
            }

  - platform: template
    name: "${friendly_name} Volume Down"
    id: btn_volume_down
    on_press:
      then:
        - lambda: |-
            if (id(speaker_volume) > 0) {
              id(external_speaker).volume_down();
            }


binary_sensor:
  - platform: template
    name: "${friendly_name} Volume Up"
    id: btn_vol_up
    publish_initial_state : True
    on_press:
      then:
        - lambda: |-
            if (id(speaker_volume) < 100) {
              id(external_speaker).volume_up();
            }
  - platform: template
    name: "${friendly_name} Volume Down"
    id: btn_vol_down
    publish_initial_state : True
    on_press:
      then:
        - lambda: |-
            if (id(speaker_volume) > 0) {
              id(external_speaker).volume_down();
            }
  - platform: template
    name: "${friendly_name} Set"
    id: btn_set
    publish_initial_state : True
  - platform: template
    name: "${friendly_name} Play"
    id: btn_play
    publish_initial_state : True
  - platform: template
    name: "${friendly_name} Mode"
    id: btn_mode
    publish_initial_state : True
  - platform: template
    name: "${friendly_name} Record"
    id: btn_record
    publish_initial_state : True



sensor:
  - id: button_adc
    platform: adc
    internal: true
    pin: 39
    attenuation: auto
    update_interval: 15ms
    filters:
      - median:
          window_size: 5
          send_every: 5
          send_first_at: 1
      - delta: 0.1
    on_value_range:
      - below: 0.55
        then:
          - binary_sensor.template.publish:
              id: btn_vol_up
              state: ON
      - above: 0.65
        below: 0.92
        then:
          - binary_sensor.template.publish:
              id: btn_vol_down
              state: ON
      - above: 1.02
        below: 1.33
        then:
          - binary_sensor.template.publish:
              id: btn_set
              state: ON
      - above: 1.43
        below: 1.77
        then:
          - binary_sensor.template.publish:
              id: btn_play
              state: ON
      - above: 1.87
        below: 2.15
        then:
          - binary_sensor.template.publish:
              id: btn_mode
              state: ON
      - above: 2.25
        below: 2.56
        then:
          - binary_sensor.template.publish:
              id: btn_record
              state: ON
      - above: 2.8
        then:
          - binary_sensor.template.publish:
              id: btn_vol_up
              state: OFF
          - binary_sensor.template.publish:
              id: btn_vol_down
              state: OFF
          - binary_sensor.template.publish:
              id: btn_set
              state: OFF
          - binary_sensor.template.publish:
              id: btn_play
              state: OFF
          - binary_sensor.template.publish:
              id: btn_mode
              state: OFF
          - binary_sensor.template.publish:
              id: btn_record
              state: OFF

# Wifi signal
  - platform: wifi_signal
    name: "${friendly_name} WiFi Signal"
    update_interval: 60s
 
 # Generic volume sensor was used so it can be used in the esp_adf_speaker to get volume into HA
  - platform: template
    id: generic_volume_sensor 
    internal: true  

  - platform: template
    name: "${friendly_name} Volume"
    accuracy_decimals: 0
    id: scaled_volume_sensor
    lambda: |-
      // Scale the volume from 0-100 to 0-10
      return id(generic_volume_sensor).state / 10.0;
1 « J'aime »

Yes, top !!

Il faut vraiment que je m’y remette ! (Entre temps grosse promo sur les dernier echos spots, du coup j’ai décidé d’y jeter un oeil en attendant d’avoir la même chose maison. Mais il va y avoir du boulot, c’est bien foutu leur truc…)

Hello !
Je passais par ici avec un faible espoir (j’avoue) de trouver un moyen de faire fonctionner mon ESP32 Korvo v1.1 sous ESPHOME avec HA et microwakeword.

Et bien grâce à @Krull56 ; c’est hose faite ! Énorme merci à toi :slight_smile:

J’ai un peu joué avec le YAML fourni et j’ai essayé d’y apporter quelques modifications; je voulai vous en faire toutes et tous profiter afin qu’on arrive à quelque chose d’utilisable.

Chez moi; voici ce qui marche:

  • Microwakeword (je n’arrive à faire fonctionner que ‹ OK Nabu › pour l’instant)
  • wake word on_device (je n’ai pas testé via HA; mais ça doit certainement marcher aussi)
  • lecture d’un son quand un wakeword est détecté (cf mes modifs dans le YAML)
  • retour vocal de Piper suite à la commande; via le son du Korvo
  • gestion du volume en +/-

Ce qui marche pas:

  • Le son marche; mais il est très faible (j’utilise un mini haut parleur avec ampli intégré branché sur le jack). Sur d’autres machines (Raspberry, Respeaker Core v2) il fonctionne très bien avec un très bon volume.
  • Initialisation du volume au boot (en changeant « initial_value: ‹ 100 › » dans « id: speaker_volume »); le haut-parleur ne fonctionne plus du tout avec cette config
  • Dans mon implémentation; je détecte le wake word → puis je déclenche le son « bip » → puis je lance voice_assistant pour attendre la commande. Ça provoque un délai de ~1 seconde entre le « bip » et le moment où voice assistant est prêt à écouter la commande. Je suis preneur de suggestions pour améliorer ça :wink:

J’espère que ça aidera d’autres à avancer et si quelqu’un ici a une idée pour augmenter le volume; je suis preneur :slight_smile:

Mon YAML:

substitutions:
  name: korvosat2
  friendly_name: korvosat2
  voice_assist_idle_phase_id: "1"
  voice_assist_listening_phase_id: "2"
  voice_assist_thinking_phase_id: "3"
  voice_assist_replying_phase_id: "4"
  voice_assist_not_ready_phase_id: "10"
  voice_assist_error_phase_id: "11"
  voice_assist_muted_phase_id: "12"

  
esphome:
  name: ${name}
  friendly_name: ${friendly_name}
  platformio_options:
    board_build.flash_mode: dio
  on_boot:
    - priority: -100
      then:
        - light.turn_on:
            id: led_ring
            blue: 0%
            red: 100%
            green: 0%
            effect: Fast Pulse
        - delay: 1s
        - wait_until:
            condition:
              wifi.connected:
        - light.turn_on:
            id: led_ring
            blue: 0%
            red: 100%
            green: 0%
            effect: Slow Pulse
        - wait_until: api.connected
        - light.turn_on:
            id: led_ring
            blue: 100%
            red: 0%
            green: 100%
            effect: Pulse
        - delay: 1s
        - if:
            condition:
              lambda: return id(init_in_progress);
            then:
              - lambda: id(init_in_progress) = false;
              - script.execute: reset_led
esp32:
  board: esp-wrover-kit
  framework:
    type: esp-idf
    version: recommended
    sdkconfig_options:
      CONFIG_IDF_TARGET_ESP32: y
      CONFIG_ESPTOOLPY_FLASHMODE_QIO: y
      CONFIG_ESPTOOLPY_FLASHFREQ_80M: y
      CONFIG_ESPTOOLPY_FLASHSIZE_16MB: y
      CONFIG_ESP32S3_DATA_CACHE_64KB: y
      CONFIG_ESP32S3_DATA_CACHE_LINE_64B: y
      CONFIG_PARTITION_TABLE_CUSTOM: y
      CONFIG_PARTITION_TABLE_CUSTOM_FILENAME: "default_16MB.csv" 
      CONFIG_PARTITION_TABLE_FILENAME: "default_16MB.csv" 
      CONFIG_PARTITION_TABLE_OFFSET: "0x8000"
      CONFIG_ESP32_DEFAULT_CPU_FREQ_240: y
      CONFIG_ESP32_SPIRAM_SUPPORT: y
      CONFIG_SPIRAM_SPEED_80M: y
      CONFIG_ESP_SYSTEM_PANIC_SILENT_REBOOT: y
      CONFIG_I2S_ENABLE_DEBUG_LOG: y
      CONFIG_AUDIO_BOARD_CUSTOM: y
      
    components:
      - name: esp32_korvo1_board 
        source: github://dwitgen/korvo_1@main
        refresh: 0s

####new
file:
  - id: timer_finished_wave_file
    file: https://github.com/esphome/firmware/raw/main/voice-assistant/sounds/timer_finished.wav

external_components:
  - source: github://dwitgen/esphome@main
    components: [esp_adf]
    refresh: 0s
  ####new
  - source: github://jesserockz/esphome-components
    components: [file]
    refresh: 0s

# Enable logging
logger:

# Enable Home Assistant API
api:
  encryption:
    key: "2p0zexXOjHNwfkD222YYY1dF8Vsdg/fozQaJZJa9hk0="
  services:
    - service: volume_up
      then:
        - lambda: |-
            if (id(speaker_volume) < 100) {
              id(external_speaker).volume_up();
            }

    - service: volume_down
      then:
        - lambda: |-
            if (id(speaker_volume) > 0) {
              id(external_speaker).volume_down();
            }

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password

captive_portal:

web_server:
  port: 80

output:
  - platform: gpio
    id: pa_ctrl
    pin:
      number: GPIO12
      ignore_strapping_warning: true

esp_adf:
  board: esp32korvo1 


speaker:
  - platform: esp_adf
    id: external_speaker
    
microphone:
  - platform: esp_adf
    id: external_mic


micro_wake_word:
  vad:
  models:
    - model: okay_nabu
    - model: hey_jarvis
    - model: alexa
  on_wake_word_detected:
    then:
      ####new
      - voice_assistant.stop
      - lambda: id(external_speaker).play(id(timer_finished_wave_file), sizeof(id(timer_finished_wave_file)));
      - voice_assistant.start:
          wake_word: !lambda return wake_word;

voice_assistant:
  id: voice_asst
  microphone: external_mic
  speaker: external_speaker
  noise_suppression_level: 2
  auto_gain: 31dBFS
  volume_multiplier: 6

  on_listening:
    - lambda: id(voice_assistant_phase) = ${voice_assist_listening_phase_id};
    - script.execute: reset_led
  on_stt_vad_end:
    - lambda: id(voice_assistant_phase) = ${voice_assist_thinking_phase_id};
    - script.execute: reset_led
  on_tts_start:
    - light.turn_on:
        id: led_ring
        blue: 0%
        red: 100%
        green: 100%
        brightness: 60%
        effect: Working
  on_stt_end: 
    - homeassistant.service:
        service: media_player.play_media
        data:
          entity_id: media_player.mpd
          media_content_id: !lambda return x;
          media_content_type: music
          announce: "true"

  on_tts_stream_start:
    - output.turn_on: pa_ctrl
    - delay: 100ms
    - lambda: id(voice_assistant_phase) = ${voice_assist_replying_phase_id};
    - script.execute: reset_led

  on_end:
    - delay: 100ms
    - wait_until:
        not:
          speaker.is_playing:
    - lambda: id(voice_assistant_phase) = ${voice_assist_idle_phase_id};
    - script.execute: reset_led
    - if:
        condition:
          and:
            - switch.is_off: mute
            - lambda: return id(wake_word_engine_location).state == "On device";
        then:
          - wait_until:
              not:
                voice_assistant.is_running:
          - micro_wake_word.start:

  on_error:
    - if:
        condition:
          lambda: return !id(init_in_progress);
        then:
          - lambda: id(voice_assistant_phase) = ${voice_assist_error_phase_id};
          - script.execute: reset_led
          - delay: 2s
          - if:
              condition:
                switch.is_off: mute
              then:
                - lambda: id(voice_assistant_phase) = ${voice_assist_idle_phase_id};
              else:
                - lambda: id(voice_assistant_phase) = ${voice_assist_muted_phase_id};
          - script.execute: reset_led

  on_client_connected:
    - if:
        condition:
          switch.is_off: mute
        then:
          - if:
              condition:
                lambda: return id(wake_word_engine_location).state == "In Home Assistant";
              then:
                - lambda: id(voice_asst).set_use_wake_word(true);
                - voice_assistant.start_continuous:
          - if:
              condition:
                lambda: return id(wake_word_engine_location).state == "On device";
              then:
                - micro_wake_word.start
          - lambda: id(voice_assistant_phase) = ${voice_assist_idle_phase_id};
        else:
          - lambda: id(voice_assistant_phase) = ${voice_assist_muted_phase_id};
    - lambda: id(init_in_progress) = false;
    - script.execute: reset_led

  on_client_disconnected:
    - if:
        condition:
          lambda: return id(wake_word_engine_location).state == "In Home Assistant";
        then:
          - lambda: id(voice_asst).set_use_wake_word(false);
          - voice_assistant.stop:
    - if:
        condition:
          lambda: return id(wake_word_engine_location).state == "On device";
        then:
          - micro_wake_word.stop
    - lambda: id(voice_assistant_phase) = ${voice_assist_not_ready_phase_id};
    - script.execute: reset_led


script:
  - id: reset_led
    then:
      - if:
          condition:
            lambda: return !id(init_in_progress);
          then:
            - if:
                condition:
                  lambda: return id(voice_assistant_phase) == ${voice_assist_listening_phase_id};
                then:                     
                  - light.turn_on:
                      id: led_ring
                      blue: 0%
                      red: 100%
                      green: 0%
                      brightness: 100%
                      effect: wakeword
            - if:
                condition:
                  lambda: return id(voice_assistant_phase) == ${voice_assist_thinking_phase_id};
                then:                     
                  - light.turn_on:
                      id: led_ring
                      blue: 100%
                      red: 0%
                      green: 0%
                      brightness: 100%
                      effect: Working
                  - delay: 100ms
            - if:
                condition:
                  lambda: return id(voice_assistant_phase) == ${voice_assist_replying_phase_id};
                then:                     
                  - light.turn_on:
                      id: led_ring
                      blue: 0%
                      red: 0%
                      green: 100%
                      brightness: 100%
                      effect: Working
            - if:
                condition:
                  lambda: return id(voice_assistant_phase) == ${voice_assist_idle_phase_id};
                then:                     
                  - light.turn_on:
                      id: led_ring
                      blue: 100%
                      red: 0%
                      green: 0%
                      brightness: 40%
                      effect: none
                  - delay: 200ms
            - if:
                condition:
                  lambda: return id(voice_assistant_phase) == ${voice_assist_not_ready_phase_id};
                then:                     
                  - light.turn_on:
                      id: led_ring
                      blue: 40%
                      red: 100%
                      green: 0%
                      effect: Slow Pulse
            - if:
                condition:
                  lambda: return id(voice_assistant_phase) == ${voice_assist_error_phase_id};
                then:                     
                  - light.turn_on:
                      id: led_ring
                      blue: 0%
                      red: 100%
                      green: 0%
                      brightness: 100%
                      effect: none
            - if:
                condition:
                  lambda: return id(voice_assistant_phase) == ${voice_assist_muted_phase_id};
                then:                     
                  - light.turn_off: led_ring
          else:
            - light.turn_on:
                id: led_ring
                blue: 0%
                red: 100%
                green: 0%
                effect: Fast Pulse

light:
  - platform: esp32_rmt_led_strip
    id: led_ring
    name: "${friendly_name} Light"
    pin: GPIO33 #GPIO19
    num_leds: 12
    rmt_channel: 0
    rgb_order: GRB
    chipset: ws2812
    default_transition_length: 0s
    effects:
      - pulse:
          name: "Pulse"
          transition_length: 300ms
          update_interval: 300ms
          min_brightness: 50%
          max_brightness: 100%
      - addressable_twinkle:
          name: "Working"
          twinkle_probability: 5%
          progress_interval: 3ms
      - addressable_color_wipe:
          name: "Wakeword"
          colors:
            - red: 100%
              green: 0%
              blue: 0%
              num_leds: 12
          add_led_interval: 40ms
          reverse: false
      - pulse:
          name: "Slow Pulse"
          transition_length: 0.5s
          update_interval: 1s
          min_brightness: 0%
          max_brightness: 100%
      - pulse:
          name: "Fast Pulse"
          transition_length: 50ms
          update_interval: 100ms
          min_brightness: 50%
          max_brightness: 100%

switch:
  - platform: template
    name: Mute
    id: mute
    optimistic: true
    restore_mode: RESTORE_DEFAULT_OFF
    entity_category: config
    on_turn_off:
      - if:
          condition:
            lambda: return !id(init_in_progress);
          then:
            - lambda: id(voice_assistant_phase) = ${voice_assist_idle_phase_id};
            - if:
                condition:
                  not:
                    - voice_assistant.is_running
                then:
                  - if:
                      condition:
                        lambda: return id(wake_word_engine_location).state == "In Home Assistant";
                      then:
                        - lambda: id(voice_asst).set_use_wake_word(true);
                        - voice_assistant.start_continuous
                  - if:
                      condition:
                        lambda: return id(wake_word_engine_location).state == "On device";
                      then:
                        - micro_wake_word.start
            - script.execute: reset_led
    on_turn_on:
      - if:
          condition:
            lambda: return !id(init_in_progress);
          then:
            - lambda: id(voice_asst).set_use_wake_word(false);
            - voice_assistant.stop
            - micro_wake_word.stop
            - lambda: id(voice_assistant_phase) = ${voice_assist_muted_phase_id};
            - script.execute: reset_led
  - platform: restart
    name: "${name} Restart"
select:
  - platform: template
    entity_category: config
    name: Wake word engine location
    id: wake_word_engine_location
    optimistic: true
    restore_value: true
    options:
      - In Home Assistant
      - On device
    initial_option: On device
    on_value:
      - wait_until:
          lambda: return id(voice_assistant_phase) == ${voice_assist_muted_phase_id} || id(voice_assistant_phase) == ${voice_assist_idle_phase_id};
      - if:
          condition:
            lambda: return x == "In Home Assistant";
          then:
            - micro_wake_word.stop
            - delay: 500ms
            - if:
                condition:
                  switch.is_off: mute
                then:
                  - lambda: id(voice_asst).set_use_wake_word(true);
                  - voice_assistant.start_continuous:
      - if:
          condition:
            lambda: return x == "On device";
          then:
            - lambda: id(voice_asst).set_use_wake_word(false);
            - voice_assistant.stop
            - delay: 500ms
            - micro_wake_word.start

globals:
  - id: init_in_progress
    type: bool
    restore_value: false
    initial_value: "true"
  - id: voice_assistant_phase
    type: int
    restore_value: false
    initial_value: ${voice_assist_not_ready_phase_id}
  - id: speaker_volume
    type: int
    restore_value: yes
    initial_value: '50'

button:
  - platform: template
    name: "${friendly_name} Volume Up"
    id: btn_volume_up
    on_press:
      then:
        - lambda: |-
            if (id(speaker_volume) < 100) {
              id(external_speaker).volume_up();
            }

  - platform: template
    name: "${friendly_name} Volume Down"
    id: btn_volume_down
    on_press:
      then:
        - lambda: |-
            if (id(speaker_volume) > 0) {
              id(external_speaker).volume_down();
            }


binary_sensor:
  - platform: template
    name: "${friendly_name} Volume Up"
    id: btn_vol_up
    publish_initial_state : True
    on_press:
      then:
        - lambda: |-
            if (id(speaker_volume) < 100) {
              id(external_speaker).volume_up();
            }
  - platform: template
    name: "${friendly_name} Volume Down"
    id: btn_vol_down
    publish_initial_state : True
    on_press:
      then:
        - lambda: |-
            if (id(speaker_volume) > 0) {
              id(external_speaker).volume_down();
            }
  - platform: template
    name: "${friendly_name} Set"
    id: btn_set
    publish_initial_state : True
  - platform: template
    name: "${friendly_name} Play"
    id: btn_play
    publish_initial_state : True
    ####new
    on_press:
      then:
        - lambda: id(external_speaker).play(id(timer_finished_wave_file), sizeof(id(timer_finished_wave_file)));
  - platform: template
    name: "${friendly_name} Mode"
    id: btn_mode
    publish_initial_state : True
  - platform: template
    name: "${friendly_name} Record"
    id: btn_record
    publish_initial_state : True



sensor:
  - id: button_adc
    platform: adc
    internal: true
    pin: 39
    attenuation: auto
    update_interval: 15ms
    filters:
      - median:
          window_size: 5
          send_every: 5
          send_first_at: 1
      - delta: 0.1
    on_value_range:
      - below: 0.55
        then:
          - binary_sensor.template.publish:
              id: btn_vol_up
              state: ON
      - above: 0.65
        below: 0.92
        then:
          - binary_sensor.template.publish:
              id: btn_vol_down
              state: ON
      - above: 1.02
        below: 1.33
        then:
          - binary_sensor.template.publish:
              id: btn_set
              state: ON
      - above: 1.43
        below: 1.77
        then:
          - binary_sensor.template.publish:
              id: btn_play
              state: ON
      - above: 1.87
        below: 2.15
        then:
          - binary_sensor.template.publish:
              id: btn_mode
              state: ON
      - above: 2.25
        below: 2.56
        then:
          - binary_sensor.template.publish:
              id: btn_record
              state: ON
      - above: 2.8
        then:
          - binary_sensor.template.publish:
              id: btn_vol_up
              state: OFF
          - binary_sensor.template.publish:
              id: btn_vol_down
              state: OFF
          - binary_sensor.template.publish:
              id: btn_set
              state: OFF
          - binary_sensor.template.publish:
              id: btn_play
              state: OFF
          - binary_sensor.template.publish:
              id: btn_mode
              state: OFF
          - binary_sensor.template.publish:
              id: btn_record
              state: OFF

# Wifi signal
  - platform: wifi_signal
    name: "${friendly_name} WiFi Signal"
    update_interval: 60s
 
 # Generic volume sensor was used so it can be used in the esp_adf_speaker to get volume into HA
  - platform: template
    id: generic_volume_sensor 
    internal: true  

  - platform: template
    name: "${friendly_name} Volume"
    accuracy_decimals: 0
    id: scaled_volume_sensor
    lambda: |-
      // Scale the volume from 0-100 to 0-10
      return id(generic_volume_sensor).state / 10.0;

Bien à vous

2 « J'aime »

Hello @jerome83136

Bienvenue dans la communauté :wink:

Effectivement, la sortie audio est très très capricieuse, impossible d’initialiser le volume correctement au boot et il faut faire plusieurs appui vol UP pour obtenir un volume correct ( et ne plus y toucher après).
Mais concernant la détection vocale, il est plutôt efficace.

J’attends une livraison d’un kit vocal sur base de respeaker lite, je ferai un petit retour sur ce fil.

D’autres kit sont prévus pour fin d’année, dont un de Nabu Casa ( et à regarder le code yaml en cours de dev, il va être pas mal du tout) .

@+

1 « J'aime »

eh bien merci a @Krull56 et @jerome83163

encore une petite question peux ton faire sortir le son sur une enceinte en wifi

j’ai cette bete https://raspiaudio.com/product/esp-muse-luxe/

elle fonctionne très bien via un serveur lms et picoreplayer en squeezelite

Merci

J’ai aussi ce message d’erreur en tentant de compiler

INFO ESPHome 2024.11.3
INFO Reading configuration /config/esphome/korvo-2.yaml...
INFO Updating https://github.com/dwitgen/esphome.git@main
INFO Updating https://github.com/jesserockz/esphome-components.git@None
INFO Generating C++ source...
INFO Cloning https://github.com/dwitgen/korvo_1.git@main
Traceback (most recent call last):
  File "/usr/local/bin/esphome", line 8, in <module>
    sys.exit(main())
             ^^^^^^
  File "/esphome/esphome/__main__.py", line 1036, in main
    return run_esphome(sys.argv)
           ^^^^^^^^^^^^^^^^^^^^^
  File "/esphome/esphome/__main__.py", line 1023, in run_esphome
    rc = POST_CONFIG_ACTIONS[args.command](args, config)
         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/esphome/esphome/__main__.py", line 503, in command_run
    exit_code = write_cpp(config)
                ^^^^^^^^^^^^^^^^^
  File "/esphome/esphome/__main__.py", line 213, in write_cpp
    return write_cpp_file()
           ^^^^^^^^^^^^^^^^
  File "/esphome/esphome/__main__.py", line 231, in write_cpp_file
    writer.write_cpp(code_s)
  File "/esphome/esphome/writer.py", line 341, in write_cpp
    copy_src_tree()
  File "/esphome/esphome/writer.py", line 307, in copy_src_tree
    copy_files()
  File "/esphome/esphome/components/esp32/__init__.py", line 716, in copy_files
    repo_dir, _ = git.clone_or_update(
                  ^^^^^^^^^^^^^^^^^^^^
  File "/esphome/esphome/git.py", line 67, in clone_or_update
    run_git_command(cmd)
  File "/esphome/esphome/git.py", line 31, in run_git_command
    raise cv.Invalid(lines[-1][len("fatal: ") :])
voluptuous.error.Invalid: could not read Username for 'https://github.com': No such device or address

merci de votre aide