diff --git a/esphome/m5stack-atom-echo.yaml b/esphome/m5stack-atom-echo.yaml index 037bc34..e29018e 100644 --- a/esphome/m5stack-atom-echo.yaml +++ b/esphome/m5stack-atom-echo.yaml @@ -1,15 +1,13 @@ substitutions: - name: "m5stack-atom-echo" - friendly_name: "M5Stack Atom Echo" + name: m5stack-atom-echo + friendly_name: M5Stack Atom Echo + micro_wake_word_model: okay_nabu # alexa, hey_jarvis, hey_mycroft are also supported esphome: name: ${name} - friendly_name: ${friendly_name} name_add_mac_suffix: False - project: - name: m5stack.atom-echo-voice-assistant - version: "24.7.24" - min_version: 2024.9.0 + friendly_name: ${friendly_name} + min_version: 2025.2.0 esp32: board: m5stack-atom @@ -25,25 +23,12 @@ api: ota: - platform: esphome - password: !secret ota id: ota_esphome - - platform: http_request - id: ota_http_request - -update: - - platform: http_request - id: update_http_request - name: Firmware - source: https://firmware.esphome.io/voice-assistant/m5stack-atom-echo/manifest.json - -http_request: + password: !secret ota wifi: on_connect: - delay: 5s # Gives time for improv results to be transmitted - - ble.disable: - on_disconnect: - - ble.enable: ssid: Voltage-legacy password: !secret voltage_legacy_psk use_address: ${name}.home @@ -54,22 +39,11 @@ wifi: ssid: "Raspiaudio Fallback Hotspot" password: !secret fallback_psk -improv_serial: - -esp32_improv: - authorizer: none - logger: -dashboard_import: - package_import_url: github://esphome/firmware/voice-assistant/m5stack-atom-echo.adopted.yaml@main - captive_portal: button: - - platform: safe_mode - id: button_safe_mode - name: Safe Mode Boot - platform: factory_reset id: factory_reset_btn name: Factory reset @@ -91,16 +65,52 @@ speaker: id: echo_speaker i2s_dout_pin: GPIO22 dac_type: external - channel: mono + bits_per_sample: 32bit + channel: right + buffer_duration: 60ms + +media_player: + - platform: speaker + name: None + id: echo_media_player + announcement_pipeline: + speaker: echo_speaker + format: WAV + codec_support_enabled: false + buffer_size: 6000 + files: + - id: timer_finished_wave_file + file: https://github.com/esphome/wake-word-voice-assistants/raw/main/sounds/timer_finished.wav + on_announcement: + - if: + condition: + - microphone.is_capturing: + then: + - if: + condition: + lambda: return id(wake_word_engine_location).state == "On device"; + then: + - micro_wake_word.stop: + else: + - voice_assistant.stop: + - script.execute: reset_led + - light.turn_on: + id: led + blue: 100% + red: 0% + green: 0% + brightness: 100% + effect: none + on_idle: + - script.execute: start_wake_word voice_assistant: id: va microphone: echo_microphone - speaker: echo_speaker + media_player: echo_media_player noise_suppression_level: 2 auto_gain: 31dBFS volume_multiplier: 2.0 - vad_threshold: 3 on_listening: - light.turn_on: id: led @@ -125,10 +135,7 @@ voice_assistant: effect: none on_end: - delay: 100ms - - wait_until: - not: - speaker.is_playing: - - script.execute: reset_led + - script.execute: start_wake_word on_error: - light.turn_on: id: led @@ -137,28 +144,21 @@ voice_assistant: blue: 0% brightness: 100% effect: none - - delay: 1s + - delay: 2s - script.execute: reset_led on_client_connected: - - if: - condition: - switch.is_on: use_wake_word - then: - - voice_assistant.start_continuous: - - script.execute: reset_led + - delay: 2s # Give the api server time to settle + - script.execute: start_wake_word on_client_disconnected: - - if: - condition: - switch.is_on: use_wake_word - then: - - voice_assistant.stop: - - light.turn_off: led + - voice_assistant.stop: + - micro_wake_word.stop: on_timer_finished: - voice_assistant.stop: - - switch.turn_on: timer_ringing + - micro_wake_word.stop: - wait_until: not: microphone.is_capturing: + - switch.turn_on: timer_ringing - light.turn_on: id: led red: 0% @@ -166,25 +166,15 @@ voice_assistant: blue: 0% brightness: 100% effect: "Fast Pulse" - - while: - condition: - switch.is_on: timer_ringing - then: - - lambda: id(echo_speaker).play(id(timer_finished_wave_file), sizeof(id(timer_finished_wave_file))); - - delay: 1s - wait_until: - not: - speaker.is_playing: + - switch.is_off: timer_ringing - light.turn_off: led - switch.turn_off: timer_ringing - - if: - condition: - switch.is_on: use_wake_word - then: - - voice_assistant.start_continuous: - - script.execute: reset_led binary_sensor: + # button does the following: + # short click - stop a timer + # if no timer then restart either microwakeword or voice assistant continuous - platform: gpio pin: number: GPIO39 @@ -204,23 +194,7 @@ binary_sensor: then: - switch.turn_off: timer_ringing else: - - if: - condition: - switch.is_off: use_wake_word - then: - - if: - condition: voice_assistant.is_running - then: - - voice_assistant.stop: - - script.execute: reset_led - else: - - voice_assistant.start: - else: - - voice_assistant.stop - - delay: 1s - - script.execute: reset_led - - script.wait: reset_led - - voice_assistant.start_continuous: + - script.execute: start_wake_word - timing: - ON for at least 10s then: @@ -257,7 +231,7 @@ script: then: - if: condition: - - switch.is_on: use_wake_word + - lambda: return id(wake_word_engine_location).state == "On device"; - switch.is_on: use_listen_light then: - light.turn_on: @@ -268,28 +242,45 @@ script: brightness: 60% effect: none else: - - light.turn_off: led - -switch: - - platform: template - name: Use wake word - id: use_wake_word - optimistic: true - restore_mode: RESTORE_DEFAULT_ON - entity_category: config - on_turn_on: - - lambda: id(va).set_use_wake_word(true); + - if: + condition: + - lambda: return id(wake_word_engine_location).state != "On device"; + - switch.is_on: use_listen_light + then: + - light.turn_on: + id: led + red: 0% + green: 100% + blue: 100% + brightness: 60% + effect: none + else: + - light.turn_off: led + - id: start_wake_word + then: + - wait_until: + and: + - media_player.is_idle: + - speaker.is_stopped: - if: condition: - not: - - voice_assistant.is_running + lambda: return id(wake_word_engine_location).state == "On device"; then: - - voice_assistant.start_continuous - - script.execute: reset_led - on_turn_off: - - voice_assistant.stop - - lambda: id(va).set_use_wake_word(false); - - script.execute: reset_led + - voice_assistant.stop + - micro_wake_word.stop: + - delay: 1s + - script.execute: reset_led + - script.wait: reset_led + - micro_wake_word.start: + else: + - if: + condition: voice_assistant.is_running + then: + - voice_assistant.stop: + - script.execute: reset_led + - voice_assistant.start_continuous: + +switch: - platform: template name: Use listen light id: use_listen_light @@ -303,26 +294,67 @@ switch: - platform: template id: timer_ringing optimistic: true - internal: true restore_mode: ALWAYS_OFF + on_turn_off: + # Turn off the repeat mode and disable the pause between playlist items + - lambda: |- + id(echo_media_player) + ->make_call() + .set_command(media_player::MediaPlayerCommand::MEDIA_PLAYER_COMMAND_REPEAT_OFF) + .set_announcement(true) + .perform(); + id(echo_media_player)->set_playlist_delay_ms(speaker::AudioPipelineType::ANNOUNCEMENT, 0); + # Stop playing the alarm + - media_player.stop: + announcement: true on_turn_on: + # Turn on the repeat mode and pause for 1000 ms between playlist items/repeats + - lambda: |- + id(echo_media_player) + ->make_call() + .set_command(media_player::MediaPlayerCommand::MEDIA_PLAYER_COMMAND_REPEAT_ONE) + .set_announcement(true) + .perform(); + id(echo_media_player)->set_playlist_delay_ms(speaker::AudioPipelineType::ANNOUNCEMENT, 1000); + - media_player.speaker.play_on_device_media_file: + media_file: timer_finished_wave_file + announcement: true - delay: 15min - switch.turn_off: timer_ringing -external_components: - - source: github://pr#5230 - components: - - esp_adf - refresh: 0s - - source: - type: git - url: https://github.com/jesserockz/esphome-components - ref: main - components: [file] - refresh: 0s +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: + - if: + condition: + lambda: return x == "In Home Assistant"; + then: + - micro_wake_word.stop + - delay: 500ms + - lambda: id(va).set_use_wake_word(true); + - voice_assistant.start_continuous: + - if: + condition: + lambda: return x == "On device"; + then: + - lambda: id(va).set_use_wake_word(false); + - voice_assistant.stop + - delay: 500ms + - micro_wake_word.start -esp_adf: - -file: - - id: timer_finished_wave_file - file: https://github.com/esphome/firmware/raw/main/voice-assistant/sounds/timer_finished.wav +micro_wake_word: + on_wake_word_detected: + - voice_assistant.start: + wake_word: !lambda return wake_word; + vad: + models: + - model: ${micro_wake_word_model}