Last active
May 10, 2025 19:57
-
-
Save aalaei/515d900e1cf5be352d4f8d775e6f8678 to your computer and use it in GitHub Desktop.
ESPHome ESP32-S3-LCD-1.85
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| substitutions: | |
| name: esp32-s3-round | |
| friendly_name: esp32-s3-round | |
| 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 | |
| timer_finished_illustration_file: https://github.com/esphome/wake-word-voice-assistants/raw/main/casita/timer_finished_320_240.png | |
| 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" | |
| 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" | |
| # These unique characters have been extracted from every test file of every language available on https://github.com/home-assistant/intents (14 March 2024) | |
| # However, the Figtree font only contains Latin characters, so there is no point using this... unlessyou change the font configuration accordingly. | |
| allowed_characters: " !#%'()+,-./0123456789:;<>?@ABCDEFGHIJKLMNOPQRSTUVWYZ[]_abcdefghijklmnopqrstuvwxyz{|}°²³µ¿ÁÂÄÅÉÖÚßàáâãäåæçèéêëìíîðñòóôõöøùúûüýþāăąćčďĐđēėęěğĮįıļľŁłńňőřśšťũūůűųźŻżŽžơưșțΆΈΌΐΑΒΓΔΕΖΗΘΚΜΝΠΡΣΤΥΦάέήίαβγδεζηθικλμνξοπρςστυφχψωϊόύώАБВГДЕЖЗИКЛМНОПРСТУХЦЧШЪЭЮЯабвгдежзийклмнопрстуфхцчшщъыьэюяёђєіїјљњћאבגדהוזחטיכלםמןנסעפץצקרשת،ءآأإئابةتجحخدذرزسشصضطظعغفقكلمنهوىيٹپچڈکگںھہیےংকচতধনফবযরলশষস়ািু্చయలిెొ్ംഅആഇഈഉഎഓകഗങചജഞടഡണതദധനപഫബഭമയരറലളവശസഹാിീുൂെേൈ്ൺൻർൽൾაბგდევზთილმნოპრსტუფქყშჩცძჭხạảấầẩậắặẹẽếềểệỉịọỏốồổỗộớờởợụủứừửữựỳ—、一上不个中为主乾了些亮人任低佔何作供依侧係個側偵充光入全关冇冷几切到制前動區卧厅厨及口另右吊后吗启吸呀咗哪唔問啟嗎嘅嘛器圍在场執場外多大始安定客室家密寵对將小少左已帘常幫幾库度庫廊廚廳开式後恆感態成我戲戶户房所扇手打执把拔换掉控插摄整斯新明是景暗更最會有未本模機檯櫃欄次正氏水沒没洗活派温測源溫漏潮激濕灯為無煙照熱燈燥物狀玄现現瓦用發的盞目着睡私空窗立笛管節簾籬紅線红罐置聚聲脚腦腳臥色节著行衣解設調請謝警设调走路車车运連遊運過道邊部都量鎖锁門閂閉開關门闭除隱離電震霧面音頂題顏颜風风食餅餵가간감갔강개거게겨결경고공과관그금급기길깥꺼껐꼽나난내네놀누는능니다닫담대더데도동됐되된됨둡드든등디때떤뜨라래러렇렌려로료른를리림링마많명몇모무문물뭐바밝방배변보부불블빨뽑사산상색서설성세센션소쇼수스습시신실싱아안않알았애야어얼업없었에여연열옆오온완외왼요운움워원위으은을음의이인일임입있작잠장재전절정제져조족종주줄중줘지직진짐쪽차창천최추출충치침커컴켜켰쿠크키탁탄태탬터텔통트튼티파팬퍼폰표퓨플핑한함해했행혀현화활후휴힘,?" | |
| # Add support for non-unicode characters by using better glyphset | |
| font_glyphsets: "GF_Latin_Core" | |
| # for Greek use "Noto Sans" for other languages use a compatible font family | |
| font_family: Figtree | |
| micro_wake_word_model: okay_nabu | |
| esphome: | |
| name: ${name} | |
| friendly_name: ${friendly_name} | |
| min_version: 2025.2.0 | |
| on_boot: | |
| - priority: 600 | |
| then: | |
| - delay: 0.4s | |
| - script.execute: draw_display | |
| - component.update: batpercent | |
| - delay: 30s | |
| - if: | |
| condition: | |
| lambda: return id(init_in_progress); | |
| then: | |
| - lambda: id(init_in_progress) = false; | |
| - script.execute: draw_display | |
| - pcf85063.read_time | |
| - component.update: batpercent | |
| esp32: | |
| board: esp32-s3-devkitc-1 | |
| flash_size: 16MB | |
| framework: | |
| type: esp-idf | |
| sdkconfig_options: | |
| CONFIG_ESP32S3_DEFAULT_CPU_FREQ_240: "y" | |
| CONFIG_ESP32S3_DATA_CACHE_64KB: "y" | |
| CONFIG_ESP32S3_DATA_CACHE_LINE_64B: "y" | |
| CONFIG_SPIRAM_FETCH_INSTRUCTIONS: y | |
| CONFIG_SPIRAM_RODATA: y | |
| CONFIG_FATFS_LFN_STACK: "y" | |
| advanced: | |
| enable_idf_experimental_features: true | |
| # external_components: | |
| # source: github://n-serrette/esphome_sd_card | |
| # sd_mmc_card: | |
| # id: main_sd_mmc_card | |
| # mode_1bit: true | |
| # clk_pin: GPIO14 | |
| # cmd_pin: GPIO17 | |
| # data0_pin: GPIO16 | |
| # sd_mmc_card.create_directory: | |
| # path: "/test" | |
| time: | |
| - platform: pcf85063 | |
| id: pcf85063_time | |
| # repeated synchronization is not necessary unless the external RTC | |
| # is much more accurate than the internal clock | |
| update_interval: never | |
| - platform: homeassistant | |
| # instead try to synchronize via network repeatedly ... | |
| on_time_sync: | |
| then: | |
| # ... and update the RTC when the synchronization was successful | |
| pcf85063.write_time: | |
| psram: | |
| mode: octal | |
| speed: 80MHz # 120MHz | |
| sensor: | |
| - platform: adc | |
| name: "Battery Voltage" | |
| attenuation: auto | |
| id: batvolt | |
| pin: GPIO8 | |
| accuracy_decimals: 3 | |
| device_class: "voltage" | |
| entity_category: "diagnostic" | |
| update_interval: 1s | |
| unit_of_measurement: "V" | |
| icon: mdi:battery-medium | |
| filters: | |
| - multiply: 3.09 | |
| - median: | |
| window_size: 7 | |
| send_every: 7 | |
| send_first_at: 7 | |
| - throttle: 15min | |
| on_value: | |
| then: | |
| - component.update: batpercent | |
| - platform: template | |
| name: "Battery level" | |
| id: batpercent | |
| lambda: return id(batvolt).state; | |
| accuracy_decimals: 0 | |
| unit_of_measurement: "%" | |
| icon: mdi:battery-medium | |
| device_class: "battery" | |
| entity_category: "diagnostic" | |
| filters: | |
| - calibrate_linear: | |
| method: exact | |
| datapoints: | |
| - 0.00 -> 0.0 | |
| - 3.30 -> 1.0 | |
| - 3.39 -> 10.0 | |
| - 3.75 -> 50.0 | |
| - 4.11 -> 90.0 | |
| - 4.20 -> 100.0 | |
| - clamp: | |
| min_value: 0 | |
| max_value: 100 | |
| ignore_out_of_range: false | |
| logger: | |
| api: | |
| encryption: | |
| key: !secret encryption_key | |
| on_client_connected: | |
| - script.execute: draw_display | |
| on_client_disconnected: | |
| - script.execute: draw_display | |
| ota: | |
| - platform: esphome | |
| id: ota_esphome | |
| password: !secret ota_password | |
| on_begin: | |
| then: | |
| - logger.log: "OTA started!" | |
| - lambda: |- | |
| id(ota_active) = true; | |
| - script.execute: draw_display | |
| - lambda: |- | |
| id(main_display).update(); | |
| wifi: | |
| ssid: !secret wifi_ssid | |
| password: !secret wifi_password | |
| # Enable fallback hotspot (captive portal) in case wifi connection fails | |
| ap: | |
| on_connect: | |
| - script.execute: draw_display | |
| on_disconnect: | |
| - script.execute: draw_display | |
| captive_portal: | |
| button: | |
| - platform: factory_reset | |
| id: factory_reset_btn | |
| internal: true | |
| web_server: | |
| port: 80 | |
| version: 2 | |
| include_internal: true | |
| i2s_audio: | |
| - id: i2s_in | |
| i2s_lrclk_pin: GPIO2 | |
| i2s_bclk_pin: GPIO15 | |
| - id: i2s_out | |
| i2s_lrclk_pin: GPIO38 | |
| i2s_bclk_pin: GPIO48 | |
| speaker: | |
| - platform: i2s_audio | |
| id: i2s_speaker | |
| i2s_audio_id: i2s_out | |
| i2s_dout_pin: GPIO47 | |
| dac_type: external | |
| channel: stereo | |
| microphone: | |
| - platform: i2s_audio | |
| id: i2s_microphone | |
| i2s_audio_id: i2s_in | |
| i2s_din_pin: GPIO39 | |
| adc_type: external | |
| pdm: false | |
| channel: right | |
| sample_rate: 16000 | |
| bits_per_sample: 16bit | |
| binary_sensor: | |
| - platform: gpio | |
| pin: | |
| number: GPIO0 | |
| mode: INPUT_PULLUP | |
| inverted: true | |
| name: "Boot Button" | |
| on_multi_click: | |
| - timing: | |
| - ON for at most 500ms | |
| - OFF for at least 200ms | |
| then: | |
| - if: | |
| condition: | |
| - not: | |
| - microphone.is_muted: | |
| then: | |
| - if: | |
| condition: | |
| - voice_assistant.is_running | |
| then: | |
| - voice_assistant.stop: | |
| else: | |
| - voice_assistant.start: | |
| - timing: | |
| - ON for at most 500ms | |
| - OFF for at most 400ms | |
| - ON for at most 500ms | |
| then: | |
| - if: | |
| condition: | |
| - microphone.is_muted: | |
| then: | |
| - microphone.unmute: | |
| - globals.set: | |
| id: mic_muted | |
| value: 'false' | |
| else: | |
| - microphone.mute: | |
| - globals.set: | |
| id: mic_muted | |
| value: 'true' | |
| - script.execute: draw_display | |
| - timing: | |
| - ON for at least 10s | |
| then: | |
| - button.press: factory_reset_btn | |
| i2c: | |
| sda: GPIO11 | |
| scl: GPIO10 | |
| pca9554: | |
| - id: 'pca9554a_device' | |
| output: | |
| - platform: ledc | |
| pin: GPIO5 | |
| id: backlight_output | |
| light: | |
| - platform: monochromatic | |
| name: "backlight Light" | |
| icon: "mdi:television" | |
| entity_category: config | |
| output: backlight_output | |
| restore_mode: RESTORE_DEFAULT_ON | |
| default_transition_length: 250ms | |
| spi: | |
| id: display_qspi | |
| type: quad | |
| clk_pin: GPIO40 | |
| data_pins: [GPIO46, GPIO45, GPIO42, GPIO41] | |
| touchscreen: | |
| platform: cst816 | |
| id: my_touchscreen | |
| interrupt_pin: GPIO4 | |
| reset_pin: | |
| pca9554: pca9554a_device | |
| number: 0 | |
| display: main_display | |
| on_touch: | |
| - lambda: |- | |
| ESP_LOGI("cal", "x=%d, y=%d, x_raw=%d, y_raw=%0d", | |
| touch.x, | |
| touch.y, | |
| touch.x_raw, | |
| touch.y_raw | |
| ); | |
| display: | |
| - platform: qspi_dbi | |
| model: CUSTOM | |
| data_rate: 40MHz | |
| invert_colors: true | |
| id: main_display | |
| spi_id: display_qspi | |
| color_order: rgb | |
| dimensions: | |
| height: 360 | |
| width: 360 | |
| cs_pin: GPIO21 | |
| reset_pin: | |
| pca9554: pca9554a_device | |
| number: 1 | |
| auto_clear_enabled: true #set to false for LVGL | |
| update_interval: never | |
| init_sequence: | |
| - [ 0xF0, 0x08 ] | |
| - [ 0xF2, 0x08 ] | |
| - [ 0x9B, 0x51 ] | |
| - [ 0x86, 0x53 ] | |
| - [ 0xF2, 0x80 ] | |
| - [ 0xF0, 0x00 ] | |
| - [ 0xF0, 0x01 ] | |
| - [ 0xF1, 0x01 ] | |
| - [ 0xB0, 0x54 ] | |
| - [ 0xB1, 0x3F ] | |
| - [ 0xB2, 0x2A ] | |
| - [ 0xB4, 0x46 ] | |
| - [ 0xB5, 0x34 ] | |
| - [ 0xB6, 0xD5 ] | |
| - [ 0xB7, 0x30 ] | |
| - [ 0xBA, 0x00 ] | |
| - [ 0xBB, 0x08 ] | |
| - [ 0xBC, 0x08 ] | |
| - [ 0xBD, 0x00 ] | |
| - [ 0xC0, 0x80 ] | |
| - [ 0xC1, 0x10 ] | |
| - [ 0xC2, 0x37 ] | |
| - [ 0xC3, 0x80 ] | |
| - [ 0xC4, 0x10 ] | |
| - [ 0xC5, 0x37 ] | |
| - [ 0xC6, 0xA9 ] | |
| - [ 0xC7, 0x41 ] | |
| - [ 0xC8, 0x51 ] | |
| - [ 0xC9, 0xA9 ] | |
| - [ 0xCA, 0x41 ] | |
| - [ 0xCB, 0x51 ] | |
| - [ 0xD0, 0x91 ] | |
| - [ 0xD1, 0x68 ] | |
| - [ 0xD2, 0x69 ] | |
| - [ 0xF5, 0x00, 0xA5 ] | |
| - [ 0xDD, 0x3F ] | |
| - [ 0xDE, 0x3F ] | |
| - [ 0xF1, 0x10 ] | |
| - [ 0xF0, 0x00 ] | |
| - [ 0xF0, 0x02 ] | |
| - [ 0xE0, 0xF0, 0x06, 0x0B, 0x09, 0x09, 0x16, 0x32, 0x44, 0x4A, 0x37, 0x13, 0x13, 0x2E, 0x34 ] | |
| - [ 0xE1, 0xF0, 0x06, 0x0B, 0x09, 0x08, 0x05, 0x32, 0x33, 0x49, 0x17, 0x13, 0x13, 0x2E, 0x34 ] | |
| - [ 0xF0, 0x10 ] | |
| - [ 0xF3, 0x10 ] | |
| - [ 0xE0, 0x0A ] | |
| - [ 0xE1, 0x00 ] | |
| - [ 0xE2, 0x00 ] | |
| - [ 0xE3, 0x00 ] | |
| - [ 0xE4, 0xE0 ] | |
| - [ 0xE5, 0x06 ] | |
| - [ 0xE6, 0x21 ] | |
| - [ 0xE7, 0x00 ] | |
| - [ 0xE8, 0x05 ] | |
| - [ 0xE9, 0x82 ] | |
| - [ 0xEA, 0xDF ] | |
| - [ 0xEB, 0x89 ] | |
| - [ 0xEC, 0x20 ] | |
| - [ 0xED, 0x14 ] | |
| - [ 0xEE, 0xFF ] | |
| - [ 0xEF, 0x00 ] | |
| - [ 0xF8, 0xFF ] | |
| - [ 0xF9, 0x00 ] | |
| - [ 0xFA, 0x00 ] | |
| - [ 0xFB, 0x30 ] | |
| - [ 0xFC, 0x00 ] | |
| - [ 0xFD, 0x00 ] | |
| - [ 0xFE, 0x00 ] | |
| - [ 0xFF, 0x00 ] | |
| - [ 0x60, 0x42 ] | |
| - [ 0x61, 0xE0 ] | |
| - [ 0x62, 0x40 ] | |
| - [ 0x63, 0x40 ] | |
| - [ 0x64, 0x02 ] | |
| - [ 0x65, 0x00 ] | |
| - [ 0x66, 0x40 ] | |
| - [ 0x67, 0x03 ] | |
| - [ 0x68, 0x00 ] | |
| - [ 0x69, 0x00 ] | |
| - [ 0x6A, 0x00 ] | |
| - [ 0x6B, 0x00 ] | |
| - [ 0x70, 0x42 ] | |
| - [ 0x71, 0xE0 ] | |
| - [ 0x72, 0x40 ] | |
| - [ 0x73, 0x40 ] | |
| - [ 0x74, 0x02 ] | |
| - [ 0x75, 0x00 ] | |
| - [ 0x76, 0x40 ] | |
| - [ 0x77, 0x03 ] | |
| - [ 0x78, 0x00 ] | |
| - [ 0x79, 0x00 ] | |
| - [ 0x7A, 0x00 ] | |
| - [ 0x7B, 0x00 ] | |
| - [ 0x80, 0x48 ] | |
| - [ 0x81, 0x00 ] | |
| - [ 0x82, 0x05 ] | |
| - [ 0x83, 0x02 ] | |
| - [ 0x84, 0xDD ] | |
| - [ 0x85, 0x00 ] | |
| - [ 0x86, 0x00 ] | |
| - [ 0x87, 0x00 ] | |
| - [ 0x88, 0x48 ] | |
| - [ 0x89, 0x00 ] | |
| - [ 0x8A, 0x07 ] | |
| - [ 0x8B, 0x02 ] | |
| - [ 0x8C, 0xDF ] | |
| - [ 0x8D, 0x00 ] | |
| - [ 0x8E, 0x00 ] | |
| - [ 0x8F, 0x00 ] | |
| - [ 0x90, 0x48 ] | |
| - [ 0x91, 0x00 ] | |
| - [ 0x92, 0x09 ] | |
| - [ 0x93, 0x02 ] | |
| - [ 0x94, 0xE1 ] | |
| - [ 0x95, 0x00 ] | |
| - [ 0x96, 0x00 ] | |
| - [ 0x97, 0x00 ] | |
| - [ 0x98, 0x48 ] | |
| - [ 0x99, 0x00 ] | |
| - [ 0x9A, 0x0B ] | |
| - [ 0x9B, 0x02 ] | |
| - [ 0x9C, 0xE3 ] | |
| - [ 0x9D, 0x00 ] | |
| - [ 0x9E, 0x00 ] | |
| - [ 0x9F, 0x00 ] | |
| - [ 0xA0, 0x48 ] | |
| - [ 0xA1, 0x00 ] | |
| - [ 0xA2, 0x04 ] | |
| - [ 0xA3, 0x02 ] | |
| - [ 0xA4, 0xDC ] | |
| - [ 0xA5, 0x00 ] | |
| - [ 0xA6, 0x00 ] | |
| - [ 0xA7, 0x00 ] | |
| - [ 0xA8, 0x48 ] | |
| - [ 0xA9, 0x00 ] | |
| - [ 0xAA, 0x06 ] | |
| - [ 0xAB, 0x02 ] | |
| - [ 0xAC, 0xDE ] | |
| - [ 0xAD, 0x00 ] | |
| - [ 0xAE, 0x00 ] | |
| - [ 0xAF, 0x00 ] | |
| - [ 0xB0, 0x48 ] | |
| - [ 0xB1, 0x00 ] | |
| - [ 0xB2, 0x08 ] | |
| - [ 0xB3, 0x02 ] | |
| - [ 0xB4, 0xE0 ] | |
| - [ 0xB5, 0x00 ] | |
| - [ 0xB6, 0x00 ] | |
| - [ 0xB7, 0x00 ] | |
| - [ 0xB8, 0x48 ] | |
| - [ 0xB9, 0x00 ] | |
| - [ 0xBA, 0x0A ] | |
| - [ 0xBB, 0x02 ] | |
| - [ 0xBC, 0xE2 ] | |
| - [ 0xBD, 0x00 ] | |
| - [ 0xBE, 0x00 ] | |
| - [ 0xBF, 0x00 ] | |
| - [ 0xC0, 0x12 ] | |
| - [ 0xC1, 0xAA ] | |
| - [ 0xC2, 0x65 ] | |
| - [ 0xC3, 0x74 ] | |
| - [ 0xC4, 0x47 ] | |
| - [ 0xC5, 0x56 ] | |
| - [ 0xC6, 0x00 ] | |
| - [ 0xC7, 0x88 ] | |
| - [ 0xC8, 0x99 ] | |
| - [ 0xC9, 0x33 ] | |
| - [ 0xD0, 0x21 ] | |
| - [ 0xD1, 0xAA ] | |
| - [ 0xD2, 0x65 ] | |
| - [ 0xD3, 0x74 ] | |
| - [ 0xD4, 0x47 ] | |
| - [ 0xD5, 0x56 ] | |
| - [ 0xD6, 0x00 ] | |
| - [ 0xD7, 0x88 ] | |
| - [ 0xD8, 0x99 ] | |
| - [ 0xD9, 0x33 ] | |
| - [ 0xF3, 0x01 ] | |
| - [ 0xF0, 0x00 ] | |
| - [ 0xF0, 0x01 ] | |
| - [ 0xF1, 0x01 ] | |
| - [ 0xA0, 0x0B ] | |
| - [ 0xA3, 0x2A ] | |
| - [ 0xA5, 0xC3 ] | |
| - delay 1ms | |
| - [ 0xA3, 0x2B ] | |
| - [ 0xA5, 0xC3 ] | |
| - delay 1ms | |
| - [ 0xA3, 0x2C ] | |
| - [ 0xA5, 0xC3 ] | |
| - delay 1ms | |
| - [ 0xA3, 0x2D ] | |
| - [ 0xA5, 0xC3 ] | |
| - delay 1ms | |
| - [ 0xA3, 0x2E ] | |
| - [ 0xA5, 0xC3 ] | |
| - delay 1ms | |
| - [ 0xA3, 0x2F ] | |
| - [ 0xA5, 0xC3 ] | |
| - delay 1ms | |
| - [ 0xA3, 0x30 ] | |
| - [ 0xA5, 0xC3 ] | |
| - delay 1ms | |
| - [ 0xA3, 0x31 ] | |
| - [ 0xA5, 0xC3 ] | |
| - delay 1ms | |
| - [ 0xA3, 0x32 ] | |
| - [ 0xA5, 0xC3 ] | |
| - delay 1ms | |
| - [ 0xA3, 0x33 ] | |
| - [ 0xA5, 0xC3 ] | |
| - delay 1ms | |
| - [ 0xA0, 0x09 ] | |
| - [ 0xF1, 0x10 ] | |
| - [ 0xF0, 0x00 ] | |
| - [ 0x2A, 0x00, 0x00, 0x01, 0x67 ] | |
| - [ 0x2B, 0x01, 0x68, 0x01, 0x68 ] | |
| - [ 0x4D, 0x00 ] | |
| - [ 0x4E, 0x00 ] | |
| - [ 0x4F, 0x00 ] | |
| - [ 0x4C, 0x01 ] | |
| - delay 10ms | |
| - [ 0x4C, 0x00 ] | |
| - [ 0x2A, 0x00, 0x00, 0x01, 0x67 ] | |
| - [ 0x2B, 0x00, 0x00, 0x01, 0x67 ] | |
| - [ 0x3A, 0x55 ] | |
| - [ 0x21, 0x00 ] | |
| - [ 0x11, 0x00 ] | |
| - delay 120ms | |
| - [ 0x29, 0x00 ] | |
| pages: | |
| - id: idle_page | |
| lambda: |- | |
| it.fill(id(idle_color)); | |
| if (id(ota_active)) { | |
| it.filled_circle(70, 50, 3, Color(255, 255, 0)); | |
| } | |
| if (id(mic_muted)) { | |
| it.filled_circle(290, 50, 3, Color(255, 0, 0)); | |
| } | |
| it.image((it.get_width() / 2), (it.get_height() / 2), id(casita_idle), ImageAlign::CENTER); | |
| id(draw_timer_timeline).execute(); | |
| id(draw_active_timer_widget).execute(); | |
| - 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(draw_timer_timeline).execute(); | |
| - id: thinking_page | |
| lambda: |- | |
| it.fill(id(thinking_color)); | |
| it.image((it.get_width() / 2), (it.get_height() / 2), id(casita_thinking), ImageAlign::CENTER); | |
| it.filled_rectangle(55 , 60 , 250 , 30 , Color::WHITE ); | |
| it.rectangle(55 , 60 , 250 , 30 , Color::BLACK ); | |
| it.printf(65, 65, id(font_request), Color::BLACK, "%s", id(text_request).state.c_str()); | |
| id(draw_timer_timeline).execute(); | |
| - id: replying_page | |
| lambda: |- | |
| it.fill(id(replying_color)); | |
| it.image((it.get_width() / 2), (it.get_height() / 2), id(casita_replying), ImageAlign::CENTER); | |
| it.filled_rectangle(55 , 60 , 250 , 30 , Color::WHITE ); | |
| it.rectangle(55 , 60 , 250 , 30 , Color::BLACK ); | |
| it.filled_rectangle(90 , 290 , 180 , 30 , Color::WHITE ); | |
| it.rectangle(90 , 290 , 180 , 30 , Color::BLACK ); | |
| it.printf(65, 65, id(font_request), Color::BLACK, "%s", id(text_request).state.c_str()); | |
| it.printf(100, 295, id(font_response), Color::BLACK, "%s", id(text_response).state.c_str()); | |
| id(draw_timer_timeline).execute(); | |
| - id: timer_finished_page | |
| lambda: |- | |
| it.fill(id(idle_color)); | |
| it.image((it.get_width() / 2), (it.get_height() / 2), id(casita_timer_finished), 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); | |
| id(draw_timer_timeline).execute(); | |
| id(draw_active_timer_widget).execute(); | |
| media_player: | |
| - platform: speaker | |
| name: None | |
| id: speaker_media_player | |
| volume_min: 0.5 | |
| volume_max: 0.9 | |
| announcement_pipeline: | |
| speaker: i2s_speaker | |
| format: FLAC | |
| sample_rate: 48000 | |
| files: | |
| - id: timer_finished_sound | |
| file: https://github.com/esphome/home-assistant-voice-pe/raw/dev/sounds/timer_finished.flac | |
| on_announcement: | |
| - if: | |
| condition: | |
| microphone.is_capturing: | |
| then: | |
| - script.execute: stop_voice_assistant | |
| - if: | |
| condition: | |
| not: | |
| voice_assistant.is_running: | |
| then: | |
| - lambda: id(voice_assistant_phase) = ${voice_assist_muted_phase_id}; | |
| - script.execute: draw_display | |
| on_idle: | |
| - script.execute: start_voice_assistant | |
| - script.execute: draw_display | |
| micro_wake_word: | |
| models: | |
| - ${micro_wake_word_model} | |
| on_wake_word_detected: | |
| - voice_assistant.start: | |
| wake_word: !lambda return wake_word; | |
| voice_assistant: | |
| id: va | |
| microphone: i2s_microphone | |
| use_wake_word: true | |
| noise_suppression_level: 2 #4 | |
| auto_gain: 31dBFS | |
| media_player: speaker_media_player | |
| volume_multiplier: 2.0 # 8.0 | |
| 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: | |
| and: | |
| - not: | |
| media_player.is_announcing: | |
| - not: | |
| voice_assistant.is_running: | |
| - 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 | |
| - if: | |
| condition: | |
| and: | |
| - switch.is_off: mute | |
| - lambda: return id(wake_word_engine_location).state == "On device"; | |
| - lambda: return id(voice_assistant_phase) != ${voice_assist_timer_finished_phase_id}; | |
| then: | |
| - micro_wake_word.start: | |
| 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_voice_assistant | |
| - script.execute: draw_display | |
| on_client_disconnected: | |
| - script.execute: stop_voice_assistant | |
| - 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 | |
| - wait_until: | |
| media_player.is_announcing: | |
| - lambda: id(voice_assistant_phase) = ${voice_assist_timer_finished_phase_id}; | |
| - script.execute: draw_display | |
| switch: | |
| - platform: gpio | |
| pin: GPIO18 | |
| id: lcd_TE | |
| - platform: template | |
| name: "Speaker Mute" | |
| id: speaker_mute_switch | |
| entity_category: config | |
| disabled_by_default: true | |
| turn_on_action: | |
| - speaker.mute_on: | |
| - globals.set: | |
| id: speaker_muted | |
| value: 'true' | |
| - logger.log: "Speaker muted" | |
| turn_off_action: | |
| - speaker.mute_off: | |
| - globals.set: | |
| id: speaker_muted | |
| value: 'false' | |
| - logger.log: "Speaker unmuted" | |
| restore_mode: RESTORE_DEFAULT_OFF | |
| - platform: template | |
| name: "Microphone Mute" | |
| id: mute | |
| icon: "mdi:microphone-off" | |
| optimistic: true | |
| restore_mode: RESTORE_DEFAULT_OFF | |
| entity_category: config | |
| on_turn_off: | |
| - if: | |
| condition: | |
| lambda: return !id(init_in_progress); | |
| then: | |
| - lambda: id(voice_assistant_phase) = ${voice_assist_idle_phase_id}; | |
| - if: | |
| condition: | |
| not: | |
| - voice_assistant.is_running | |
| then: | |
| - if: | |
| condition: | |
| lambda: return id(wake_word_engine_location).state == "In Home Assistant"; | |
| then: | |
| - lambda: id(va).set_use_wake_word(true); | |
| - voice_assistant.start_continuous | |
| - 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: draw_display | |
| on_turn_on: | |
| - if: | |
| condition: | |
| lambda: return !id(init_in_progress); | |
| then: | |
| - lambda: id(va).set_use_wake_word(false); | |
| - voice_assistant.stop | |
| - micro_wake_word.stop | |
| - 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: | |
| # Turn off the repeat mode and disable the pause between playlist items | |
| - lambda: |- | |
| id(speaker_media_player) | |
| ->make_call() | |
| .set_command(media_player::MediaPlayerCommand::MEDIA_PLAYER_COMMAND_REPEAT_OFF) | |
| .set_announcement(true) | |
| .perform(); | |
| id(speaker_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(speaker_media_player) | |
| ->make_call() | |
| .set_command(media_player::MediaPlayerCommand::MEDIA_PLAYER_COMMAND_REPEAT_ONE) | |
| .set_announcement(true) | |
| .perform(); | |
| id(speaker_media_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 | |
| globals: | |
| - id: ota_active | |
| type: bool | |
| initial_value: 'false' | |
| - id: mic_muted | |
| type: bool | |
| restore_value: no | |
| initial_value: 'false' | |
| - id: speaker_muted | |
| type: bool | |
| restore_value: no | |
| initial_value: 'false' | |
| - 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 | |
| image: | |
| - file: ${error_illustration_file} | |
| id: casita_error | |
| resize: 320x240 | |
| type: RGB | |
| transparency: alpha_channel | |
| - file: ${idle_illustration_file} | |
| id: casita_idle | |
| resize: 320x240 | |
| type: RGB | |
| transparency: alpha_channel | |
| - file: ${listening_illustration_file} | |
| id: casita_listening | |
| resize: 320x240 | |
| type: RGB | |
| transparency: alpha_channel | |
| - file: ${thinking_illustration_file} | |
| id: casita_thinking | |
| resize: 320x240 | |
| type: RGB | |
| transparency: alpha_channel | |
| - file: ${replying_illustration_file} | |
| id: casita_replying | |
| resize: 320x240 | |
| type: RGB | |
| transparency: alpha_channel | |
| - file: ${timer_finished_illustration_file} | |
| id: casita_timer_finished | |
| resize: 320x240 | |
| type: RGB | |
| transparency: alpha_channel | |
| - file: ${loading_illustration_file} | |
| id: casita_initializing | |
| resize: 320x240 | |
| type: RGB | |
| 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: 320x240 | |
| type: RGB | |
| 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: 320x240 | |
| type: RGB | |
| 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} | |
| - file: | |
| type: gfonts | |
| family: ${font_family} | |
| weight: 300 | |
| id: font_timer | |
| size: 30 | |
| glyphsets: | |
| - ${font_glyphsets} | |
| text_sensor: | |
| - id: text_request | |
| platform: template | |
| on_value: | |
| lambda: |- | |
| if(id(text_request).state.length()>32) { | |
| 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 | |
| on_value: | |
| lambda: |- | |
| if(id(text_response).state.length()>32) { | |
| 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} | |
| - id: active_timer_color | |
| hex: "26ed3a" | |
| - id: paused_timer_color | |
| hex: "3b89e3" | |
| 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: fetch_first_active_timer | |
| then: | |
| - lambda: | | |
| const auto timers = id(va).get_timers(); | |
| auto output_timer = timers.begin()->second; | |
| for (auto &iterable_timer : timers) { | |
| if (iterable_timer.second.is_active && iterable_timer.second.seconds_left <= output_timer.seconds_left) { | |
| output_timer = iterable_timer.second; | |
| } | |
| } | |
| id(global_first_active_timer) = output_timer; | |
| - id: check_if_timers_active | |
| then: | |
| - lambda: | | |
| const auto timers = id(va).get_timers(); | |
| bool output = false; | |
| if (timers.size() > 0) { | |
| for (auto &iterable_timer : timers) { | |
| if(iterable_timer.second.is_active) { | |
| output = true; | |
| } | |
| } | |
| } | |
| id(global_is_timer_active) = output; | |
| - id: fetch_first_timer | |
| then: | |
| - lambda: | | |
| const auto timers = id(va).get_timers(); | |
| auto output_timer = timers.begin()->second; | |
| for (auto &iterable_timer : timers) { | |
| if (iterable_timer.second.seconds_left <= output_timer.seconds_left) { | |
| output_timer = iterable_timer.second; | |
| } | |
| } | |
| id(global_first_timer) = output_timer; | |
| - id: check_if_timers | |
| then: | |
| - lambda: | | |
| const auto timers = id(va).get_timers(); | |
| bool output = false; | |
| if (timers.size() > 0) { | |
| output = true; | |
| } | |
| id(global_is_timer) = output; | |
| - id: draw_timer_timeline | |
| then: | |
| - lambda: | | |
| id(check_if_timers_active).execute(); | |
| id(check_if_timers).execute(); | |
| if (id(global_is_timer_active)){ | |
| id(fetch_first_active_timer).execute(); | |
| int active_pixels = round( 320 * id(global_first_active_timer).seconds_left / max(id(global_first_active_timer).total_seconds , static_cast<uint32_t>(1)) ); | |
| if (active_pixels > 0){ | |
| id(main_display).filled_rectangle(0 , 225 , 320 , 15 , Color::WHITE ); | |
| id(main_display).filled_rectangle(0 , 226 , active_pixels , 13 , id(active_timer_color) ); | |
| } | |
| } else if (id(global_is_timer)){ | |
| id(fetch_first_timer).execute(); | |
| int active_pixels = round( 320 * id(global_first_timer).seconds_left / max(id(global_first_timer).total_seconds , static_cast<uint32_t>(1))); | |
| if (active_pixels > 0){ | |
| id(main_display).filled_rectangle(0 , 225 , 320 , 15 , Color::WHITE ); | |
| id(main_display).filled_rectangle(0 , 226 , active_pixels , 13 , id(paused_timer_color) ); | |
| } | |
| } | |
| - id: draw_active_timer_widget | |
| then: | |
| - lambda: | | |
| id(check_if_timers_active).execute(); | |
| if (id(global_is_timer_active)){ | |
| id(main_display).filled_rectangle(80 , 40 , 160 , 50 , Color::WHITE ); | |
| id(main_display).rectangle(80 , 40 , 160 , 50 , Color::BLACK ); | |
| id(fetch_first_active_timer).execute(); | |
| int hours_left = floor(id(global_first_active_timer).seconds_left / 3600); | |
| int minutes_left = floor((id(global_first_active_timer).seconds_left - hours_left * 3600) / 60); | |
| int seconds_left = id(global_first_active_timer).seconds_left - hours_left * 3600 - minutes_left * 60 ; | |
| auto display_hours = (hours_left < 10 ? "0" : "") + std::to_string(hours_left); | |
| auto display_minute = (minutes_left < 10 ? "0" : "") + std::to_string(minutes_left); | |
| auto display_seconds = (seconds_left < 10 ? "0" : "") + std::to_string(seconds_left) ; | |
| std::string display_string = ""; | |
| if (hours_left > 0) { | |
| display_string = display_hours + ":" + display_minute; | |
| } else { | |
| display_string = display_minute + ":" + display_seconds; | |
| } | |
| id(main_display).printf(120, 47, id(font_timer), Color::BLACK, "%s", display_string.c_str()); | |
| } | |
| - id: start_voice_assistant | |
| then: | |
| - if: | |
| condition: | |
| switch.is_off: mute | |
| then: | |
| - if: | |
| condition: | |
| lambda: return id(wake_word_engine_location).state == "In Home Assistant"; | |
| then: | |
| - lambda: id(va).set_use_wake_word(true); | |
| - voice_assistant.start_continuous: | |
| - 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 | |
| - lambda: id(voice_assistant_phase) = ${voice_assist_idle_phase_id}; | |
| else: | |
| - lambda: id(voice_assistant_phase) = ${voice_assist_muted_phase_id}; | |
| - id: stop_voice_assistant | |
| 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: | |
| - voice_assistant.stop: | |
| - micro_wake_word.stop: |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment