@@ -35,12 +35,10 @@ substitutions:
3535 # for Greek use "Noto Sans" for other languages use a compatible font family
3636 font_family : Figtree
3737
38- micro_wake_word_model : okay_nabu
39-
4038esphome :
4139 name : ${name}
4240 friendly_name : ${friendly_name}
43- min_version : 2025.2 .0
41+ min_version : 2025.5 .0
4442 name_add_mac_suffix : true
4543 on_boot :
4644 priority : 600
@@ -57,6 +55,7 @@ esphome:
5755esp32 :
5856 board : esp32s3box
5957 flash_size : 16MB
58+ cpu_frequency : 240MHz
6059 framework :
6160 type : esp-idf
6261 sdkconfig_options :
@@ -101,7 +100,7 @@ binary_sensor:
101100 number : GPIO0
102101 mode : INPUT_PULLUP
103102 inverted : true
104- id : top_left_button
103+ id : left_top_button
105104 internal : true
106105 on_multi_click :
107106 - timing :
@@ -185,11 +184,21 @@ media_player:
185184 - id : timer_finished_sound
186185 file : https://github.com/esphome/home-assistant-voice-pe/raw/dev/sounds/timer_finished.flac
187186 on_announcement :
187+ # Stop the wake word (mWW or VA) if the mic is capturing
188188 - if :
189189 condition :
190- microphone.is_capturing :
190+ - microphone.is_capturing :
191191 then :
192- - script.execute : stop_voice_assistant
192+ - script.execute : stop_wake_word
193+ # Ensure VA stops before moving on
194+ - if :
195+ condition :
196+ - lambda : return id(wake_word_engine_location).state == "In Home Assistant";
197+ then :
198+ - wait_until :
199+ - not :
200+ voice_assistant.is_running :
201+ # Since VA isn't running, this is user-intiated media playback. Draw the mute display
193202 - if :
194203 condition :
195204 not :
@@ -198,12 +207,22 @@ media_player:
198207 - lambda : id(voice_assistant_phase) = ${voice_assist_muted_phase_id};
199208 - script.execute : draw_display
200209 on_idle :
201- - script.execute : start_voice_assistant
202- - script.execute : draw_display
210+ # Since VA isn't running, this is the end of user-intiated media playback. Restart the wake word.
211+ - if :
212+ condition :
213+ not :
214+ voice_assistant.is_running :
215+ then :
216+ - script.execute : start_wake_word
217+ - script.execute : set_idle_or_mute_phase
218+ - script.execute : draw_display
203219
204220micro_wake_word :
221+ id : mww
205222 models :
206- - ${micro_wake_word_model}
223+ - okay_nabu
224+ - hey_mycroft
225+ - hey_jarvis
207226 on_wake_word_detected :
208227 - voice_assistant.start :
209228 wake_word : !lambda return wake_word;
@@ -212,6 +231,7 @@ voice_assistant:
212231 id : va
213232 microphone : box_mic
214233 media_player : speaker_media_player
234+ micro_wake_word : mww
215235 noise_suppression_level : 2
216236 auto_gain : 31dBFS
217237 volume_multiplier : 2.0
@@ -239,28 +259,34 @@ voice_assistant:
239259 - lambda : id(voice_assistant_phase) = ${voice_assist_replying_phase_id};
240260 - script.execute : draw_display
241261 on_end :
262+ # Wait a short amount of time to see if an announcement starts
242263 - wait_until :
243- and :
244- - not :
245- media_player.is_announcing :
246- - not :
247- voice_assistant.is_running :
248- - if :
249264 condition :
250- switch.is_off : mute
251- then :
252- - lambda : id(voice_assistant_phase) = ${voice_assist_idle_phase_id};
253- else :
254- - lambda : id(voice_assistant_phase) = ${voice_assist_muted_phase_id};
255- - script.execute : draw_display
265+ - media_player.is_announcing :
266+ timeout : 0.5s
267+ # Announcement is finished and the I2S bus is free
268+ - wait_until :
269+ - and :
270+ - not :
271+ media_player.is_announcing :
272+ - not :
273+ speaker.is_playing :
274+ # Restart only mWW if enabled; streaming wake words automatically restart
256275 - if :
257276 condition :
258- and :
259- - switch.is_off : mute
260- - lambda : return id(wake_word_engine_location).state == "On device";
261- - lambda : return id(voice_assistant_phase) != ${voice_assist_timer_finished_phase_id};
277+ - lambda : return id(wake_word_engine_location).state == "On device";
262278 then :
279+ - lambda : id(va).set_use_wake_word(false);
263280 - micro_wake_word.start :
281+ - script.execute : set_idle_or_mute_phase
282+ - script.execute : draw_display
283+ # Clear text sensors
284+ - text_sensor.template.publish :
285+ id : text_request
286+ state : " "
287+ - text_sensor.template.publish :
288+ id : text_response
289+ state : " "
264290 on_error :
265291 - if :
266292 condition :
@@ -279,10 +305,11 @@ voice_assistant:
279305 - script.execute : draw_display
280306 on_client_connected :
281307 - lambda : id(init_in_progress) = false;
282- - script.execute : start_voice_assistant
308+ - script.execute : start_wake_word
309+ - script.execute : set_idle_or_mute_phase
283310 - script.execute : draw_display
284311 on_client_disconnected :
285- - script.execute : stop_voice_assistant
312+ - script.execute : stop_wake_word
286313 - lambda : id(voice_assistant_phase) = ${voice_assist_not_ready_phase_id};
287314 - script.execute : draw_display
288315 on_timer_started :
@@ -448,30 +475,29 @@ script:
448475 }
449476 id(s3_box_lcd).printf(120, 47, id(font_timer), Color::BLACK, "%s", display_string.c_str());
450477 }
451-
452- - id : start_voice_assistant
478+ # Starts either mWW or the streaming wake word, depending on the configured location
479+ - id : start_wake_word
453480 then :
454481 - if :
455482 condition :
456- switch.is_off : mute
483+ and :
484+ - not :
485+ - voice_assistant.is_running :
486+ - lambda : return id(wake_word_engine_location).state == "On device";
457487 then :
458- - if :
459- condition :
460- lambda : return id(wake_word_engine_location).state == "In Home Assistant";
461- then :
462- - lambda : id(va).set_use_wake_word(true);
463- - voice_assistant.start_continuous :
464- - if :
465- condition :
466- lambda : return id(wake_word_engine_location).state == "On device";
467- then :
468- - lambda : id(va).set_use_wake_word(false);
469- - micro_wake_word.start
470- - lambda : id(voice_assistant_phase) = ${voice_assist_idle_phase_id};
471- else :
472- - lambda : id(voice_assistant_phase) = ${voice_assist_muted_phase_id};
473-
474- - id : stop_voice_assistant
488+ - lambda : id(va).set_use_wake_word(false);
489+ - micro_wake_word.start :
490+ - if :
491+ condition :
492+ and :
493+ - not :
494+ - voice_assistant.is_running :
495+ - lambda : return id(wake_word_engine_location).state == "In Home Assistant";
496+ then :
497+ - lambda : id(va).set_use_wake_word(true);
498+ - voice_assistant.start_continuous :
499+ # Stops either mWW or the streaming wake word, depending on the configured location
500+ - id : stop_wake_word
475501 then :
476502 - if :
477503 condition :
@@ -483,8 +509,17 @@ script:
483509 condition :
484510 lambda : return id(wake_word_engine_location).state == "On device";
485511 then :
486- - voice_assistant.stop :
487512 - micro_wake_word.stop :
513+ # Set the voice assistant phase to idle or muted, depending on if the software mute switch is activated
514+ - id : set_idle_or_mute_phase
515+ then :
516+ - if :
517+ condition :
518+ switch.is_off : mute
519+ then :
520+ - lambda : id(voice_assistant_phase) = ${voice_assist_idle_phase_id};
521+ else :
522+ - lambda : id(voice_assistant_phase) = ${voice_assist_muted_phase_id};
488523
489524switch :
490525 - platform : gpio
@@ -501,40 +536,13 @@ switch:
501536 restore_mode : RESTORE_DEFAULT_OFF
502537 entity_category : config
503538 on_turn_off :
504- - if :
505- condition :
506- lambda : return !id(init_in_progress);
507- then :
508- - lambda : id(voice_assistant_phase) = ${voice_assist_idle_phase_id};
509- - if :
510- condition :
511- not :
512- - voice_assistant.is_running
513- then :
514- - if :
515- condition :
516- lambda : return id(wake_word_engine_location).state == "In Home Assistant";
517- then :
518- - lambda : id(va).set_use_wake_word(true);
519- - voice_assistant.start_continuous
520- - if :
521- condition :
522- lambda : return id(wake_word_engine_location).state == "On device";
523- then :
524- - lambda : id(va).set_use_wake_word(false);
525- - micro_wake_word.start
526- - script.execute : draw_display
539+ - microphone.unmute :
540+ - lambda : id(voice_assistant_phase) = ${voice_assist_idle_phase_id};
541+ - script.execute : draw_display
527542 on_turn_on :
528- - if :
529- condition :
530- lambda : return !id(init_in_progress);
531- then :
532- - lambda : id(va).set_use_wake_word(false);
533- - voice_assistant.stop
534- - micro_wake_word.stop
535- - lambda : id(voice_assistant_phase) = ${voice_assist_muted_phase_id};
536- - script.execute : draw_display
537-
543+ - microphone.mute :
544+ - lambda : id(voice_assistant_phase) = ${voice_assist_muted_phase_id};
545+ - script.execute : draw_display
538546 - platform : template
539547 id : timer_ringing
540548 optimistic : true
@@ -782,7 +790,6 @@ display:
782790 it.filled_rectangle(20 , 20 , 280 , 30 , Color::WHITE );
783791 it.rectangle(20 , 20 , 280 , 30 , Color::BLACK );
784792 it.printf(30, 25, id(font_request), Color::BLACK, "%s", id(text_request).state.c_str());
785-
786793 id(draw_timer_timeline).execute();
787794 - id : replying_page
788795 lambda : |-
0 commit comments