Skip to content

Commit 7cc8870

Browse files
authored
[s3-box*] Add support for new voice assistant features (#110)
1 parent 6d683e0 commit 7cc8870

File tree

3 files changed

+266
-244
lines changed

3 files changed

+266
-244
lines changed

esp32-s3-box-3/esp32-s3-box-3.yaml

Lines changed: 89 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -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-
4038
esphome:
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:
5755
esp32:
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

204220
micro_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

489524
switch:
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

Comments
 (0)