You’ll need to compile with the dev version, there are other changes merged.
I updated the PR so should fix that.
external_components:
- source: github://pr#11680
components: [lvgl, const]
refresh: 1h
sidenote, i used the same display model as before, not the one in your example:
spi:
id: display_qspi
type: quad
clk_pin: 10
data_pins: [11, 12, 13, 14]
display:
- platform: mipi_spi
id: main_display
model: WAVESHARE-ESP32-S3-TOUCH-LCD-3.49
rotation: 90
lvgl:
I had, or rather still have, this green stripe on my motherboard. I managed to get rid of it using AI testing by shifting the image by a few pixels. Thomas, maybe that will help a little? However, other things didn’t work after that.
waveshare 1.75 Amoled Screen 466x466 resolution
substitutions:
name: waveshare_amoled_175
friendly_name: "Xiaozhi 1.75 AMOLED"
# Xiaozhi Character Images
imagemodel: "Eyes" # Options: Gwen, Casita, Eyes, Robot, etc.
imagewidth: "360"
imageheight: "360"
rotate_display: "0"
startup_sound: "Home_Connected"
# Xiaozhi Image URLs - Using 360x360 (best balance of quality and RAM usage)
loading_illustration_file: https://github.com/RealDeco/xiaozhi-esphome/raw/main/images/Eyes/360x360/loading.png
idle_illustration_file: https://github.com/RealDeco/xiaozhi-esphome/raw/main/images/Eyes/360x360/idle.png
listening_illustration_file: https://github.com/RealDeco/xiaozhi-esphome/raw/main/images/Eyes/360x360/listening.png
thinking_illustration_file: https://github.com/RealDeco/xiaozhi-esphome/raw/main/images/Eyes/360x360/thinking.png
replying_illustration_file: https://github.com/RealDeco/xiaozhi-esphome/raw/main/images/Eyes/360x360/replying.png
error_illustration_file: https://github.com/RealDeco/xiaozhi-esphome/raw/main/images/Eyes/360x360/error.png
timer_finished_illustration_file: https://github.com/RealDeco/xiaozhi-esphome/raw/main/images/Eyes/360x360/timer_finished.png
mute_illustration_file: https://github.com/RealDeco/xiaozhi-esphome/raw/main/images/Eyes/360x360/mute.png
# Extra image for blinking animation
blink_illustration_file: https://github.com/RealDeco/xiaozhi-esphome/raw/main/images/Eyes/360x360/mute.png
startup_sound_file: https://github.com/RealDeco/xiaozhi-esphome/raw/main/sounds/${startup_sound}.flac
# Colors
loading_illustration_background_color: "000000"
idle_illustration_background_color: "000000"
listening_illustration_background_color: "000000"
thinking_illustration_background_color: "000000"
replying_illustration_background_color: "000000"
error_illustration_background_color: "000000"
# Phase IDs
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"
voice_assist_timer_finished_phase_id: "20"
font_glyphsets: "GF_Latin_Core"
font_family: Figtree
light5_entity: "light.none"
# External Media Player (e.g., your Sonos)
external_media_player_entity: "media_player.sonos_wohnzimmer" # Change to your Sonos entity ID
esphome:
name: ${name}
friendly_name: ${friendly_name}
min_version: 2024.6.0
name_add_mac_suffix: false
platformio_options:
board_build.flash_mode: dio
board_build.f_flash: 80000000L
board_build.f_cpu: 240000000L
on_boot:
priority: 600
then:
- switch.turn_on: speaker_enable
- delay: 500ms
- script.execute: draw_display
- delay: 30s
- if:
condition:
lambda: return id(init_in_progress);
then:
- lambda: id(init_in_progress) = false;
- script.execute: draw_display
esp32:
board: esp32-s3-devkitc-1
flash_size: 16MB
framework:
type: esp-idf
psram:
mode: octal
speed: 80MHz
# Required for CST9217 touchscreen driver
external_components:
- source:
type: git
url: https://github.com/shelson/esphome-cst9217
api:
on_client_connected:
- script.execute: draw_display
# Startup sound disabled due to download issues
# - if:
# condition:
# switch.is_on: startup_sound_switch
# then:
# - delay: 300ms
# - script.execute:
# id: play_sound
# priority: true
# sound_file: !lambda return id(ready_sound);
# - delay: 300ms
on_client_disconnected:
- script.execute: draw_display
ota:
- platform: esphome
logger:
level: INFO
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
ap:
ssid: "${name} Fallback"
password: "fallbackpass"
on_connect:
- script.execute: draw_display
on_disconnect:
- script.execute: draw_display
captive_portal:
# --- HARDWARE ---
i2c:
- id: bus_a
sda: GPIO15
scl: GPIO14
scan: true
i2s_audio:
- id: i2s_audio_bus
i2s_lrclk_pin:
number: GPIO45
ignore_strapping_warning: true
i2s_bclk_pin: GPIO9
i2s_mclk_pin: GPIO42
audio_adc:
- platform: es7210
i2c_id: bus_a
id: es7210_adc
address: 0x40
mic_gain: 30db
bits_per_sample: 16bit
sample_rate: 16000
audio_dac:
- platform: es8311
i2c_id: bus_a
id: es8311_dac
address: 0x18
bits_per_sample: 16bit
sample_rate: 16000
microphone:
- platform: i2s_audio
id: box_mic
i2s_audio_id: i2s_audio_bus
sample_rate: 16000
i2s_din_pin: GPIO10
bits_per_sample: 16bit
adc_type: external
pdm: false
speaker:
- platform: i2s_audio
id: box_speaker
i2s_audio_id: i2s_audio_bus
dac_type: external
i2s_dout_pin: GPIO8
sample_rate: 16000
bits_per_sample: 16bit
audio_dac: es8311_dac
buffer_duration: 64ms
media_player:
- platform: speaker
name: None
id: external_media_player
announcement_pipeline:
speaker: box_speaker
format: FLAC
sample_rate: 48000
num_channels: 1
# Sound files temporarily disabled due to download issues
# files:
# - id: timer_finished_sound
# file: https://github.com/esphome/home-assistant-voice-pe/raw/dev/sounds/timer_finished.flac
# - id: wake_word_triggered_sound
# file: https://github.com/esphome/home-assistant-voice-pe/raw/dev/sounds/wake_word_triggered.flac
# - id: ready_sound
# file: ${startup_sound_file}
on_announcement:
- if:
condition:
- microphone.is_capturing:
then:
- script.execute: stop_wake_word
- if:
condition:
- lambda: return id(wake_word_engine_location).state == "In Home Assistant";
then:
- wait_until:
- not:
voice_assistant.is_running:
- if:
condition:
not:
voice_assistant.is_running:
then:
- if:
condition:
lambda: 'return id(playing_internal_sound);'
then:
- lambda: 'id(playing_internal_sound) = false;'
else:
- lambda: id(voice_assistant_phase) = ${voice_assist_muted_phase_id};
- script.execute: draw_display
on_idle:
- if:
condition:
not:
voice_assistant.is_running:
then:
- script.execute: start_wake_word
- script.execute: set_idle_or_mute_phase
- script.execute: draw_display
button:
- platform: factory_reset
id: factory_reset_btn
internal: true
binary_sensor:
- platform: template
name: "Touch Button"
id: touch_input
on_multi_click:
- timing:
- ON for 40ms to 400ms
- OFF for at least 330ms
then:
- if:
condition:
switch.is_on: timer_ringing
then:
- switch.turn_off: timer_ringing
else:
- if:
condition:
lambda: return id(wake_word_engine_location).state == "On device";
then:
- if:
condition:
not:
voice_assistant.is_running:
then:
- voice_assistant.start:
else:
- voice_assistant.stop:
- platform: gpio
pin:
number: GPIO0
mode: INPUT_PULLUP
ignore_strapping_warning: true
inverted: true
id: boot_button
internal: true
on_multi_click:
- timing:
- ON for at least 50ms
- OFF for at least 50ms
then:
- if:
condition:
switch.is_on: timer_ringing
then:
- switch.turn_off: timer_ringing
else:
- if:
condition:
not:
voice_assistant.is_running:
then:
- voice_assistant.start:
else:
- voice_assistant.stop:
- timing:
- ON for at least 10s
then:
- button.press: factory_reset_btn
micro_wake_word:
id: mww
models:
- okay_nabu
- hey_jarvis
- alexa
on_wake_word_detected:
# Sound disabled due to download issues
# - if:
# condition:
# switch.is_on: wake_sound
# then:
# - script.execute:
# id: play_sound
# priority: true
# sound_file: !lambda return id(wake_word_triggered_sound);
# - delay: 300ms
- voice_assistant.start:
wake_word: !lambda return wake_word;
voice_assistant:
id: va
microphone: box_mic
media_player: external_media_player
micro_wake_word: mww
noise_suppression_level: 2
auto_gain: 31dBFS
volume_multiplier: 1.5
on_listening:
- lambda: id(voice_assistant_phase) = ${voice_assist_listening_phase_id};
- text_sensor.template.publish:
id: text_request
state: "..."
- text_sensor.template.publish:
id: text_response
state: "..."
- script.execute: draw_display
on_stt_vad_end:
- lambda: id(voice_assistant_phase) = ${voice_assist_thinking_phase_id};
- script.execute: draw_display
on_stt_end:
- text_sensor.template.publish:
id: text_request
state: !lambda return x;
- script.execute: draw_display
on_tts_start:
- text_sensor.template.publish:
id: text_response
state: !lambda return x;
- lambda: id(voice_assistant_phase) = ${voice_assist_replying_phase_id};
- script.execute: draw_display
on_end:
- wait_until:
condition:
- media_player.is_announcing:
timeout: 0.5s
- wait_until:
- and:
- not:
media_player.is_announcing:
- not:
speaker.is_playing:
- if:
condition:
- lambda: return id(wake_word_engine_location).state == "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
- text_sensor.template.publish:
id: text_request
state: ""
- text_sensor.template.publish:
id: text_response
state: ""
on_error:
- if:
condition:
lambda: return !id(init_in_progress);
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};
- 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_started:
- script.execute: draw_display
on_timer_cancelled:
- script.execute: draw_display
on_timer_updated:
- script.execute: draw_display
on_timer_tick:
- script.execute: draw_display
on_timer_finished:
- switch.turn_on: timer_ringing
script:
- 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(main_display).show_page(listening_page);
id(main_display).update();
break;
case ${voice_assist_thinking_phase_id}:
id(main_display).show_page(thinking_page);
id(main_display).update();
break;
case ${voice_assist_replying_phase_id}:
id(main_display).show_page(replying_page);
id(main_display).update();
break;
case ${voice_assist_error_phase_id}:
id(main_display).show_page(error_page);
id(main_display).update();
break;
case ${voice_assist_muted_phase_id}:
id(main_display).show_page(muted_page);
id(main_display).update();
break;
case ${voice_assist_not_ready_phase_id}:
id(main_display).show_page(no_ha_page);
id(main_display).update();
break;
case ${voice_assist_timer_finished_phase_id}:
id(main_display).show_page(timer_finished_page);
id(main_display).update();
break;
default:
id(main_display).show_page(idle_page);
id(main_display).update();
}
else:
- display.page.show: no_ha_page
- component.update: main_display
else:
- display.page.show: no_wifi_page
- component.update: main_display
else:
- display.page.show: initializing_page
- component.update: main_display
- id: start_wake_word
then:
- if:
condition:
and:
- not:
- voice_assistant.is_running:
- lambda: return id(wake_word_engine_location).state == "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).state == "In Home Assistant";
then:
- lambda: id(va).set_use_wake_word(true);
- voice_assistant.start_continuous:
- id: stop_wake_word
then:
- if:
condition:
lambda: return id(wake_word_engine_location).state == "In Home Assistant";
then:
- lambda: id(va).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:
- 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: |-
id(playing_internal_sound) = true;
if (priority) {
id(external_media_player)
->make_call()
.set_command(media_player::MediaPlayerCommand::MEDIA_PLAYER_COMMAND_STOP)
.set_announcement(true)
.perform();
}
if ( (id(external_media_player).state != media_player::MediaPlayerState::MEDIA_PLAYER_STATE_ANNOUNCING ) || priority) {
id(external_media_player)
->play_file(sound_file, true, false);
}
switch:
- platform: template
name: Mute
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};
- script.execute: draw_display
on_turn_on:
- microphone.mute:
- lambda: id(voice_assistant_phase) = ${voice_assist_muted_phase_id};
- script.execute: draw_display
- platform: template
id: timer_ringing
optimistic: true
internal: true
restore_mode: ALWAYS_OFF
on_turn_off:
- lambda: |-
id(external_media_player)
->make_call()
.set_command(media_player::MediaPlayerCommand::MEDIA_PLAYER_COMMAND_REPEAT_OFF)
.set_announcement(true)
.perform();
id(external_media_player)->set_playlist_delay_ms(speaker::AudioPipelineType::ANNOUNCEMENT, 0);
- media_player.stop:
announcement: true
on_turn_on:
- lambda: |-
id(external_media_player)
->make_call()
.set_command(media_player::MediaPlayerCommand::MEDIA_PLAYER_COMMAND_REPEAT_ONE)
.set_announcement(true)
.perform();
id(external_media_player)->set_playlist_delay_ms(speaker::AudioPipelineType::ANNOUNCEMENT, 1000);
# Timer sound disabled due to download issues
# - media_player.speaker.play_on_device_media_file:
# media_file: timer_finished_sound
# announcement: true
- delay: 15min
- switch.turn_off: timer_ringing
- platform: gpio
name: Speaker Enable
id: speaker_enable
icon: "mdi:speaker"
entity_category: config
pin:
number: GPIO46
ignore_strapping_warning: true
restore_mode: RESTORE_DEFAULT_ON
- platform: template
id: wake_sound
name: Wake sound
icon: "mdi:bullhorn"
entity_category: config
optimistic: true
restore_mode: RESTORE_DEFAULT_ON
- platform: template
id: startup_sound_switch
name: Startup sound
icon: "mdi:card-text-outline"
entity_category: config
optimistic: true
restore_mode: RESTORE_DEFAULT_ON
- platform: template
id: show_text
name: Show Text
icon: "mdi:card-text-outline"
entity_category: config
optimistic: true
restore_mode: RESTORE_DEFAULT_ON
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
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: global_first_active_timer
type: voice_assistant::Timer
restore_value: false
- id: global_is_timer_active
type: bool
restore_value: false
- id: global_first_timer
type: voice_assistant::Timer
restore_value: false
- id: global_is_timer
type: bool
restore_value: false
- id: playing_internal_sound
type: bool
restore_value: no
initial_value: 'false'
- id: is_blinking
type: bool
restore_value: no
initial_value: 'false'
# Blinking animation interval
interval:
- interval: 4s
then:
- lambda: |-
// Only blink during idle phase
if (id(voice_assistant_phase) == ${voice_assist_idle_phase_id} && !id(is_blinking)) {
id(is_blinking) = true;
// Show blink
id(main_display).show_page(blink_page);
id(main_display).update();
}
- delay: 150ms
- lambda: |-
if (id(is_blinking)) {
id(is_blinking) = false;
// Back to idle
id(main_display).show_page(idle_page);
id(main_display).update();
}
# Display using MIPI SPI (CO5300 controller)
spi:
id: display_qspi
type: quad
clk_pin: GPIO38
data_pins: [GPIO4, GPIO5, GPIO6, GPIO7]
display:
- platform: mipi_spi
id: main_display
model: CO5300
cs_pin: GPIO12
reset_pin: GPIO39
bus_mode: quad
update_interval: never
dimensions:
width: 466
height: 466
offset_width: 6
offset_height: 0
data_rate: 80MHz
color_order: rgb
pages:
- id: idle_page
lambda: |-
it.fill(id(idle_color));
it.image(233, 233, id(casita_idle), ImageAlign::CENTER);
- id: blink_page
lambda: |-
it.fill(id(idle_color));
it.image(233, 233, id(eyes_blink), ImageAlign::CENTER);
- id: listening_page
lambda: |-
it.fill(id(listening_color));
it.image(233, 233, id(casita_listening), ImageAlign::CENTER);
- id: thinking_page
lambda: |-
it.fill(id(thinking_color));
it.image(233, 233, id(casita_thinking), ImageAlign::CENTER);
if (id(show_text).state) {
it.filled_rectangle(75, 20, 210, 30, Color::WHITE);
it.rectangle(75, 20, 210, 30, Color::BLACK);
it.printf(75, 25, id(font_request), Color::BLACK, "%s", id(text_request).state.c_str());
}
- id: replying_page
lambda: |-
it.fill(id(replying_color));
it.image(233, 233, id(casita_replying), ImageAlign::CENTER);
int y_offset = 190;
if (id(show_text).state) {
it.filled_rectangle(75, y_offset, 210, 30, Color::WHITE);
it.rectangle(75, y_offset, 210, 30, Color::BLACK);
it.printf(75, y_offset + 5, id(font_response), Color::BLACK, "%s", id(text_response).state.c_str());
}
- id: timer_finished_page
lambda: |-
it.fill(id(idle_color));
it.image(233, 233, id(casita_timer_finished), ImageAlign::CENTER);
- id: error_page
lambda: |-
it.fill(id(error_color));
it.image(233, 233, id(casita_error), ImageAlign::CENTER);
- id: no_ha_page
lambda: |-
it.fill(Color::BLACK);
it.image(233, 233, id(error_no_ha), ImageAlign::CENTER);
- id: no_wifi_page
lambda: |-
it.fill(Color::BLACK);
it.image(233, 233, id(error_no_wifi), ImageAlign::CENTER);
- id: initializing_page
lambda: |-
it.fill(id(loading_color));
it.image(233, 233, id(casita_initializing), ImageAlign::CENTER);
- id: muted_page
lambda: |-
it.fill(Color::BLACK);
it.image(233, 233, id(casita_muted), ImageAlign::CENTER);
# Touchscreen using CST9217 driver
touchscreen:
- platform: cst9217
display: main_display
id: touch_input_driver
interrupt_pin: GPIO11
reset_pin: GPIO40
transform:
mirror_x: true
mirror_y: true
on_touch:
- lambda: |-
int x = touch.x;
int y = touch.y;
ESP_LOGI("touch", "Touch at x=%d, y=%d", x, y);
// Define touch zones (divide display into 3 vertical zones)
// Left: 0-155, Center: 156-310, Right: 311-466
if (x < 155) {
// LEFT ZONE - Previous Track
ESP_LOGI("touch", "Left zone - Previous track");
}
else if (x > 311) {
// RIGHT ZONE - Next Track
ESP_LOGI("touch", "Right zone - Next track");
}
else {
// CENTER ZONE - Play/Pause
ESP_LOGI("touch", "Center zone - Play/Pause");
}
- if:
condition:
lambda: 'return touch.x < 155;'
then:
- homeassistant.service:
service: media_player.media_previous_track
data:
entity_id: ${external_media_player_entity}
- if:
condition:
lambda: 'return touch.x > 311;'
then:
- homeassistant.service:
service: media_player.media_next_track
data:
entity_id: ${external_media_player_entity}
- if:
condition:
lambda: 'return touch.x >= 156 && touch.x <= 310;'
then:
- homeassistant.service:
service: media_player.media_play_pause
data:
entity_id: ${external_media_player_entity}
- binary_sensor.template.publish:
id: touch_input
state: ON
on_release:
then:
- binary_sensor.template.publish:
id: touch_input
state: OFF
# Images - resized to 466x466 to fill the entire display
image:
- file: ${error_illustration_file}
id: casita_error
resize: 466x466
type: RGB565
transparency: alpha_channel
- file: ${idle_illustration_file}
id: casita_idle
resize: 466x466
type: RGB565
transparency: alpha_channel
- file: ${listening_illustration_file}
id: casita_listening
resize: 466x466
type: RGB565
transparency: alpha_channel
- file: ${thinking_illustration_file}
id: casita_thinking
resize: 466x466
type: RGB565
transparency: alpha_channel
- file: ${replying_illustration_file}
id: casita_replying
resize: 466x466
type: RGB565
transparency: alpha_channel
- file: ${timer_finished_illustration_file}
id: casita_timer_finished
resize: 466x466
type: RGB565
transparency: alpha_channel
- file: ${loading_illustration_file}
id: casita_initializing
resize: 466x466
type: RGB565
transparency: alpha_channel
- file: ${mute_illustration_file}
id: casita_muted
resize: 466x466
type: RGB565
transparency: alpha_channel
- file: ${blink_illustration_file}
id: eyes_blink
resize: 466x466
type: RGB565
transparency: alpha_channel
- file: https://github.com/esphome/wake-word-voice-assistants/raw/main/error_box_illustrations/error-no-wifi.png
id: error_no_wifi
resize: 466x466
type: RGB565
transparency: alpha_channel
- file: https://github.com/esphome/wake-word-voice-assistants/raw/main/error_box_illustrations/error-no-ha.png
id: error_no_ha
resize: 466x466
type: RGB565
transparency: alpha_channel
font:
- file:
type: gfonts
family: ${font_family}
weight: 300
italic: true
id: font_request
size: 15
glyphsets:
- ${font_glyphsets}
- file:
type: gfonts
family: ${font_family}
weight: 300
id: font_response
size: 15
glyphsets:
- ${font_glyphsets}
text_sensor:
- id: text_request
platform: template
name: "Text Request"
on_value:
lambda: |-
if(id(text_request).state.length()>30) {
std::string name = id(text_request).state.c_str();
std::string truncated = esphome::str_truncate(name.c_str(),31);
id(text_request).state = (truncated+"...").c_str();
}
- id: text_response
platform: template
name: "Text Response"
on_value:
lambda: |-
if(id(text_response).state.length()>30) {
std::string name = id(text_response).state.c_str();
std::string truncated = esphome::str_truncate(name.c_str(),31);
id(text_response).state = (truncated+"...").c_str();
}
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}
