Files
homelab/active/device_esphome/pyramid1.yaml
ducoterra f2015e2c71
All checks were successful
Podman DDNS Image / build-and-push-ddns (push) Successful in 1m3s
checkpoint commit
2026-05-05 06:26:40 -04:00

998 lines
30 KiB
YAML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
substitutions:
name: pyramid1
friendly_name: Pyramid 1
# Casita images
loading_illustration_file: https://github.com/esphome/wake-word-voice-assistants/raw/main/casita/loading_320_240.png
idle_illustration_file: https://github.com/esphome/wake-word-voice-assistants/raw/main/casita/idle_320_240.png
listening_illustration_file: https://github.com/esphome/wake-word-voice-assistants/raw/main/casita/listening_320_240.png
thinking_illustration_file: https://github.com/esphome/wake-word-voice-assistants/raw/main/casita/thinking_320_240.png
replying_illustration_file: https://github.com/esphome/wake-word-voice-assistants/raw/main/casita/replying_320_240.png
error_illustration_file: https://github.com/esphome/wake-word-voice-assistants/raw/main/casita/error_320_240.png
error_no_wifi_illustration_file: https://github.com/esphome/wake-word-voice-assistants/raw/main/error_box_illustrations/error-no-wifi.png
error_no_ha_illustration_file: https://github.com/esphome/wake-word-voice-assistants/raw/main/error_box_illustrations/error-no-ha.png
# Fonts
mdi_webfont_file: https://raw.githubusercontent.com/Templarian/MaterialDesign-Webfont/master/fonts/materialdesignicons-webfont.ttf
# Audio files
wake_word_trigger_sound_file: wake_word_triggered.wav
# timer_finished_sound_file: https://github.com/esphome/home-assistant-voice-pe/raw/dev/sounds/timer_finished.flac
# error_cloud_expired_file: https://github.com/esphome/home-assistant-voice-pe/raw/dev/sounds/error_cloud_expired.mp3
# Micro wake word models
pick_pig: https://raw.githubusercontent.com/esphome/micro-wake-word-models/refs/heads/main/models/v2/experiments/hey_peppa_pig.json
stop_model_file: https://github.com/kahrendt/microWakeWord/releases/download/stop/stop.json
# Background colors
loading_illustration_background_color: "000000"
idle_illustration_background_color: "000000"
listening_illustration_background_color: "FFFFFF"
thinking_illustration_background_color: "FFFFFF"
replying_illustration_background_color: "FFFFFF"
error_illustration_background_color: "000000"
# Phases of the Voice Assistant
# The voice assistant is ready to be triggered by a wake word
voice_assist_idle_phase_id: "1"
# The voice assistant is listening for a voice command
voice_assist_listening_phase_id: "2"
# The voice assistant is currently processing the command
voice_assist_thinking_phase_id: "3"
# The voice assistant is replying to the command
voice_assist_replying_phase_id: "4"
# The voice assistant is not ready
voice_assist_not_ready_phase_id: "10"
# The voice assistant encountered an error
voice_assist_error_phase_id: "11"
# Muted phase
voice_assist_muted_phase_id: "12"
# Finished timer phase
voice_assist_timer_finished_phase_id: "20"
esphome:
name: pyramid1
friendly_name: Pyramid 1
min_version: 2025.11.3
on_boot:
priority: 600
then:
- delay: 30s
- if:
condition:
lambda: return id(init_in_progress);
then:
- lambda: id(init_in_progress) = false;
esp32:
variant: esp32s3
flash_size: 8MB
cpu_frequency: 240MHz
framework:
type: esp-idf
api:
encryption:
key: "innoIL7I6ZfRekL58F65REjeYNLW1Hp/Q/Kv9SEjnNA="
ota:
- platform: esphome
password: "22de00dcf5c2701a25d2fe719d596123"
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
ap:
ssid: "Echo-Pyramid Fallback Hotspot"
password: "uSTvJjVzweZp"
# Enable logging
logger:
level: INFO
logs:
sensor: WARN
captive_portal:
button:
- platform: factory_reset
id: factory_reset_btn
internal: true
binary_sensor:
- platform: gpio
pin:
number: GPIO41
mode: INPUT_PULLUP
inverted: true
id: user_button
internal: true
on_multi_click:
- timing:
- ON for at least 50ms
- OFF for at least 50ms
then:
- switch.turn_off: timer_ringing
- timing:
- ON for at least 10s
then:
- button.press: factory_reset_btn
external_components:
- source: github://m5stack/esphome-yaml/components
components: [aw87559, si5351, lp5562, pyramidrgb, pyramidtouch]
refresh: 0s
# I2C Bus Configuration
i2c:
- id: bsp_bus
sda: GPIO45
scl: GPIO0
scan: true
- id: ext_bus # used on atomic echo base
sda: GPIO38
scl: GPIO39
# Ehco Base GPIO Expander
pi4ioe5v6408:
- id: pi4ioe5v6408_hub
i2c_id: ext_bus
address: 0x43
aw87559:
id: audio_amp
i2c_id: ext_bus
address: 0x5B
si5351:
id: clock_gen
i2c_id: ext_bus
address: 0x60
# I2S Bus Configuration
i2s_audio:
- id: i2s_audio_bus
i2s_lrclk_pin: GPIO8
i2s_bclk_pin: GPIO6
spi:
clk_pin: GPIO15
mosi_pin: GPIO21
# miso_pin is not used
audio_dac:
- platform: es8311
id: es8311_dac
i2c_id: ext_bus
bits_per_sample: 16bit
sample_rate: 16000
audio_adc:
- platform: es7210
id: es7210_adc
i2c_id: ext_bus
address: 0x40
bits_per_sample: 16bit
sample_rate: 16000
microphone:
- platform: i2s_audio
id: i2s_mic
sample_rate: 16000
i2s_din_pin: GPIO5
bits_per_sample: 16bit
adc_type: external
channel: stereo
speaker:
- platform: i2s_audio
id: i2s_speaker
i2s_dout_pin: GPIO7
dac_type: external
bits_per_sample: 16bit
sample_rate: 16000
channel: mono
audio_dac: es8311_dac
media_player:
- platform: speaker
name: "Echo Pyramid Player"
id: echo_pyramid_player
volume_min: 0.0
volume_max: 1.0
volume_initial: 0.10
buffer_size: 6000
announcement_pipeline:
speaker: i2s_speaker
format: WAV
# sample_rate: 48000
# num_channels: 1
codec_support_enabled: false
files:
- id: wake_word_triggered_sound
file: ${wake_word_trigger_sound_file}
# - id: timer_finished_sound
# file: ${timer_finished_sound_file}
# - id: error_cloud_expired
# file: ${error_cloud_expired_file}
on_state:
- logger.log: "State updated!"
on_play:
- logger.log: "Playback started!"
on_announcement:
- logger.log: "Announcing!"
# Stop the wake word (mWW or VA) if the mic is capturing
- if:
condition:
- microphone.is_capturing:
then:
- script.execute: stop_wake_word
# Ensure VA stops before moving on
- if:
condition:
- lambda: |-
return id(wake_word_engine_location).current_option() == "In Home Assistant";
then:
- wait_until:
- not:
voice_assistant.is_running:
# Since VA isn't running, this is user-intiated media playback. Draw the mute display
- if:
condition:
not:
voice_assistant.is_running:
then:
- lambda: id(voice_assistant_phase) = ${voice_assist_muted_phase_id};
- script.execute: draw_display
on_idle:
# Since VA isn't running, this is the end of user-intiated media playback. Restart the wake word.
- if:
condition:
not:
voice_assistant.is_running:
then:
- script.execute: start_wake_word
- script.execute: set_idle_or_mute_phase
- script.execute: draw_display
switch:
# NS4150B
- platform: gpio
name: Speaker Enable
pin:
pi4ioe5v6408: pi4ioe5v6408_hub
number: 0
mode:
output: true
icon: "mdi:volume-high"
restore_mode: RESTORE_DEFAULT_ON
- platform: template
name: Mute Microphone
id: mute
icon: "mdi:microphone-off"
optimistic: true
restore_mode: RESTORE_DEFAULT_OFF
entity_category: config
on_turn_off:
- microphone.unmute:
- lambda: id(voice_assistant_phase) = ${voice_assist_idle_phase_id};
on_turn_on:
- microphone.mute:
- lambda: id(voice_assistant_phase) = ${voice_assist_muted_phase_id};
- 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_pyramid_player)
->make_call()
.set_command(media_player::MediaPlayerCommand::MEDIA_PLAYER_COMMAND_REPEAT_OFF)
.set_announcement(true)
.perform();
id(echo_pyramid_player)->set_playlist_delay_ms(speaker::AudioPipelineType::ANNOUNCEMENT, 0);
# Stop playing the alarm
- media_player.stop:
announcement: true
- script.execute: start_wake_word
on_turn_on:
- script.execute: stop_wake_word
# Turn on the repeat mode and pause for 1000 ms between playlist items/repeats
- lambda: |-
id(echo_pyramid_player)
->make_call()
.set_command(media_player::MediaPlayerCommand::MEDIA_PLAYER_COMMAND_REPEAT_ONE)
.set_announcement(true)
.perform();
id(echo_pyramid_player)->set_playlist_delay_ms(speaker::AudioPipelineType::ANNOUNCEMENT, 1000);
# - media_player.speaker.play_on_device_media_file:
# media_file: timer_finished_sound
# announcement: true
- delay: 15min
- switch.turn_off: timer_ringing
select:
- platform: template
entity_category: config
name: Wake word engine location
id: wake_word_engine_location
icon: "mdi:account-voice"
optimistic: true
restore_value: true
options:
- In Home Assistant
- On device
initial_option: On device
on_value:
- if:
condition:
lambda: return !id(init_in_progress);
then:
- 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(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
- if:
condition:
switch.is_off: mute
then:
- micro_wake_word.start
- platform: template
name: "Wake word sensitivity"
optimistic: true
initial_option: Slightly sensitive
restore_value: true
entity_category: config
options:
- Slightly sensitive
- Moderately sensitive
- Very sensitive
on_value:
# Sets specific wake word probabilities computed for each particular model
# Note probability cutoffs are set as a quantized uint8 value, each comment has the corresponding floating point cutoff
# False Accepts per Hour values are tested against all units and channels from the Dinner Party Corpus.
# These cutoffs apply only to the specific models included in the firmware: okay_nabu@20241226.3, hey_jarvis@v2, hey_mycroft@v2
lambda: |-
if (x == "Slightly sensitive") {
id(okay_nabu).set_probability_cutoff(217); // 0.85 -> 0.000 FAPH on DipCo (Manifest's default)
id(hey_jarvis).set_probability_cutoff(247); // 0.97 -> 0.563 FAPH on DipCo (Manifest's default)
id(hey_mycroft).set_probability_cutoff(253); // 0.99 -> 0.567 FAPH on DipCo
} else if (x == "Moderately sensitive") {
id(okay_nabu).set_probability_cutoff(176); // 0.69 -> 0.376 FAPH on DipCo
id(hey_jarvis).set_probability_cutoff(235); // 0.92 -> 0.939 FAPH on DipCo
id(hey_mycroft).set_probability_cutoff(242); // 0.95 -> 1.502 FAPH on DipCo (Manifest's default)
} else if (x == "Very sensitive") {
id(okay_nabu).set_probability_cutoff(143); // 0.56 -> 0.751 FAPH on DipCo
id(hey_jarvis).set_probability_cutoff(212); // 0.83 -> 1.502 FAPH on DipCo
id(hey_mycroft).set_probability_cutoff(237); // 0.93 -> 1.878 FAPH on DipCo
}
micro_wake_word:
id: mww
microphone: i2s_mic
models:
- model: okay_nabu
id: okay_nabu
- model: hey_jarvis
id: hey_jarvis
- model: hey_mycroft
id: hey_mycroft
- model: https://raw.githubusercontent.com/esphome/micro-wake-word-models/refs/heads/main/models/v2/experiments/hey_peppa_pig.json
id: hey_peppa_pig
- model: ${stop_model_file}
id: stop
internal: true
vad:
on_wake_word_detected:
- script.execute:
id: play_sound
priority: true
sound_file: !lambda return id(wake_word_triggered_sound);
- wait_until:
condition:
- media_player.is_announcing:
timeout: 0.5s
# Announcement is finished and the I2S bus is free
- wait_until:
- and:
- not:
media_player.is_announcing:
- not:
speaker.is_playing:
- voice_assistant.start:
wake_word: !lambda return wake_word;
voice_assistant:
id: va
microphone: i2s_mic
media_player: echo_pyramid_player
micro_wake_word: mww
noise_suppression_level: 2
auto_gain: 31dBFS
volume_multiplier: 2.0
on_listening:
- lambda: id(voice_assistant_phase) = ${voice_assist_listening_phase_id};
- script.execute: draw_display
on_stt_vad_end:
- lambda: id(voice_assistant_phase) = ${voice_assist_thinking_phase_id};
- script.execute: draw_display
on_tts_start:
- lambda: id(voice_assistant_phase) = ${voice_assist_replying_phase_id};
- script.execute: draw_display
on_end:
# Wait a short amount of time to see if an announcement starts
- wait_until:
condition:
- media_player.is_announcing:
timeout: 0.5s
# Announcement is finished and the I2S bus is free
- wait_until:
- and:
- not:
media_player.is_announcing:
- not:
speaker.is_playing:
# Restart only mWW if enabled; streaming wake words automatically restart
- if:
condition:
- lambda: |-
return id(wake_word_engine_location).current_option() == "On device";
then:
- lambda: id(va).set_use_wake_word(false);
- micro_wake_word.start:
- script.execute: set_idle_or_mute_phase
- script.execute: draw_display
on_error:
# Only set the error phase if the error code is different than duplicate_wake_up_detected or stt-no-text-recognized
# These two are ignored for a better user experience
- if:
condition:
and:
- lambda: return !id(init_in_progress);
- lambda: return code != "duplicate_wake_up_detected";
- lambda: return code != "stt-no-text-recognized";
then:
- lambda: id(voice_assistant_phase) = ${voice_assist_error_phase_id};
- script.execute: draw_display
- delay: 1s
- 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};
# If the error code is cloud-auth-failed, serve a local audio file guiding the user.
- if:
condition:
- lambda: return code == "cloud-auth-failed";
then:
# - script.execute:
# id: play_sound
# priority: true
# sound_file: !lambda return id(error_cloud_expired);
- script.execute: draw_display
on_client_connected:
- lambda: id(init_in_progress) = false;
- script.execute: start_wake_word
- script.execute: set_idle_or_mute_phase
- script.execute: draw_display
on_client_disconnected:
- script.execute: stop_wake_word
- lambda: id(voice_assistant_phase) = ${voice_assist_not_ready_phase_id};
- script.execute: draw_display
on_timer_finished:
- switch.turn_on: timer_ringing
- wait_until:
media_player.is_announcing:
- lambda: id(voice_assistant_phase) = ${voice_assist_timer_finished_phase_id};
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: current_volume
type: float
restore_value: true
initial_value: "0.3"
sensor:
- platform: pyramidtouch
address: 0x1A
i2c_id: ext_bus
update_interval: 50ms
publish_swipe_event: true
swipe_timeout_ms: 500
touch1:
name: "Touch 1"
touch2:
name: "Touch 2"
touch3:
name: "Touch 3"
touch4:
name: "Touch 4"
swipe_event:
name: "Touch Swipe Event"
entity_category: diagnostic
on_value:
then:
- lambda: |-
// Swipe codes:
// 1 = Left Up (volume up)
// 2 = Left Down (volume down)
// 3 = Right Up (brightness up)
// 4 = Right Down (brightness down)
const float volume_step = 0.05f; // 5% volume per gesture
const float brightness_step = 5.0f; // 5% brightness per gesture
const int ev = (int) x;
if (ev == 1 || ev == 2) {
// Left side: control volume (0.0 - 1.0)
float v = id(current_volume);
if (ev == 1) {
v = std::min(1.0f, v + volume_step);
} else {
v = std::max(0.0f, v - volume_step);
}
auto call = id(echo_pyramid_player).make_call();
call.set_volume(v);
call.perform();
id(current_volume) = v;
} else if (ev == 3 || ev == 4) {
// Right side: control RGB brightness (0 - 100)
float b = id(rgb_master_brightness).state;
if (ev == 3) {
b = std::min(100.0f, b + brightness_step);
} else {
b = std::max(0.0f, b - brightness_step);
}
uint8_t b8 = (uint8_t) b;
id(pyramid_rgb1).set_strip_brightness(1, b8);
id(pyramid_rgb2).set_strip_brightness(2, b8);
id(rgb_master_brightness).publish_state(b);
} else {
return;
}
lp5562:
id: lp5562_led
i2c_id: bsp_bus
use_internal_clk: true
# power_save_mode: true
# high_pwm_freq: true
# logarithmic_dimming: true
white_current: 17.5
pyramidrgb:
- id: pyramid_rgb1
i2c_id: ext_bus
address: 0x1A
strip: 1
brightness: 80
- id: pyramid_rgb2
i2c_id: ext_bus
address: 0x1A
strip: 2
brightness: 80
number:
# Master media player volume (0.01.0)
- platform: template
name: "Master Volume"
id: master_volume
icon: "mdi:volume-high"
min_value: 0.0
max_value: 0.4
step: 0.01
restore_value: true
initial_value: 0.3
optimistic: true
set_action:
- lambda: |-
float v = x;
auto call = id(echo_pyramid_player).make_call();
call.set_volume(v);
call.perform();
id(current_volume) = v;
# Master RGB brightness (applies to both strips, 0100%)
- platform: template
name: "RGB Master Brightness"
id: rgb_master_brightness
icon: "mdi:brightness-6"
min_value: 0
max_value: 100
step: 1
restore_value: true
initial_value: 100
optimistic: true
set_action:
- lambda: |-
uint8_t b = (uint8_t) x;
id(pyramid_rgb1).set_strip_brightness(1, b);
id(pyramid_rgb2).set_strip_brightness(2, b);
output:
- platform: lp5562
id: lp5562_white_channel
lp5562_id: lp5562_led
channel: white
- platform: pyramidrgb
id: rgb1_ch0_red
pyramidrgb_id: pyramid_rgb1
channel: 0
color: red
- platform: pyramidrgb
id: rgb1_ch0_green
pyramidrgb_id: pyramid_rgb1
channel: 0
color: green
- platform: pyramidrgb
id: rgb1_ch0_blue
pyramidrgb_id: pyramid_rgb1
channel: 0
color: blue
# Strip 1, Channel 1 (Group 2)
- platform: pyramidrgb
id: rgb1_ch1_red
pyramidrgb_id: pyramid_rgb1
channel: 1
color: red
- platform: pyramidrgb
id: rgb1_ch1_green
pyramidrgb_id: pyramid_rgb1
channel: 1
color: green
- platform: pyramidrgb
id: rgb1_ch1_blue
pyramidrgb_id: pyramid_rgb1
channel: 1
color: blue
# Strip 2, Channel 2 (Group 1)
- platform: pyramidrgb
id: rgb2_ch2_red
pyramidrgb_id: pyramid_rgb2
channel: 2
color: red
- platform: pyramidrgb
id: rgb2_ch2_green
pyramidrgb_id: pyramid_rgb2
channel: 2
color: green
- platform: pyramidrgb
id: rgb2_ch2_blue
pyramidrgb_id: pyramid_rgb2
channel: 2
color: blue
# Strip 2, Channel 3 (Group 2)
- platform: pyramidrgb
id: rgb2_ch3_red
pyramidrgb_id: pyramid_rgb2
channel: 3
color: red
- platform: pyramidrgb
id: rgb2_ch3_green
pyramidrgb_id: pyramid_rgb2
channel: 3
color: green
- platform: pyramidrgb
id: rgb2_ch3_blue
pyramidrgb_id: pyramid_rgb2
channel: 3
color: blue
light:
- platform: monochromatic
name: "LCD Backlight"
output: lp5562_white_channel
icon: "mdi:television"
restore_mode: RESTORE_DEFAULT_ON
- platform: rgb
name: "Strip1 Group1"
red: rgb1_ch0_red
green: rgb1_ch0_green
blue: rgb1_ch0_blue
restore_mode: RESTORE_DEFAULT_ON
- platform: rgb
name: "Strip1 Group2"
red: rgb1_ch1_red
green: rgb1_ch1_green
blue: rgb1_ch1_blue
restore_mode: RESTORE_DEFAULT_ON
- platform: rgb
name: "Strip2 Group1"
red: rgb2_ch2_red
green: rgb2_ch2_green
blue: rgb2_ch2_blue
restore_mode: RESTORE_DEFAULT_ON
- platform: rgb
name: "Strip2 Group2"
red: rgb2_ch3_red
green: rgb2_ch3_green
blue: rgb2_ch3_blue
restore_mode: RESTORE_DEFAULT_ON
display:
- platform: mipi_spi
id: atoms3r_lcd
model: ST7789V
dc_pin: GPIO42
reset_pin: GPIO48
cs_pin: GPIO14
data_rate: 40MHz
dimensions:
height: 128
width: 128
offset_width: 2
offset_height: 1
invert_colors: true
rotation: 180°
pages:
- id: idle_page
lambda: |-
it.fill(id(idle_color));
it.image((it.get_width() / 2), (it.get_height() / 2), id(casita_idle), ImageAlign::CENTER);
- id: listening_page
lambda: |-
it.fill(id(listening_color));
it.image((it.get_width() / 2), (it.get_height() / 2), id(casita_listening), ImageAlign::CENTER);
- id: thinking_page
lambda: |-
it.fill(id(thinking_color));
it.image((it.get_width() / 2), (it.get_height() / 2), id(casita_thinking), ImageAlign::CENTER);
- id: replying_page
lambda: |-
it.fill(id(replying_color));
it.image((it.get_width() / 2), (it.get_height() / 2), id(casita_replying), ImageAlign::CENTER);
- id: error_page
lambda: |-
it.fill(id(error_color));
it.image((it.get_width() / 2), (it.get_height() / 2), id(casita_error), ImageAlign::CENTER);
- id: no_ha_page
lambda: |-
it.image((it.get_width() / 2), (it.get_height() / 2), id(error_no_ha), ImageAlign::CENTER);
- id: no_wifi_page
lambda: |-
it.image((it.get_width() / 2), (it.get_height() / 2), id(error_no_wifi), ImageAlign::CENTER);
- id: initializing_page
lambda: |-
it.fill(id(loading_color));
it.image((it.get_width() / 2), (it.get_height() / 2), id(casita_initializing), ImageAlign::CENTER);
- id: muted_page
lambda: |-
it.fill(Color::BLACK);
it.printf(0, 0, id(mdi_icon_128), Color::WHITE, "%s", "\U000F036D");
script:
# Starts either mWW or the streaming wake word, depending on the configured location
- id: start_wake_word
then:
- if:
condition:
and:
- not:
- voice_assistant.is_running:
- lambda: |-
return id(wake_word_engine_location).current_option() == "On device";
then:
- lambda: id(va).set_use_wake_word(false);
- micro_wake_word.start:
- if:
condition:
and:
- not:
- voice_assistant.is_running:
- lambda: |-
return id(wake_word_engine_location).current_option() == "In Home Assistant";
then:
- lambda: id(va).set_use_wake_word(true);
- voice_assistant.start_continuous:
# Stops either mWW or the streaming wake word, depending on the configured location
- id: stop_wake_word
then:
- if:
condition:
lambda: |-
return id(wake_word_engine_location).current_option() == "In Home Assistant";
then:
- lambda: id(va).set_use_wake_word(false);
- voice_assistant.stop:
- if:
condition:
lambda: |-
return id(wake_word_engine_location).current_option() == "On device";
then:
- micro_wake_word.stop:
# Set the voice assistant phase to idle or muted, depending on if the software mute switch is activated
- id: set_idle_or_mute_phase
then:
- 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};
- id: play_sound
parameters:
priority: bool
sound_file: "audio::AudioFile*"
then:
- lambda: |-
if (priority) {
id(echo_pyramid_player)
->make_call()
.set_command(media_player::MediaPlayerCommand::MEDIA_PLAYER_COMMAND_STOP)
.set_announcement(true)
.perform();
}
if ( (id(echo_pyramid_player).state != media_player::MediaPlayerState::MEDIA_PLAYER_STATE_ANNOUNCING ) || priority) {
id(echo_pyramid_player)
->play_file(sound_file, true, false);
}
- id: draw_display
then:
- if:
condition:
lambda: return !id(init_in_progress);
then:
- if:
condition:
wifi.connected:
then:
- if:
condition:
api.connected:
then:
- lambda: |
switch(id(voice_assistant_phase)) {
case ${voice_assist_listening_phase_id}:
id(atoms3r_lcd).show_page(listening_page);
id(atoms3r_lcd).update();
break;
case ${voice_assist_thinking_phase_id}:
id(atoms3r_lcd).show_page(thinking_page);
id(atoms3r_lcd).update();
break;
case ${voice_assist_replying_phase_id}:
id(atoms3r_lcd).show_page(replying_page);
id(atoms3r_lcd).update();
break;
case ${voice_assist_error_phase_id}:
id(atoms3r_lcd).show_page(error_page);
id(atoms3r_lcd).update();
break;
case ${voice_assist_muted_phase_id}:
id(atoms3r_lcd).show_page(muted_page);
id(atoms3r_lcd).update();
break;
case ${voice_assist_not_ready_phase_id}:
id(atoms3r_lcd).show_page(no_ha_page);
id(atoms3r_lcd).update();
break;
default:
id(atoms3r_lcd).show_page(idle_page);
id(atoms3r_lcd).update();
}
else:
- display.page.show: no_ha_page
- component.update: atoms3r_lcd
else:
- display.page.show: no_wifi_page
- component.update: atoms3r_lcd
else:
- display.page.show: initializing_page
- component.update: atoms3r_lcd
image:
- file: ${error_illustration_file}
id: casita_error
resize: 160x120
type: RGB
transparency: alpha_channel
- file: ${idle_illustration_file}
id: casita_idle
resize: 160x120
type: RGB
transparency: alpha_channel
- file: ${listening_illustration_file}
id: casita_listening
resize: 160x120
type: RGB
transparency: alpha_channel
- file: ${thinking_illustration_file}
id: casita_thinking
resize: 160x120
type: RGB
transparency: alpha_channel
- file: ${replying_illustration_file}
id: casita_replying
resize: 160x120
type: RGB
transparency: alpha_channel
- file: ${loading_illustration_file}
id: casita_initializing
resize: 160x120
type: RGB
transparency: alpha_channel
- file: ${error_no_wifi_illustration_file}
id: error_no_wifi
resize: 160x120
type: RGB
transparency: alpha_channel
- file: ${error_no_ha_illustration_file}
id: error_no_ha
resize: 160x120
type: RGB
transparency: alpha_channel
font:
- file: ${mdi_webfont_file}
id: mdi_icon_128
size: 128
bpp: 4
glyphs:
- "\U000F036D" # mdi:mic-mute
color:
- id: idle_color
hex: ${idle_illustration_background_color}
- id: listening_color
hex: ${listening_illustration_background_color}
- id: thinking_color
hex: ${thinking_illustration_background_color}
- id: replying_color
hex: ${replying_illustration_background_color}
- id: loading_color
hex: ${loading_illustration_background_color}
- id: error_color
hex: ${error_illustration_background_color}