diff --git a/.github/workflows/build-esphome-configs.yml b/.github/workflows/build-esphome-configs.yml index c9901c6..965d04c 100644 --- a/.github/workflows/build-esphome-configs.yml +++ b/.github/workflows/build-esphome-configs.yml @@ -8,11 +8,13 @@ on: - 'firmware/esphome/**' - '.github/workflows/build-esphome.yml' - '!firmware/esphome/archive/**' + - '!firmware/esphome/packages-archive/**' pull_request: paths: - 'firmware/esphome/**' - '.github/workflows/build-esphome.yml' - '!firmware/esphome/archive/**' + - '!firmware/esphome/packages-archive/**' jobs: prepare: @@ -29,6 +31,7 @@ jobs: configs=$(find firmware/esphome -maxdepth 2 -name "*.yaml" -type f \ | grep -v "/packages/" \ | grep -v "/archive/" \ + | grep -v "/packages-archive/" \ | grep -v "/secrets/" \ | grep -v "secrets.yaml" \ | jq -R -s -c 'split("\n")[:-1]') @@ -77,5 +80,5 @@ jobs: -v "${{ github.workspace }}/firmware/esphome/packages:/config/packages" \ -v "${{ github.workspace }}/.esphome-cache:/cache" \ -e PLATFORMIO_BUILD_CACHE_DIR=/cache/.pio-cache \ - esphome/esphome:latest \ + esphome/esphome:dev \ compile "/config/${{ steps.config-info.outputs.config_file }}" \ No newline at end of file diff --git a/firmware/esphome/archive/echo-duo-a-media-player.yaml b/firmware/esphome/archive/echo-duo-a-media-player.yaml deleted file mode 100644 index 2fb05d0..0000000 --- a/firmware/esphome/archive/echo-duo-a-media-player.yaml +++ /dev/null @@ -1,166 +0,0 @@ -substitutions: - name: esphome-web-47a48c - friendly_name: esparagus-duo-01 - -esphome: - name: ${name} - friendly_name: ${friendly_name} - project: - name: esphome.web - version: '1.0' - on_boot: - priority: 800 - then: - - media_player.volume_set: - id: esparagus_duo - volume: 70% - -esp32: - board: esp32-s3-devkitc-1 - variant: ESP32S3 - flash_size: 16MB - framework: - type: arduino - -# Enable logging -logger: - level: DEBUG - -# Enable Home Assistant API -api: - -# Allow Over-The-Air updates -ota: - platform: esphome - password: !secret esphome_ota_password - -wifi: - ssid: !secret esphome_wifi_ssid - password: !secret esphome_wifi_password - ap: - ssid: "$name Hotspot" - password: !secret esphome_ap_password - -captive_portal: - -# uncomment to use ethernet instead of wifi - -# ethernet: -# type: W5500 -# clk_pin: GPIO12 -# mosi_pin: GPIO11 -# miso_pin: GPIO13 -# cs_pin: GPIO10 -# interrupt_pin: GPIO6 -# reset_pin: GPIO5 - -psram: - mode: octal - speed: 80MHz - -i2s_audio: - i2s_lrclk_pin: GPIO15 - i2s_bclk_pin: GPIO14 - -switch: - - platform: gpio - id: speaker_switch - icon: "mdi:speaker" - pin: - number: GPIO17 - -media_player: - - platform: i2s_audio - name: $friendly_name - id: esparagus_duo - dac_type: external - i2s_dout_pin: GPIO16 - mode: stereo - on_announcement: - - logger.log: "Announcement started" - - switch.turn_on: - id: speaker_switch - - light.turn_on: - id: rgb_front_led - blue: 100% - red: 0% - green: 100% - brightness: 50% - effect: "Fast Pulse" - on_play: - - logger.log: "Playback started" - - switch.turn_on: - id: speaker_switch - - light.turn_on: - id: rgb_front_led - blue: 100% - red: 0% - green: 100% - brightness: 50% - effect: "Slow Pulse" - on_pause: - - logger.log: "Playback paused" - - light.turn_on: - id: rgb_front_led - blue: 100% - red: 0% - green: 100% - brightness: 25% - on_idle: - - logger.log: "Playback ended" - - switch.turn_off: - id: speaker_switch - - light.turn_off: - id: rgb_front_led - -microphone: - - platform: i2s_audio - id: external_mic - adc_type: external - i2s_din_pin: GPIO21 - pdm: false - channel: right - sample_rate: 16000 - bits_per_sample: 32bit - -button: - - platform: restart - id: restart_btn - name: "${friendly_name} REBOOT" - -light: - - platform: esp32_rmt_led_strip - id: rgb_front_led - name: "${friendly_name} Front LED" - rgb_order: GRB - pin: GPIO42 - num_leds: 3 - rmt_channel: 1 - chipset: ws2812 - default_transition_length: 0s - effects: - - addressable_color_wipe: - name: "Connecting" - colors: - - red: 60% - green: 60% - blue: 60% - num_leds: 1 - - red: 60% - green: 60% - blue: 0% - num_leds: 1 - add_led_interval: 100ms - reverse: true - - pulse: - name: "Fast Pulse" - transition_length: 100ms - update_interval: 100ms - min_brightness: 50% - max_brightness: 100% - - pulse: - name: "Slow Pulse" - transition_length: 1000ms - update_interval: 1000ms - min_brightness: 50% - max_brightness: 100% \ No newline at end of file diff --git a/firmware/esphome/archive/echo-duo-b-media-player.yaml b/firmware/esphome/archive/echo-duo-b-media-player.yaml deleted file mode 100644 index 6990ccf..0000000 --- a/firmware/esphome/archive/echo-duo-b-media-player.yaml +++ /dev/null @@ -1,172 +0,0 @@ -substitutions: - name: esphome-web-32fb44 - friendly_name: esparagus-duo-B01 - -esphome: - name: ${name} - friendly_name: ${friendly_name} - project: - name: esphome.web - version: '1.0' - on_boot: - priority: 800 - then: - - media_player.volume_set: - id: esparagus_duo - volume: 70% - -esp32: - board: esp32-s3-devkitc-1 - variant: ESP32S3 - flash_size: 16MB - framework: - type: arduino - -# Enable logging -logger: - level: DEBUG - -# Enable Home Assistant API -api: - -# Allow Over-The-Air updates -ota: - platform: esphome - password: !secret esphome_ota_password - -wifi: - ssid: !secret esphome_wifi_ssid - password: !secret esphome_wifi_password - ap: - ssid: "$name Hotspot" - password: !secret esphome_ap_password - -captive_portal: - -# uncomment to use ethernet instead of wifi - -# ethernet: -# type: W5500 -# clk_pin: GPIO12 -# mosi_pin: GPIO11 -# miso_pin: GPIO13 -# cs_pin: GPIO10 -# interrupt_pin: GPIO6 -# reset_pin: GPIO5 - -psram: - mode: octal - speed: 80MHz - -i2s_audio: - - id: i2s_in - i2s_lrclk_pin: GPIO7 - i2s_bclk_pin: GPIO16 - - id: i2s_out - i2s_lrclk_pin: GPIO8 - i2s_bclk_pin: GPIO18 - -switch: - - platform: gpio - id: speaker_switch - icon: "mdi:speaker" - pin: - number: GPIO9 - -media_player: - - platform: i2s_audio - i2s_audio_id: i2s_out - name: $friendly_name - id: esparagus_duo - dac_type: external - i2s_dout_pin: GPIO17 - mode: stereo - on_announcement: - - logger.log: "Announcement started" - - switch.turn_on: - id: speaker_switch - - light.turn_on: - id: rgb_front_led - blue: 100% - red: 0% - green: 100% - brightness: 50% - effect: "Fast Pulse" - on_play: - - logger.log: "Playback started" - - switch.turn_on: - id: speaker_switch - - light.turn_on: - id: rgb_front_led - blue: 100% - red: 0% - green: 100% - brightness: 50% - effect: "Slow Pulse" - on_pause: - - logger.log: "Playback paused" - - light.turn_on: - id: rgb_front_led - blue: 100% - red: 0% - green: 100% - brightness: 25% - on_idle: - - logger.log: "Playback ended" - - switch.turn_off: - id: speaker_switch - - light.turn_off: - id: rgb_front_led - -microphone: - - platform: i2s_audio - id: external_mic - i2s_audio_id: i2s_in - adc_type: external - i2s_din_pin: GPIO15 - pdm: false - channel: right - sample_rate: 16000 - bits_per_sample: 32bit - -button: - - platform: restart - id: restart_btn - name: "${friendly_name} REBOOT" - -light: - - platform: esp32_rmt_led_strip - id: rgb_front_led - name: "${friendly_name} Front LED" - rgb_order: GRB - pin: GPIO42 - num_leds: 3 - rmt_channel: 1 - chipset: ws2812 - default_transition_length: 0s - effects: - - addressable_color_wipe: - name: "Connecting" - colors: - - red: 60% - green: 60% - blue: 60% - num_leds: 1 - - red: 60% - green: 60% - blue: 0% - num_leds: 1 - add_led_interval: 100ms - reverse: true - - pulse: - name: "Fast Pulse" - transition_length: 100ms - update_interval: 100ms - min_brightness: 50% - max_brightness: 100% - - pulse: - name: "Slow Pulse" - transition_length: 1000ms - update_interval: 1000ms - min_brightness: 50% - max_brightness: 100% \ No newline at end of file diff --git a/firmware/esphome/archive/echo-solo-a-media-player.yaml b/firmware/esphome/archive/echo-solo-a-media-player.yaml deleted file mode 100644 index 982b6b9..0000000 --- a/firmware/esphome/archive/echo-solo-a-media-player.yaml +++ /dev/null @@ -1,165 +0,0 @@ -substitutions: - name: esphome-web-01972c - friendly_name: esparagus-solo-01 - -esphome: - name: ${name} - friendly_name: ${friendly_name} - project: - name: esphome.web - version: '1.0' - platformio_options: - build_flags: "-DBOARD_HAS_PSRAM" - board_build.arduino.memory_type: qio_opi - on_boot: - priority: 800 - then: - - media_player.volume_set: - id: esparagus_solo - volume: 70% - -esp32: - board: esp32-s3-devkitc-1 - variant: ESP32S3 - flash_size: 16MB - framework: - type: arduino - -# Enable logging -logger: - level: DEBUG - -# Enable Home Assistant API -api: - -# Allow Over-The-Air updates -ota: - platform: esphome - password: !secret esphome_ota_password - -wifi: - ssid: !secret esphome_wifi_ssid - password: !secret esphome_wifi_password - ap: - ssid: "$name Hotspot" - password: !secret esphome_ap_password - -captive_portal: - -psram: - mode: octal - speed: 80MHz - -i2s_audio: - - id: i2s_in - i2s_lrclk_pin: GPIO7 - i2s_bclk_pin: GPIO16 - - id: i2s_out - i2s_lrclk_pin: GPIO8 - i2s_bclk_pin: GPIO18 - -switch: - - platform: gpio - id: speaker_switch - icon: "mdi:speaker" - pin: - number: GPIO9 - -media_player: - - platform: i2s_audio - i2s_audio_id: i2s_out - name: $friendly_name - id: esparagus_solo - dac_type: external - i2s_dout_pin: GPIO17 - mode: mono - on_announcement: - - logger.log: "Announcement started" - - switch.turn_on: - id: speaker_switch - - light.turn_on: - id: rgb_front_led - blue: 100% - red: 0% - green: 100% - brightness: 50% - effect: "Fast Pulse" - on_play: - - logger.log: "Playback started" - - switch.turn_on: - id: speaker_switch - - light.turn_on: - id: rgb_front_led - blue: 100% - red: 0% - green: 100% - brightness: 50% - effect: "Slow Pulse" - on_pause: - - logger.log: "Playback paused" - - light.turn_on: - id: rgb_front_led - blue: 100% - red: 0% - green: 100% - brightness: 25% - on_idle: - - logger.log: "Playback ended" - - switch.turn_off: - id: speaker_switch - - light.turn_off: - id: rgb_front_led - - -microphone: - - platform: i2s_audio - id: external_mic - i2s_audio_id: i2s_in - adc_type: external - i2s_din_pin: GPIO15 - pdm: false - channel: right - sample_rate: 16000 - bits_per_sample: 32bit - -button: - - platform: restart - id: restart_btn - name: "${friendly_name} REBOOT" - -light: - - platform: esp32_rmt_led_strip - id: rgb_front_led - name: "${friendly_name} Front LED" - rgb_order: GRB - pin: GPIO42 - num_leds: 2 - rmt_channel: 1 - chipset: ws2812 - default_transition_length: 0s - effects: - - addressable_color_wipe: - name: "Connecting" - colors: - - red: 60% - green: 60% - blue: 60% - num_leds: 1 - - red: 60% - green: 60% - blue: 0% - num_leds: 1 - add_led_interval: 100ms - reverse: true - - pulse: - name: "Fast Pulse" - transition_length: 100ms - update_interval: 100ms - min_brightness: 50% - max_brightness: 100% - - pulse: - name: "Slow Pulse" - transition_length: 1000ms - update_interval: 1000ms - min_brightness: 50% - max_brightness: 100% diff --git a/firmware/esphome/echo-duo-a/echo-duo-a-idf-media-player.yaml b/firmware/esphome/echo-duo-a/echo-duo-a-idf-media-player.yaml new file mode 100644 index 0000000..7e8452c --- /dev/null +++ b/firmware/esphome/echo-duo-a/echo-duo-a-idf-media-player.yaml @@ -0,0 +1,117 @@ + +# ============================================================ +# Esparagus-Echo - Media Player Configuration +# ============================================================ +# ESP32-S3 audio board with dual MAX98357A DAC and onboards I2S Microphone +# Features: Wi-Fi media player with mixer/resampler, +# announcements, RGB LED +# Board-specific pinouts defined in substitutions +# Functional blocks imported from /packages +# ============================================================ + +substitutions: + name: esphome-web-47a438 + friendly_name: Esparagus-Echo-Duo + task_stack_in_psram: "true" + + # ===== Board-Specific Pinouts ===== + # I2S pins + i2s_lrclk_pin: GPIO15 + i2s_bclk_pin: GPIO14 + i2s_dout_pin: GPIO16 + i2s_mic_din_pin: GPIO21 + i2s_mic_bus_id: i2s_out + + # DAC enable pins + dac_enable_pin: GPIO17 + dac_enable_restore_mode: ALWAYS_OFF + + # RGB LED + rgb_led_pin: GPIO42 + num_leds: 3 + + # SPI pins (for ethernet) + spi_clk_pin: GPIO12 + spi_mosi_pin: GPIO11 + spi_miso_pin: GPIO13 + + # Ethernet pins (if using ethernet instead of wifi) + eth_cs_pin: GPIO10 + eth_interrupt_pin: GPIO6 + eth_reset_pin: GPIO5 + +esphome: + name: ${name} + friendly_name: ${friendly_name} + min_version: 2026.2.1 + name_add_mac_suffix: false + platformio_options: + board_build.flash_mode: dio + +esp32: + variant: ESP32S3 + flash_size: 8MB + cpu_frequency: 240MHz + framework: + type: esp-idf + version: recommended + sdkconfig_options: + CONFIG_ESP32S3_DATA_CACHE_64KB: "y" + CONFIG_ESP32S3_DATA_CACHE_LINE_64B: "y" + CONFIG_ESP32S3_INSTRUCTION_CACHE_32KB: "y" + # Moves instructions and read only data from flash into PSRAM on boot. + # Both enabled allows instructions to execute while a flash operation is in progress without needing to be placed in IRAM. + # Considerably speeds up mWW at the cost of using more PSRAM. + CONFIG_SPIRAM_RODATA: "y" + CONFIG_SPIRAM_FETCH_INSTRUCTIONS: "y" + CONFIG_BT_ALLOCATION_FROM_SPIRAM_FIRST: "y" + CONFIG_BT_BLE_DYNAMIC_ENV_MEMORY: "y" + CONFIG_MBEDTLS_EXTERNAL_MEM_ALLOC: "y" + CONFIG_MBEDTLS_SSL_PROTO_TLS1_3: "y" + +logger: + level: DEBUG + +api: + +ota: + platform: esphome + password: !secret esphome_ota_password + +wifi: + ssid: !secret esphome_wifi_ssid + password: !secret esphome_wifi_password + +psram: + mode: octal + speed: 80MHz + ignore_not_found: false + +network: + enable_ipv6: true + +# ===== Packages ===== +packages: + esparagus_remote: + url: https://github.com/sonocotta/esparagus-media-center + ref: main + files: + - firmware/esphome/packages/dac-switch.yaml + - firmware/esphome/packages/light-x.yaml + - firmware/esphome/packages/monitoring.yaml + - firmware/esphome/packages/monitoring-wifi.yaml + # Optional ethernet (replace wifi config if you use this, you need to exclude OLED package if you do this to free up SPI bus) + # - firmware/esphome/packages/ethernet-w5500.yaml + + esparagus_echo: + url: https://github.com/sonocotta/esparagus-echo + ref: main + files: + - firmware/esphome/packages/audio.yaml + - firmware/esphome/packages/media-player.yaml + - firmware/esphome/packages/media-player-addon-dac-enable.yaml + + # # Debug/local development fallback: + # audio: !include packages/audio.yaml + # media_player: !include packages/media-player.yaml + # media_player_dac_enable: !include packages/media-player-addon-dac-enable.yaml diff --git a/firmware/esphome/echo-duo-a/echo-duo-a-idf-sendspin.yaml b/firmware/esphome/echo-duo-a/echo-duo-a-idf-sendspin.yaml new file mode 100644 index 0000000..cd9ae7c --- /dev/null +++ b/firmware/esphome/echo-duo-a/echo-duo-a-idf-sendspin.yaml @@ -0,0 +1,114 @@ + +# ============================================================ +# Esparagus-Echo - Sendspin Configuration +# ============================================================ +# ESP32-S3 audio board with dual MAX98357A DAC and onboards I2S Microphone +# Configured with Sendspin for synchronized multi-room audio +# Board-specific pinouts defined in substitutions +# Functional blocks imported from /packages +# ============================================================ + +substitutions: + name: esphome-web-47a438 + friendly_name: Esparagus-Echo-Duo + task_stack_in_psram: "true" + + # ===== Board-Specific Pinouts ===== + # I2S pins + i2s_lrclk_pin: GPIO15 + i2s_bclk_pin: GPIO14 + i2s_dout_pin: GPIO16 + + # DAC enable pins + dac_enable_pin: GPIO17 + dac_enable_restore_mode: ALWAYS_OFF + + # RGB LED + rgb_led_pin: GPIO42 + num_leds: 3 + + # SPI pins (for ethernet) + spi_clk_pin: GPIO12 + spi_mosi_pin: GPIO11 + spi_miso_pin: GPIO13 + + # Ethernet pins (if using ethernet instead of wifi) + eth_cs_pin: GPIO10 + eth_interrupt_pin: GPIO6 + eth_reset_pin: GPIO5 + +esphome: + name: ${name} + friendly_name: ${friendly_name} + min_version: 2026.5.0 + name_add_mac_suffix: false + platformio_options: + board_build.flash_mode: dio + +esp32: + variant: ESP32S3 + flash_size: 8MB + cpu_frequency: 240MHz + framework: + type: esp-idf + version: recommended + sdkconfig_options: + CONFIG_ESP32S3_DATA_CACHE_64KB: "y" + CONFIG_ESP32S3_DATA_CACHE_LINE_64B: "y" + CONFIG_ESP32S3_INSTRUCTION_CACHE_32KB: "y" + # Moves instructions and read only data from flash into PSRAM on boot. + # Both enabled allows instructions to execute while a flash operation is in progress without needing to be placed in IRAM. + # Considerably speeds up mWW at the cost of using more PSRAM. + CONFIG_SPIRAM_RODATA: "y" + CONFIG_SPIRAM_FETCH_INSTRUCTIONS: "y" + CONFIG_BT_ALLOCATION_FROM_SPIRAM_FIRST: "y" + CONFIG_BT_BLE_DYNAMIC_ENV_MEMORY: "y" + CONFIG_MBEDTLS_EXTERNAL_MEM_ALLOC: "y" + CONFIG_MBEDTLS_SSL_PROTO_TLS1_3: "y" + +logger: + level: DEBUG + +api: + +ota: + platform: esphome + password: !secret esphome_ota_password + +wifi: + ssid: !secret esphome_wifi_ssid + password: !secret esphome_wifi_password + +psram: + mode: octal + speed: 80MHz + ignore_not_found: false + +network: + enable_ipv6: true + +# ===== Packages ===== +packages: + esparagus_remote: + url: https://github.com/sonocotta/esparagus-media-center + ref: main + files: + - firmware/esphome/packages/dac-switch.yaml + - firmware/esphome/packages/light-x.yaml + - firmware/esphome/packages/monitoring.yaml + - firmware/esphome/packages/monitoring-wifi.yaml + # Optional ethernet (replace wifi config if you use this, you need to exclude OLED package if you do this to free up SPI bus) + # - firmware/esphome/packages/ethernet-w5500.yaml + + esparagus_echo: + url: https://github.com/sonocotta/esparagus-echo + ref: main + files: + - firmware/esphome/packages/audio.yaml + - firmware/esphome/packages/sendspin.yaml + - firmware/esphome/packages/sendspin-addon-dac-enable.yaml + + # # Debug/local development fallback: + # audio: !include packages/audio.yaml + # sendspin: !include packages/sendspin.yaml + # sendspin_addon_dac_enable: !include packages/sendspin-addon-dac-enable.yaml \ No newline at end of file diff --git a/firmware/esphome/echo-duo-a/echo-duo-a-idf-snapclient-with-dsp.yaml b/firmware/esphome/echo-duo-a/echo-duo-a-idf-snapclient-with-dsp.yaml new file mode 100644 index 0000000..7b60283 --- /dev/null +++ b/firmware/esphome/echo-duo-a/echo-duo-a-idf-snapclient-with-dsp.yaml @@ -0,0 +1,114 @@ + +# ============================================================ +# Esparagus-Echo - Snapclient Configuration +# ============================================================ +# ESP32-S3 audio board with dual MAX98357A DAC and onboards I2S Microphone +# Configured as a Snapcast client for synchronized audio +# There are two implementations available: +# 2) snapclient with added DSP controls (farmed-switch fork of c-MM port) +# Frequencies: 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, +# 140, 200, 315, 500, 800, 1250, 2000, 5000 Hz +# Range: -15dB to +15dB per band +# Board-specific pinouts defined in substitutions +# Functional blocks imported from /packages +# ============================================================ + +substitutions: + name: esphome-web-47a438 + friendly_name: Esparagus-Echo-Duo + task_stack_in_psram: "true" + + # ===== Board-Specific Pinouts ===== + # I2S pins + i2s_lrclk_pin: GPIO15 + i2s_bclk_pin: GPIO14 + i2s_dout_pin: GPIO16 + + # DAC enable pins + dac_enable_pin: GPIO17 + dac_enable_restore_mode: ALWAYS_ON + + # RGB LED + rgb_led_pin: GPIO42 + num_leds: 3 + + # SPI pins (for ethernet) + spi_clk_pin: GPIO12 + spi_mosi_pin: GPIO11 + spi_miso_pin: GPIO13 + + # Ethernet pins (if using ethernet instead of wifi) + eth_cs_pin: GPIO10 + eth_interrupt_pin: GPIO6 + eth_reset_pin: GPIO5 + + # ===== Snapcast Server Configuration ===== + snapcast_server_ip: "192.168.1.49" + snapcast_server_port: "1704" + +esphome: + name: ${name} + friendly_name: ${friendly_name} + min_version: 2026.2.1 + name_add_mac_suffix: false + platformio_options: + board_build.flash_mode: dio + + +esp32: + variant: ESP32S3 + flash_size: 8MB + cpu_frequency: 240MHz + framework: + type: esp-idf + version: recommended + sdkconfig_options: + CONFIG_ESP32S3_DATA_CACHE_64KB: "y" + CONFIG_ESP32S3_DATA_CACHE_LINE_64B: "y" + CONFIG_ESP32S3_INSTRUCTION_CACHE_32KB: "y" + # Moves instructions and read only data from flash into PSRAM on boot. + # Both enabled allows instructions to execute while a flash operation is in progress without needing to be placed in IRAM. + # Considerably speeds up mWW at the cost of using more PSRAM. + CONFIG_SPIRAM_RODATA: "y" + CONFIG_SPIRAM_FETCH_INSTRUCTIONS: "y" + CONFIG_BT_ALLOCATION_FROM_SPIRAM_FIRST: "y" + CONFIG_BT_BLE_DYNAMIC_ENV_MEMORY: "y" + CONFIG_MBEDTLS_EXTERNAL_MEM_ALLOC: "y" + CONFIG_MBEDTLS_SSL_PROTO_TLS1_3: "y" + +logger: + level: DEBUG + +api: + +ota: + platform: esphome + password: !secret esphome_ota_password + +wifi: + ssid: !secret esphome_wifi_ssid + password: !secret esphome_wifi_password + +psram: + mode: octal + speed: 80MHz + ignore_not_found: false + +network: + enable_ipv6: true + +# ===== Packages ===== +packages: + esparagus_remote: + url: https://github.com/sonocotta/esparagus-media-center + ref: main + files: + # Implements software BQ-filters, but makes less sense for DSP-enabled DACs like TAS58XX + - firmware/esphome/packages/snapclient-with-dsp.yaml + - firmware/esphome/packages/dac-switch.yaml + + - firmware/esphome/packages/monitoring.yaml + - firmware/esphome/packages/monitoring-wifi.yaml + - firmware/esphome/packages/light-x.yaml + # Optional ethernet (replace wifi config if you use this) + # - firmware/esphome/packages/ethernet-w5500.yaml \ No newline at end of file diff --git a/firmware/esphome/echo-duo-a/echo-duo-a-idf-snapclient.yaml b/firmware/esphome/echo-duo-a/echo-duo-a-idf-snapclient.yaml new file mode 100644 index 0000000..c2cfbdf --- /dev/null +++ b/firmware/esphome/echo-duo-a/echo-duo-a-idf-snapclient.yaml @@ -0,0 +1,112 @@ + +# ============================================================ +# Esparagus-Echo - Snapclient Configuration +# ============================================================ +# ESP32-S3 audio board with dual MAX98357A DAC and onboards I2S Microphone +# Configured as a Snapcast client for synchronized audio +# There are two implementations available: +# 1) Basic snapclient without DSP controls (c-MM original port) +# Board-specific pinouts defined in substitutions +# Functional blocks imported from /packages +# ============================================================ + +substitutions: + name: esphome-web-47a438 + friendly_name: Esparagus-Echo-Duo + task_stack_in_psram: "true" + + # ===== Board-Specific Pinouts ===== + # I2S pins + i2s_lrclk_pin: GPIO15 + i2s_bclk_pin: GPIO14 + i2s_dout_pin: GPIO16 + + # DAC enable pins + dac_enable_pin: GPIO17 + dac_enable_restore_mode: ALWAYS_OFF + + # RGB LED + rgb_led_pin: GPIO42 + num_leds: 3 + + # SPI pins (for ethernet) + spi_clk_pin: GPIO12 + spi_mosi_pin: GPIO11 + spi_miso_pin: GPIO13 + + # Ethernet pins (if using ethernet instead of wifi) + eth_cs_pin: GPIO10 + eth_interrupt_pin: GPIO6 + eth_reset_pin: GPIO5 + + # ===== Snapcast Server Configuration ===== + snapcast_server_ip: "192.168.1.49" + snapcast_server_port: "1704" + +esphome: + name: ${name} + friendly_name: ${friendly_name} + min_version: 2026.2.1 + name_add_mac_suffix: false + platformio_options: + board_build.flash_mode: dio + + +esp32: + variant: ESP32S3 + flash_size: 8MB + cpu_frequency: 240MHz + framework: + type: esp-idf + version: recommended + sdkconfig_options: + CONFIG_ESP32S3_DATA_CACHE_64KB: "y" + CONFIG_ESP32S3_DATA_CACHE_LINE_64B: "y" + CONFIG_ESP32S3_INSTRUCTION_CACHE_32KB: "y" + # Moves instructions and read only data from flash into PSRAM on boot. + # Both enabled allows instructions to execute while a flash operation is in progress without needing to be placed in IRAM. + # Considerably speeds up mWW at the cost of using more PSRAM. + CONFIG_SPIRAM_RODATA: "y" + CONFIG_SPIRAM_FETCH_INSTRUCTIONS: "y" + CONFIG_BT_ALLOCATION_FROM_SPIRAM_FIRST: "y" + CONFIG_BT_BLE_DYNAMIC_ENV_MEMORY: "y" + CONFIG_MBEDTLS_EXTERNAL_MEM_ALLOC: "y" + CONFIG_MBEDTLS_SSL_PROTO_TLS1_3: "y" + +logger: + level: DEBUG + +api: + +ota: + platform: esphome + password: !secret esphome_ota_password + +wifi: + ssid: !secret esphome_wifi_ssid + password: !secret esphome_wifi_password + +psram: + mode: octal + speed: 80MHz + ignore_not_found: false + +network: + enable_ipv6: true + +# ===== Packages ===== +packages: + esparagus_remote: + url: https://github.com/sonocotta/esparagus-media-center + ref: main + files: + # Basic implementation without DSP controls, but with playback events (DAC_EN hook) + - firmware/esphome/packages/snapclient-new.yaml + - firmware/esphome/packages/dac-switch.yaml + - firmware/esphome/packages/snapclient-addon-dac-enable.yaml + + - firmware/esphome/packages/monitoring.yaml + - firmware/esphome/packages/monitoring-wifi.yaml + - firmware/esphome/packages/light-x.yaml + # Optional ethernet (replace wifi config if you use this) + # - firmware/esphome/packages/ethernet-w5500.yaml \ No newline at end of file diff --git a/firmware/esphome/esphome-2026.2.1/echo-duo-b-idf-media-player.yaml b/firmware/esphome/echo-duo-b/echo-duo-b-idf-media-player.yaml similarity index 93% rename from firmware/esphome/esphome-2026.2.1/echo-duo-b-idf-media-player.yaml rename to firmware/esphome/echo-duo-b/echo-duo-b-idf-media-player.yaml index de61811..e81195c 100644 --- a/firmware/esphome/esphome-2026.2.1/echo-duo-b-idf-media-player.yaml +++ b/firmware/esphome/echo-duo-b/echo-duo-b-idf-media-player.yaml @@ -99,10 +99,7 @@ packages: url: https://github.com/sonocotta/esparagus-media-center ref: main files: - - firmware/esphome/packages/audio.yaml - firmware/esphome/packages/dac-switch.yaml - - firmware/esphome/packages/media-player.yaml - - firmware/esphome/packages/media-player-addon-dac-enable.yaml - firmware/esphome/packages/light-x.yaml - firmware/esphome/packages/monitoring.yaml - firmware/esphome/packages/monitoring-wifi.yaml @@ -113,8 +110,11 @@ packages: url: https://github.com/sonocotta/esparagus-echo ref: main files: - - firmware/esphome/packages/microphone.yaml - - # # Debug/local development fallback: - # microphone: !include packages/microphone.yaml + - firmware/esphome/packages/audio.yaml + - firmware/esphome/packages/media-player.yaml + - firmware/esphome/packages/media-player-addon-dac-enable.yaml + # # Debug/local development fallback: + # audio: !include packages/audio.yaml + # media_player: !include packages/media-player.yaml + # media_player_dac_enable: !include packages/media-player-addon-dac-enable.yaml diff --git a/firmware/esphome/esphome-2026.2.1/echo-duo-b-idf-sendspin.yaml b/firmware/esphome/echo-duo-b/echo-duo-b-idf-sendspin.yaml similarity index 87% rename from firmware/esphome/esphome-2026.2.1/echo-duo-b-idf-sendspin.yaml rename to firmware/esphome/echo-duo-b/echo-duo-b-idf-sendspin.yaml index b8a25fe..024126f 100644 --- a/firmware/esphome/esphome-2026.2.1/echo-duo-b-idf-sendspin.yaml +++ b/firmware/esphome/echo-duo-b/echo-duo-b-idf-sendspin.yaml @@ -45,7 +45,7 @@ substitutions: esphome: name: ${name} friendly_name: ${friendly_name} - min_version: 2026.2.1 + min_version: 2026.5.0 name_add_mac_suffix: false platformio_options: board_build.flash_mode: dio @@ -98,14 +98,23 @@ packages: url: https://github.com/sonocotta/esparagus-media-center ref: main files: - # Basic implementation without DSP controls, but with playback events (DAC_EN hook) - firmware/esphome/packages/dac-switch.yaml - - firmware/esphome/packages/sendspin-audio.yaml - - firmware/esphome/packages/sendspin.yaml - - firmware/esphome/packages/sendspin-addon-dac-enable.yaml - + - firmware/esphome/packages/light-x.yaml - firmware/esphome/packages/monitoring.yaml - firmware/esphome/packages/monitoring-wifi.yaml - - firmware/esphome/packages/light-x.yaml # Optional ethernet (replace wifi config if you use this, you need to exclude OLED package if you do this to free up SPI bus) - # - firmware/esphome/packages/ethernet-w5500.yaml \ No newline at end of file + # - firmware/esphome/packages/ethernet-w5500.yaml + + + esparagus_echo: + url: https://github.com/sonocotta/esparagus-echo + ref: main + files: + - firmware/esphome/packages/audio.yaml + - firmware/esphome/packages/sendspin.yaml + - firmware/esphome/packages/sendspin-addon-dac-enable.yaml + + # # Debug/local development fallback: + # audio: !include packages/audio.yaml + # sendspin: !include packages/sendspin.yaml + # sendspin_addon_dac_enable: !include packages/sendspin-addon-dac-enable.yaml \ No newline at end of file diff --git a/firmware/esphome/esphome-2026.2.1/echo-duo-b-idf-snapclient-with-dsp.yaml b/firmware/esphome/echo-duo-b/echo-duo-b-idf-snapclient-with-dsp.yaml similarity index 96% rename from firmware/esphome/esphome-2026.2.1/echo-duo-b-idf-snapclient-with-dsp.yaml rename to firmware/esphome/echo-duo-b/echo-duo-b-idf-snapclient-with-dsp.yaml index 68fde02..97798d7 100644 --- a/firmware/esphome/esphome-2026.2.1/echo-duo-b-idf-snapclient-with-dsp.yaml +++ b/firmware/esphome/echo-duo-b/echo-duo-b-idf-snapclient-with-dsp.yaml @@ -12,6 +12,7 @@ # Board-specific pinouts defined in substitutions # Functional blocks imported from /packages # ============================================================ + substitutions: name: esphome-web-a87b38 friendly_name: Esparagus-Echo-Duo @@ -114,5 +115,5 @@ packages: - firmware/esphome/packages/monitoring.yaml - firmware/esphome/packages/monitoring-wifi.yaml - firmware/esphome/packages/light-x.yaml - # Optional ethernet (replace wifi config if you use this, you need to exclude OLED package if you do this to free up SPI bus) + # Optional ethernet (replace wifi config if you use this) # - firmware/esphome/packages/ethernet-w5500.yaml \ No newline at end of file diff --git a/firmware/esphome/esphome-2026.2.1/echo-duo-b-idf-snapclient.yaml b/firmware/esphome/echo-duo-b/echo-duo-b-idf-snapclient.yaml similarity index 96% rename from firmware/esphome/esphome-2026.2.1/echo-duo-b-idf-snapclient.yaml rename to firmware/esphome/echo-duo-b/echo-duo-b-idf-snapclient.yaml index f1e1d52..c8c549c 100644 --- a/firmware/esphome/esphome-2026.2.1/echo-duo-b-idf-snapclient.yaml +++ b/firmware/esphome/echo-duo-b/echo-duo-b-idf-snapclient.yaml @@ -115,5 +115,5 @@ packages: - firmware/esphome/packages/monitoring.yaml - firmware/esphome/packages/monitoring-wifi.yaml - firmware/esphome/packages/light-x.yaml - # Optional ethernet (replace wifi config if you use this, you need to exclude OLED package if you do this to free up SPI bus) + # Optional ethernet (replace wifi config if you use this) # - firmware/esphome/packages/ethernet-w5500.yaml \ No newline at end of file diff --git a/firmware/esphome/echo-duo-b/echo-duo-b-idf-voice-assist-sendspin.yaml b/firmware/esphome/echo-duo-b/echo-duo-b-idf-voice-assist-sendspin.yaml new file mode 100644 index 0000000..54ec6c6 --- /dev/null +++ b/firmware/esphome/echo-duo-b/echo-duo-b-idf-voice-assist-sendspin.yaml @@ -0,0 +1,144 @@ + +# ============================================================ +# Esparagus-Echo - Voice-Assistant Configuration +# ============================================================ +# ESP32-S3 audio board with dual MAX98357A DAC and onboards I2S Microphone +# Configured as a Voice-Assistant +# Features: Wake word detection, media player with ducking, RGB LED status +# Board-specific pinouts defined in substitutions +# Functional blocks imported from /packages +# ============================================================ + +substitutions: + name: esphome-web-a87b38 + friendly_name: Esparagus-Echo-Duo + task_stack_in_psram: "true" + + # ===== Board-Specific Pinouts ===== + # I2S audio pins + i2s_lrclk_pin: GPIO8 + i2s_bclk_pin: GPIO18 + i2s_dout_pin: GPIO17 + + # I2S microphone pins + i2s_mic_lrclk_pin: GPIO7 + i2s_mic_bclk_pin: GPIO16 + i2s_mic_din_pin: GPIO15 + + # DAC enable pins + dac_enable_pin: GPIO9 + dac_enable_restore_mode: ALWAYS_ON + + # RGB LED + rgb_led_pin: GPIO42 + num_leds: 3 + + # SPI pins (for ethernet) + spi_clk_pin: GPIO12 + spi_mosi_pin: GPIO11 + spi_miso_pin: GPIO13 + + # Ethernet pins (if using ethernet instead of wifi) + eth_cs_pin: GPIO10 + eth_interrupt_pin: GPIO6 + eth_reset_pin: GPIO5 + +esphome: + name: ${name} + friendly_name: ${friendly_name} + min_version: 2026.2.1 + name_add_mac_suffix: false + platformio_options: + board_build.flash_mode: dio + +esp32: + variant: ESP32S3 + flash_size: 8MB + cpu_frequency: 240MHz + framework: + type: esp-idf + version: recommended + sdkconfig_options: + CONFIG_ESP32S3_DATA_CACHE_64KB: "y" + CONFIG_ESP32S3_DATA_CACHE_LINE_64B: "y" + CONFIG_ESP32S3_INSTRUCTION_CACHE_32KB: "y" + # Moves instructions and read only data from flash into PSRAM on boot. + # Both enabled allows instructions to execute while a flash operation is in progress without needing to be placed in IRAM. + # Considerably speeds up mWW at the cost of using more PSRAM. + CONFIG_SPIRAM_RODATA: "y" + CONFIG_SPIRAM_FETCH_INSTRUCTIONS: "y" + CONFIG_BT_ALLOCATION_FROM_SPIRAM_FIRST: "y" + CONFIG_BT_BLE_DYNAMIC_ENV_MEMORY: "y" + CONFIG_MBEDTLS_EXTERNAL_MEM_ALLOC: "y" + CONFIG_MBEDTLS_SSL_PROTO_TLS1_3: "y" + + CONFIG_ESP32_S3_BOX_BOARD: "y" + # OPTIMIZED WiFi buffers for streaming + CONFIG_ESP32_WIFI_STATIC_RX_BUFFER_NUM: "16" + CONFIG_ESP32_WIFI_DYNAMIC_RX_BUFFER_NUM: "32" + CONFIG_TCPIP_RECVMBOX_SIZE: "512" + CONFIG_TCP_SND_BUF_DEFAULT: "65535" + CONFIG_TCP_WND_DEFAULT: "512000" + CONFIG_TCP_RECVMBOX_SIZE: "512" + # Audio priority + CONFIG_FREERTOS_UNICORE: "n" + CONFIG_ESP_TASK_WDT_TIMEOUT_S: "10" + +logger: + level: INFO # Changed from DEBUG to reduce overhead + +ota: + platform: esphome + password: !secret esphome_ota_password + +wifi: + ssid: !secret esphome_wifi_ssid + password: !secret esphome_wifi_password + +api: + +psram: + mode: octal + speed: 80MHz + ignore_not_found: false + +network: + enable_ipv6: true + +# ===== Packages ===== +packages: + esparagus_remote: + url: https://github.com/sonocotta/esparagus-media-center + ref: main + files: + - firmware/esphome/packages/dac-switch.yaml + - firmware/esphome/packages/light-x.yaml + - firmware/esphome/packages/monitoring.yaml + - firmware/esphome/packages/monitoring-wifi.yaml + # Optional ethernet (replace wifi config if you use this, you need to exclude OLED package if you do this to free up SPI bus) + # - firmware/esphome/packages/ethernet-w5500.yaml + + esparagus_echo: + url: https://github.com/sonocotta/esparagus-echo + ref: main + files: + - firmware/esphome/packages/microphone-i2s.yaml + - firmware/esphome/packages/microphone.yaml + - firmware/esphome/packages/audio.yaml + - firmware/esphome/packages/sendspin.yaml + - firmware/esphome/packages/voice-assist-sendspin.yaml + - firmware/esphome/packages/sendspin-addon-voice-assist.yaml + # - firmware/esphome/packages/voice-assist-addon-dac-enable.yaml + + + # # Debug/local development fallback: + # microphone_i2s: !include packages/microphone-i2s.yaml + # microphone: !include packages/microphone.yaml + # audio: !include packages/audio.yaml + + # # B: Using Sendspin media player with ducking support + # sendspin: !include packages/sendspin.yaml + # voice_assist: !include packages/voice-assist-sendspin.yaml + # # disabled, since it goes into conflict with the DAC enable logic in voice-assist + # # sendspin_addon_dac_enable: !include packages/sendspin-addon-dac-enable.yaml + # media_player_addon_voice_assist: !include packages/sendspin-addon-voice-assist.yaml \ No newline at end of file diff --git a/firmware/esphome/esphome-2026.2.1/echo-duo-b-idf-voice-assist.yaml b/firmware/esphome/echo-duo-b/echo-duo-b-idf-voice-assist.yaml similarity index 83% rename from firmware/esphome/esphome-2026.2.1/echo-duo-b-idf-voice-assist.yaml rename to firmware/esphome/echo-duo-b/echo-duo-b-idf-voice-assist.yaml index 19772aa..55287bb 100644 --- a/firmware/esphome/esphome-2026.2.1/echo-duo-b-idf-voice-assist.yaml +++ b/firmware/esphome/echo-duo-b/echo-duo-b-idf-voice-assist.yaml @@ -27,7 +27,7 @@ substitutions: # DAC enable pins dac_enable_pin: GPIO9 - dac_enable_restore_mode: ALWAYS_OFF + dac_enable_restore_mode: ALWAYS_ON # RGB LED rgb_led_pin: GPIO42 @@ -111,29 +111,31 @@ packages: url: https://github.com/sonocotta/esparagus-media-center ref: main files: - # - firmware/esphome/packages/audio.yaml - firmware/esphome/packages/dac-switch.yaml - # - firmware/esphome/packages/media-player.yaml - # - firmware/esphome/packages/media-player-addon-dac-enable.yaml - firmware/esphome/packages/light-x.yaml - firmware/esphome/packages/monitoring.yaml - firmware/esphome/packages/monitoring-wifi.yaml - # Optional ethernet (replace wifi config if you use this, you need to exclude OLED package if you do this to free up SPI bus) - # - firmware/esphome/packages/ethernet-w5500.yaml esparagus_echo: url: https://github.com/sonocotta/esparagus-echo ref: main files: - - firmware/esphome/packages/audio.yaml + - firmware/esphome/packages/microphone-i2s.yaml - firmware/esphome/packages/microphone.yaml + - firmware/esphome/packages/audio.yaml + - firmware/esphome/packages/media-player.yaml + - firmware/esphome/packages/media-player-addon-voice-assist.yaml - firmware/esphome/packages/voice-assist.yaml - - firmware/esphome/packages/voice-assist-addon-dac-enable.yaml - - firmware/esphome/packages/voice-assist-media-player.yaml + # - firmware/esphome/packages/voice-assist-addon-dac-enable.yaml # # Debug/local development fallback: - # audio: !include packages/audio.yaml + # microphone_i2s: !include packages/microphone-i2s.yaml # microphone: !include packages/microphone.yaml + # audio: !include packages/audio.yaml - # media_player: !include packages/voice-assist-media-player.yaml - # voice_assistant: !include packages/voice-assist.yaml \ No newline at end of file + # # A: Using standard HA media player + # media_player: !include packages/media-player.yaml + # voice_assist: !include packages/voice-assist.yaml + # # disabled, since it goes into conflict with the DAC enable logic in voice-assist + # # voice_assist_dac_enable: !include packages/voice-assist-addon-dac-enable.yaml + # media_player_addon_voice_assist: !include packages/media-player-addon-voice-assist.yaml \ No newline at end of file diff --git a/firmware/esphome/esphome-2026.2.1/echo-solo-b-idf-media-player.yaml b/firmware/esphome/echo-solo-b/echo-solo-b-idf-media-player.yaml similarity index 90% rename from firmware/esphome/esphome-2026.2.1/echo-solo-b-idf-media-player.yaml rename to firmware/esphome/echo-solo-b/echo-solo-b-idf-media-player.yaml index b11e1e0..b1bf407 100644 --- a/firmware/esphome/esphome-2026.2.1/echo-solo-b-idf-media-player.yaml +++ b/firmware/esphome/echo-solo-b/echo-solo-b-idf-media-player.yaml @@ -10,8 +10,8 @@ # ============================================================ substitutions: - name: esphome-web-a87b38 - friendly_name: Esparagus-Echo-Duo + name: esphome-web-a86afc + friendly_name: Esparagus-Echo-Solo task_stack_in_psram: "true" # ===== Board-Specific Pinouts ===== @@ -90,10 +90,7 @@ packages: url: https://github.com/sonocotta/esparagus-media-center ref: main files: - - firmware/esphome/packages/audio.yaml - firmware/esphome/packages/dac-switch.yaml - - firmware/esphome/packages/media-player.yaml - - firmware/esphome/packages/media-player-addon-dac-enable.yaml - firmware/esphome/packages/light-x.yaml - firmware/esphome/packages/monitoring.yaml - firmware/esphome/packages/monitoring-wifi.yaml @@ -102,8 +99,11 @@ packages: url: https://github.com/sonocotta/esparagus-echo ref: main files: - - firmware/esphome/packages/microphone.yaml - - # # Debug/local development fallback: - # microphone: !include packages/microphone.yaml + - firmware/esphome/packages/audio.yaml + - firmware/esphome/packages/media-player.yaml + - firmware/esphome/packages/media-player-addon-dac-enable.yaml + # # Debug/local development fallback: + # audio: !include packages/audio.yaml + # media_player: !include packages/media-player.yaml + # media_player_dac_enable: !include packages/media-player-addon-dac-enable.yaml diff --git a/firmware/esphome/esphome-2026.2.1/echo-solo-b-idf-sendspin.yaml b/firmware/esphome/echo-solo-b/echo-solo-b-idf-sendspin.yaml similarity index 85% rename from firmware/esphome/esphome-2026.2.1/echo-solo-b-idf-sendspin.yaml rename to firmware/esphome/echo-solo-b/echo-solo-b-idf-sendspin.yaml index a2b32b6..1e03f45 100644 --- a/firmware/esphome/esphome-2026.2.1/echo-solo-b-idf-sendspin.yaml +++ b/firmware/esphome/echo-solo-b/echo-solo-b-idf-sendspin.yaml @@ -9,8 +9,8 @@ # ============================================================ substitutions: - name: esphome-web-a87b38 - friendly_name: Esparagus-Echo-Duo + name: esphome-web-a86afc + friendly_name: Esparagus-Echo-Solo task_stack_in_psram: "true" # ===== Board-Specific Pinouts ===== @@ -35,7 +35,7 @@ substitutions: esphome: name: ${name} friendly_name: ${friendly_name} - min_version: 2026.2.1 + min_version: 2026.5.0 name_add_mac_suffix: false platformio_options: board_build.flash_mode: dio @@ -88,12 +88,20 @@ packages: url: https://github.com/sonocotta/esparagus-media-center ref: main files: - # Basic implementation without DSP controls, but with playback events (DAC_EN hook) - firmware/esphome/packages/dac-switch.yaml - - firmware/esphome/packages/sendspin-audio.yaml + - firmware/esphome/packages/light-x.yaml + - firmware/esphome/packages/monitoring.yaml + - firmware/esphome/packages/monitoring-wifi.yaml + + esparagus_echo: + url: https://github.com/sonocotta/esparagus-echo + ref: main + files: + - firmware/esphome/packages/audio.yaml - firmware/esphome/packages/sendspin.yaml - firmware/esphome/packages/sendspin-addon-dac-enable.yaml - - firmware/esphome/packages/monitoring.yaml - - firmware/esphome/packages/monitoring-wifi.yaml - - firmware/esphome/packages/light-x.yaml + # # Debug/local development fallback: + # audio: !include packages/audio.yaml + # sendspin: !include packages/sendspin.yaml + # sendspin_addon_dac_enable: !include packages/sendspin-addon-dac-enable.yaml \ No newline at end of file diff --git a/firmware/esphome/esphome-2026.2.1/echo-solo-b-idf-snapclient-with-dsp.yaml b/firmware/esphome/echo-solo-b/echo-solo-b-idf-snapclient-with-dsp.yaml similarity index 98% rename from firmware/esphome/esphome-2026.2.1/echo-solo-b-idf-snapclient-with-dsp.yaml rename to firmware/esphome/echo-solo-b/echo-solo-b-idf-snapclient-with-dsp.yaml index e60bbdb..c5f85d3 100644 --- a/firmware/esphome/esphome-2026.2.1/echo-solo-b-idf-snapclient-with-dsp.yaml +++ b/firmware/esphome/echo-solo-b/echo-solo-b-idf-snapclient-with-dsp.yaml @@ -14,7 +14,7 @@ # ============================================================ substitutions: name: esphome-web-a87b38 - friendly_name: Esparagus-Echo-Duo + friendly_name: Esparagus-Echo-Solo task_stack_in_psram: "true" # ===== Board-Specific Pinouts ===== diff --git a/firmware/esphome/esphome-2026.2.1/echo-solo-b-idf-snapclient.yaml b/firmware/esphome/echo-solo-b/echo-solo-b-idf-snapclient.yaml similarity index 98% rename from firmware/esphome/esphome-2026.2.1/echo-solo-b-idf-snapclient.yaml rename to firmware/esphome/echo-solo-b/echo-solo-b-idf-snapclient.yaml index bb53373..feaf97b 100644 --- a/firmware/esphome/esphome-2026.2.1/echo-solo-b-idf-snapclient.yaml +++ b/firmware/esphome/echo-solo-b/echo-solo-b-idf-snapclient.yaml @@ -14,7 +14,7 @@ substitutions: name: esphome-web-a87b38 - friendly_name: Esparagus-Echo-Duo + friendly_name: Esparagus-Echo-Solo task_stack_in_psram: "true" # ===== Board-Specific Pinouts ===== diff --git a/firmware/esphome/echo-solo-b/echo-solo-b-idf-voice-assist-sendspin.yaml b/firmware/esphome/echo-solo-b/echo-solo-b-idf-voice-assist-sendspin.yaml new file mode 100644 index 0000000..ae2c5a5 --- /dev/null +++ b/firmware/esphome/echo-solo-b/echo-solo-b-idf-voice-assist-sendspin.yaml @@ -0,0 +1,133 @@ + +# ============================================================ +# Esparagus-Echo - Voice-Assistant Configuration +# ============================================================ +# ESP32-S3 audio board with dual MAX98357A DAC and onboards I2S Microphone +# Configured as a Voice-Assistant +# Features: Wake word detection, media player with ducking, RGB LED status +# Board-specific pinouts defined in substitutions +# Functional blocks imported from /packages +# ============================================================ + +substitutions: + name: esphome-web-a86afc + friendly_name: Esparagus-Echo-Solo + task_stack_in_psram: "true" + + # ===== Board-Specific Pinouts ===== + # I2S audio pins + i2s_lrclk_pin: GPIO8 + i2s_bclk_pin: GPIO18 + i2s_dout_pin: GPIO17 + + # I2S microphone pins + i2s_mic_lrclk_pin: GPIO7 + i2s_mic_bclk_pin: GPIO16 + i2s_mic_din_pin: GPIO15 + + # DAC enable pins + dac_enable_pin: GPIO9 + dac_enable_restore_mode: ALWAYS_ON + + # RGB LED + rgb_led_pin: GPIO42 + num_leds: 2 + +esphome: + name: ${name} + friendly_name: ${friendly_name} + min_version: 2026.5.0 + name_add_mac_suffix: false + platformio_options: + board_build.flash_mode: dio + +esp32: + variant: ESP32S3 + flash_size: 8MB + cpu_frequency: 240MHz + framework: + type: esp-idf + version: recommended + sdkconfig_options: + CONFIG_ESP32S3_DATA_CACHE_64KB: "y" + CONFIG_ESP32S3_DATA_CACHE_LINE_64B: "y" + CONFIG_ESP32S3_INSTRUCTION_CACHE_32KB: "y" + # Moves instructions and read only data from flash into PSRAM on boot. + # Both enabled allows instructions to execute while a flash operation is in progress without needing to be placed in IRAM. + # Considerably speeds up mWW at the cost of using more PSRAM. + CONFIG_SPIRAM_RODATA: "y" + CONFIG_SPIRAM_FETCH_INSTRUCTIONS: "y" + CONFIG_BT_ALLOCATION_FROM_SPIRAM_FIRST: "y" + CONFIG_BT_BLE_DYNAMIC_ENV_MEMORY: "y" + CONFIG_MBEDTLS_EXTERNAL_MEM_ALLOC: "y" + CONFIG_MBEDTLS_SSL_PROTO_TLS1_3: "y" + + CONFIG_ESP32_S3_BOX_BOARD: "y" + # OPTIMIZED WiFi buffers for streaming + CONFIG_ESP32_WIFI_STATIC_RX_BUFFER_NUM: "16" + CONFIG_ESP32_WIFI_DYNAMIC_RX_BUFFER_NUM: "32" + CONFIG_TCPIP_RECVMBOX_SIZE: "512" + CONFIG_TCP_SND_BUF_DEFAULT: "65535" + CONFIG_TCP_WND_DEFAULT: "512000" + CONFIG_TCP_RECVMBOX_SIZE: "512" + # Audio priority + CONFIG_FREERTOS_UNICORE: "n" + CONFIG_ESP_TASK_WDT_TIMEOUT_S: "10" + +logger: + level: DEBUG + # level: INFO # Changed from DEBUG to reduce overhead + +ota: + platform: esphome + password: !secret esphome_ota_password + +wifi: + ssid: !secret esphome_wifi_ssid + password: !secret esphome_wifi_password + +api: + +psram: + mode: octal + speed: 80MHz + ignore_not_found: false + +network: + enable_ipv6: true + +# ===== Packages ===== +packages: + esparagus_remote: + url: https://github.com/sonocotta/esparagus-media-center + ref: main + files: + - firmware/esphome/packages/dac-switch.yaml + - firmware/esphome/packages/light-x.yaml + - firmware/esphome/packages/monitoring.yaml + - firmware/esphome/packages/monitoring-wifi.yaml + + esparagus_echo: + url: https://github.com/sonocotta/esparagus-echo + ref: main + files: + - firmware/esphome/packages/microphone-i2s.yaml + - firmware/esphome/packages/microphone.yaml + - firmware/esphome/packages/audio.yaml + - firmware/esphome/packages/sendspin.yaml + - firmware/esphome/packages/voice-assist-sendspin.yaml + - firmware/esphome/packages/sendspin-addon-voice-assist.yaml + # - firmware/esphome/packages/voice-assist-addon-dac-enable.yaml + + + # # Debug/local development fallback: + # microphone_i2s: !include packages/microphone-i2s.yaml + # microphone: !include packages/microphone.yaml + # audio: !include packages/audio.yaml + + # # B: Using Sendspin media player with ducking support + # sendspin: !include packages/sendspin.yaml + # voice_assist: !include packages/voice-assist-sendspin.yaml + # # disabled, since it goes into conflict with the DAC enable logic in voice-assist + # # sendspin_addon_dac_enable: !include packages/sendspin-addon-dac-enable.yaml + # media_player_addon_voice_assist: !include packages/sendspin-addon-voice-assist.yaml \ No newline at end of file diff --git a/firmware/esphome/esphome-2026.2.1/echo-solo-b-idf-voice-assist.yaml b/firmware/esphome/echo-solo-b/echo-solo-b-idf-voice-assist.yaml similarity index 78% rename from firmware/esphome/esphome-2026.2.1/echo-solo-b-idf-voice-assist.yaml rename to firmware/esphome/echo-solo-b/echo-solo-b-idf-voice-assist.yaml index e42e18c..4067a04 100644 --- a/firmware/esphome/esphome-2026.2.1/echo-solo-b-idf-voice-assist.yaml +++ b/firmware/esphome/echo-solo-b/echo-solo-b-idf-voice-assist.yaml @@ -10,8 +10,8 @@ # ============================================================ substitutions: - name: esphome-web-a87b38 - friendly_name: Esparagus-Echo-Duo + name: esphome-web-a86afc + friendly_name: Esparagus-Echo-Solo task_stack_in_psram: "true" # ===== Board-Specific Pinouts ===== @@ -27,7 +27,7 @@ substitutions: # DAC enable pins dac_enable_pin: GPIO9 - dac_enable_restore_mode: ALWAYS_OFF + dac_enable_restore_mode: ALWAYS_ON # RGB LED rgb_led_pin: GPIO42 @@ -36,7 +36,7 @@ substitutions: esphome: name: ${name} friendly_name: ${friendly_name} - min_version: 2026.2.1 + min_version: 2026.4.0 name_add_mac_suffix: false platformio_options: board_build.flash_mode: dio @@ -75,7 +75,8 @@ esp32: CONFIG_ESP_TASK_WDT_TIMEOUT_S: "10" logger: - level: INFO # Changed from DEBUG to reduce overhead + level: DEBUG + # level: INFO # Changed from DEBUG to reduce overhead ota: platform: esphome @@ -101,10 +102,7 @@ packages: url: https://github.com/sonocotta/esparagus-media-center ref: main files: - # - firmware/esphome/packages/audio.yaml - firmware/esphome/packages/dac-switch.yaml - # - firmware/esphome/packages/media-player.yaml - # - firmware/esphome/packages/media-player-addon-dac-enable.yaml - firmware/esphome/packages/light-x.yaml - firmware/esphome/packages/monitoring.yaml - firmware/esphome/packages/monitoring-wifi.yaml @@ -113,16 +111,23 @@ packages: url: https://github.com/sonocotta/esparagus-echo ref: main files: - - firmware/esphome/packages/audio.yaml + - firmware/esphome/packages/microphone-i2s.yaml - firmware/esphome/packages/microphone.yaml + - firmware/esphome/packages/audio.yaml + - firmware/esphome/packages/media-player.yaml + - firmware/esphome/packages/media-player-addon-voice-assist.yaml - firmware/esphome/packages/voice-assist.yaml - - firmware/esphome/packages/voice-assist-addon-dac-enable.yaml - - firmware/esphome/packages/voice-assist-media-player.yaml + # - firmware/esphome/packages/voice-assist-addon-dac-enable.yaml # # Debug/local development fallback: - # audio: !include packages/audio.yaml + # microphone_i2s: !include packages/microphone-i2s.yaml # microphone: !include packages/microphone.yaml + # audio: !include packages/audio.yaml - # media_player: !include packages/voice-assist-media-player.yaml - # voice_assistant: !include packages/voice-assist.yaml \ No newline at end of file + # # A: Using standard HA media player + # media_player: !include packages/media-player.yaml + # voice_assist: !include packages/voice-assist.yaml + # # disabled, since it goes into conflict with the DAC enable logic in voice-assist + # # voice_assist_dac_enable: !include packages/voice-assist-addon-dac-enable.yaml + # media_player_addon_voice_assist: !include packages/media-player-addon-voice-assist.yaml \ No newline at end of file diff --git a/firmware/esphome/esphome-2025.10.1/echo-duo-b-voice-assist.yaml b/firmware/esphome/esphome-2025.10.1/echo-duo-b-voice-assist.yaml deleted file mode 100644 index 3f7375e..0000000 --- a/firmware/esphome/esphome-2025.10.1/echo-duo-b-voice-assist.yaml +++ /dev/null @@ -1,544 +0,0 @@ -# ESPHome 2025.10.2 - -substitutions: - name: esparagus-duo - friendly_name: "Esparagus Duo" - wake_word: okay_nabu - speaker_volume: 60% # Increased from 50% - 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' - -esphome: - name: ${name} - friendly_name: ${friendly_name} - name_add_mac_suffix: false - min_version: 2025.10.2 - platformio_options: - build_flags: -DBOARD_HAS_PSRAM - board_build.flash_mode: dio - board_upload.maximum_size: 16777216 - on_boot: - priority: 200.0 - then: - - media_player.volume_set: - id: voice_assistant_media_player - volume: ${speaker_volume} - - switch.turn_on: - id: speaker_switch - - delay: 30s - - if: - condition: - lambda: return id(init_in_progress); - then: - - lambda: id(init_in_progress) = false; - -esp32: - board: esp32-s3-devkitc-1 - variant: ESP32S3 - flash_size: 16MB - framework: - type: esp-idf - sdkconfig_options: - CONFIG_ESP32_S3_BOX_BOARD: "y" - # OPTIMIZED WiFi buffers for streaming - CONFIG_ESP32_WIFI_STATIC_RX_BUFFER_NUM: "16" - CONFIG_ESP32_WIFI_DYNAMIC_RX_BUFFER_NUM: "32" - CONFIG_TCPIP_RECVMBOX_SIZE: "512" - CONFIG_TCP_SND_BUF_DEFAULT: "65535" - CONFIG_TCP_WND_DEFAULT: "512000" - CONFIG_TCP_RECVMBOX_SIZE: "512" - # Audio priority - CONFIG_FREERTOS_UNICORE: "n" - CONFIG_ESP_TASK_WDT_TIMEOUT_S: "10" - -logger: - level: INFO # Changed from DEBUG to reduce overhead - -api: - -ota: - platform: esphome - -wifi: - ssid: !secret wifi_ssid - password: !secret wifi_password - ap: - ssid: "${name} Hotspot" - -### Optional if you want ethernet (then remove above wifi config) -#ethernet: -# type: W5500 -# clk_pin: GPIO12 -# mosi_pin: GPIO11 -# miso_pin: GPIO13 -# cs_pin: GPIO10 -# interrupt_pin: GPIO6 -# reset_pin: GPIO5 - -captive_portal: - -psram: - mode: octal - speed: 80MHz - -globals: - - id: init_in_progress - type: bool - restore_value: no - initial_value: 'true' - - id: voice_assistant_phase - type: int - restore_value: no - initial_value: ${voice_assist_not_ready_phase_id} - -# Correct I2S configuration -i2s_audio: - - id: i2s_in - i2s_lrclk_pin: GPIO7 - i2s_bclk_pin: GPIO16 - - id: i2s_out - i2s_lrclk_pin: GPIO8 - i2s_bclk_pin: GPIO18 - -microphone: - - platform: i2s_audio - adc_type: external - id: voice_assistant_mic - i2s_audio_id: i2s_in - i2s_din_pin: GPIO15 - pdm: false - channel: right - sample_rate: 16000 - bits_per_sample: 32bit - use_apll: true # Better clock accuracy - i2s_mode: primary -# OPTIMIZED speaker configuration -speaker: - - platform: i2s_audio - id: i2s_audio_speaker - i2s_audio_id: i2s_out - i2s_dout_pin: GPIO17 - dac_type: external - channel: stereo - i2s_mode: primary - buffer_duration: 500ms # INCREASED from 100ms (reduces glitches) - timeout: never - sample_rate: 48000 - bits_per_sample: 16bit # Keep at 32bit for quality - - - - platform: mixer - id: mixing_speaker - output_speaker: i2s_audio_speaker - num_channels: 2 - source_speakers: - - id: announcement_mixing_input - timeout: never - - id: media_mixing_input - timeout: never - - # OPTIMIZED resamplers - 32bit for better quality - - platform: resampler - id: announcement_resampling_speaker - output_speaker: announcement_mixing_input - sample_rate: 48000 - bits_per_sample: 16 # Changed from 16 to 32 - - platform: resampler - id: media_resampling_speaker - output_speaker: media_mixing_input - sample_rate: 48000 - bits_per_sample: 16 # Changed from 16 to 32 - - - -media_player: - - platform: speaker - id: voice_assistant_media_player - name: "${friendly_name} Media Player" - internal: false - announcement_pipeline: - speaker: announcement_resampling_speaker - format: WAV # Changed from FLAC (less CPU) - num_channels: 1 - sample_rate: 48000 - media_pipeline: - speaker: media_resampling_speaker - format: WAV # Changed from FLAC (less CPU) - num_channels: 2 - sample_rate: 48000 - buffer_size: 2000000 # INCREASED buffer (2MB vs default 1MB) - task_stack_in_psram: true # Use PSRAM for audio tasks - on_announcement: - - mixer_speaker.apply_ducking: - id: media_mixing_input - decibel_reduction: 20 - duration: 0.0s - on_state: - if: - condition: - and: - - switch.is_off: timer_ringing - - not: - voice_assistant.is_running: - - not: - media_player.is_announcing: - then: - - mixer_speaker.apply_ducking: - id: media_mixing_input - decibel_reduction: 0 - duration: 1.0s - 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 - -light: - - platform: esp32_rmt_led_strip - id: rgb_front_led - name: "${friendly_name} Front LED" - rgb_order: GRB - pin: GPIO42 - num_leds: 3 - rmt_symbols: 96 - chipset: ws2812 - default_transition_length: 0s - effects: - - addressable_color_wipe: - name: "Connecting" - colors: - - red: 60% - green: 60% - blue: 60% - num_leds: 1 - - red: 60% - green: 60% - blue: 0% - num_leds: 1 - add_led_interval: 100ms - reverse: true - - pulse: - name: "Fast Pulse" - transition_length: 100ms - update_interval: 100ms - min_brightness: 50% - max_brightness: 100% - - pulse: - name: "Slow Pulse" - transition_length: 250ms - update_interval: 250ms - min_brightness: 50% - max_brightness: 100% - -micro_wake_word: - id: mww - models: - - model: ${wake_word} - microphone: voice_assistant_mic - on_wake_word_detected: - - if: - condition: - switch.is_on: use_wake_word - then: - - light.turn_on: - id: rgb_front_led - blue: 0% - red: 0% - green: 100% - brightness: 75% - effect: "Slow Pulse" - - if: - condition: - switch.is_on: timer_ringing - then: - - switch.turn_off: timer_ringing - - micro_wake_word.start: - else: - - if: - condition: - switch.is_off: ducking_switch - then: - media_player.stop: - - if: - condition: - voice_assistant.is_running: - then: - voice_assistant.stop: - else: - - if: - condition: - media_player.is_announcing: - then: - - media_player.stop: - announcement: true - else: - - 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: - -voice_assistant: - id: va - microphone: voice_assistant_mic - media_player: voice_assistant_media_player - use_wake_word: false - auto_gain: 31dBFS - on_client_connected: - - lambda: id(init_in_progress) = false; - - if: - condition: - switch.is_on: use_wake_word - then: - - micro_wake_word.start: - - lambda: id(voice_assistant_phase) = ${voice_assist_idle_phase_id}; - - script.execute: reset_led - else: - - lambda: id(voice_assistant_phase) = ${voice_assist_muted_phase_id}; - on_client_disconnected: - - lambda: id(voice_assistant_phase) = ${voice_assist_not_ready_phase_id}; - - voice_assistant.stop - - micro_wake_word.stop - - light.turn_on: - id: rgb_front_led - blue: 0% - red: 100% - green: 100% - brightness: 50% - effect: connecting - on_listening: - - light.turn_on: - id: rgb_front_led - blue: 100% - red: 0% - green: 0% - brightness: 50% - effect: "Slow Pulse" - on_tts_start: - - light.turn_on: - id: rgb_front_led - blue: 0% - red: 0% - green: 100% - brightness: 50% - effect: "Slow Pulse" - on_start: - - mixer_speaker.apply_ducking: - id: media_mixing_input - decibel_reduction: 20 - duration: 0.0s - on_end: - then: - - wait_until: - not: - voice_assistant.is_running: - - mixer_speaker.apply_ducking: - id: media_mixing_input - decibel_reduction: 0 - duration: 1.0s - - light.turn_off: - id: rgb_front_led - - voice_assistant.stop - - script.execute: reset_led - - if: - condition: - switch.is_on: use_wake_word - then: - - micro_wake_word.start: - on_error: - - light.turn_on: - id: rgb_front_led - blue: 0% - red: 100% - green: 0% - brightness: 100% - effect: none - - delay: 1s - - script.execute: reset_led - - script.wait: reset_led - - lambda: |- - if (code == "wake-provider-missing" || code == "wake-engine-missing") { - id(use_wake_word).turn_off(); - } - - if: - condition: - switch.is_on: use_wake_word - then: - - micro_wake_word.start: - - script.execute: reset_led - on_timer_finished: - - switch.turn_on: timer_ringing - -script: - - id: reset_led - then: - - if: - condition: - switch.is_on: use_wake_word - then: - - light.turn_on: - id: rgb_front_led - blue: 100% - red: 0% - green: 0% - brightness: 25% - effect: none - else: - - light.turn_off: rgb_front_led - - id: play_sound - parameters: - priority: bool - sound_file: "audio::AudioFile*" - then: - - lambda: |- - if (priority) { - id(voice_assistant_media_player) - ->make_call() - .set_command(media_player::MediaPlayerCommand::MEDIA_PLAYER_COMMAND_STOP) - .set_announcement(true) - .perform(); - } - if ( (id(voice_assistant_media_player).state != media_player::MediaPlayerState::MEDIA_PLAYER_STATE_ANNOUNCING ) || priority) { - id(voice_assistant_media_player) - ->play_file(sound_file, true, false); - } - - id: enable_repeat_one - then: - - lambda: |- - id(voice_assistant_media_player) - ->make_call() - .set_command(media_player::MediaPlayerCommand::MEDIA_PLAYER_COMMAND_REPEAT_ONE) - .set_announcement(true) - .perform(); - id(voice_assistant_media_player)->set_playlist_delay_ms(speaker::AudioPipelineType::ANNOUNCEMENT, 500); - - id: ring_timer - then: - - script.execute: enable_repeat_one - - script.execute: - id: play_sound - priority: true - sound_file: !lambda return id(timer_finished_sound); - - id: disable_repeat - then: - - lambda: |- - id(voice_assistant_media_player) - ->make_call() - .set_command(media_player::MediaPlayerCommand::MEDIA_PLAYER_COMMAND_REPEAT_OFF) - .set_announcement(true) - .perform(); - id(voice_assistant_media_player)->set_playlist_delay_ms(speaker::AudioPipelineType::ANNOUNCEMENT, 0); - -switch: - - platform: gpio - id: speaker_switch - icon: "mdi:speaker" - pin: - number: GPIO9 - - platform: template - name: "${friendly_name} Enable Voice Assistant" - id: use_wake_word - optimistic: true - restore_mode: RESTORE_DEFAULT_ON - icon: mdi:assistant - on_turn_on: - - logger.log: "switch on" - - if: - condition: - lambda: return !id(init_in_progress); - then: - - logger.log: "condition 1" - - lambda: id(voice_assistant_phase) = ${voice_assist_idle_phase_id}; - - voice_assistant.stop - - delay: 1s - - if: - condition: - not: - - voice_assistant.is_running - then: - - logger.log: "Starting micro wake word" - - micro_wake_word.start: - - script.execute: reset_led - on_turn_off: - - if: - condition: - lambda: return !id(init_in_progress); - then: - - voice_assistant.stop - - micro_wake_word.stop - - lambda: id(voice_assistant_phase) = ${voice_assist_muted_phase_id}; - - script.execute: reset_led - - platform: template - name: "${friendly_name} Enable Ducking" - id: ducking_switch - optimistic: true - restore_mode: RESTORE_DEFAULT_ON - icon: mdi:volume-high - - platform: template - id: timer_ringing - optimistic: true - internal: true - restore_mode: ALWAYS_OFF - on_turn_on: - - mixer_speaker.apply_ducking: - id: media_mixing_input - decibel_reduction: 20 - duration: 0.0s - - script.execute: ring_timer - - light.turn_on: - id: rgb_front_led - blue: 0% - red: 0% - green: 100% - brightness: 85% - effect: "Fast Pulse" - - delay: 15min - - switch.turn_off: timer_ringing - on_turn_off: - - script.execute: disable_repeat - - if: - condition: - media_player.is_announcing: - then: - media_player.stop: - announcement: true - - mixer_speaker.apply_ducking: - id: media_mixing_input - decibel_reduction: 0 - duration: 1.0s - - script.execute: reset_led - - platform: template - id: wake_sound - name: "${friendly_name} Wake Sound" - optimistic: true - restore_mode: RESTORE_DEFAULT_ON - icon: "mdi:bullhorn" - -button: - - platform: restart - id: restart_btn - name: "${friendly_name} REBOOT" - -sensor: - - platform: wifi_signal - name: "${friendly_name} WiFi Signal" - update_interval: 60s - - platform: uptime - name: "${friendly_name} Uptime" - update_interval: 60s - -text_sensor: - - platform: wifi_info - ip_address: - name: "${friendly_name} IP Address" - - platform: version - name: "${friendly_name} ESPHome Version" \ No newline at end of file diff --git a/firmware/esphome/esphome-2025.4.1/echo-duo-b-voice-assist.yaml b/firmware/esphome/esphome-2025.4.1/echo-duo-b-voice-assist.yaml deleted file mode 100644 index 07b8123..0000000 --- a/firmware/esphome/esphome-2025.4.1/echo-duo-b-voice-assist.yaml +++ /dev/null @@ -1,565 +0,0 @@ -# based on https://github.com/HA-TB303/ultimatesensor_mini/blob/main/ultimatesensor_mini_v1_basic.yaml - -substitutions: - name: esphome-web-32fb54 - friendly_name: esparagus-duo - ## Preferred wake word, choose from: https://github.com/esphome/micro-wake-word-models/tree/main/models - # github://esphome/micro-wake-word-models/models/okay_nabu.json - wake_word: okay_nabu - ## Set the buildt in speaker volume percentage to prevent it from distorting - speaker_volume: 50% - ## Phases of the Voice Assistant - ## IDLE: The voice assistant is ready to be triggered by a wake-word - voice_assist_idle_phase_id: '1' - ## LISTENING: The voice assistant is ready to listen to a voice command (after being triggered by the wake word) - voice_assist_listening_phase_id: '2' - ## THINKING: The voice assistant is currently processing the command - voice_assist_thinking_phase_id: '3' - ## REPLYING: The voice assistant is replying to the command - voice_assist_replying_phase_id: '4' - ## NOT_READY: The voice assistant is not ready - voice_assist_not_ready_phase_id: '10' - ## ERROR: The voice assistant encountered an error - voice_assist_error_phase_id: '11' - ## MUTED: The voice assistant is muted and will not reply to a wake-word - voice_assist_muted_phase_id: '12' - -esphome: - name: ${name} - friendly_name: ${friendly_name} - name_add_mac_suffix: false - project: - name: esphome.web - version: '1.0' - min_version: 2025.4.1 - platformio_options: - build_flags: -DBOARD_HAS_PSRAM - board_build.flash_mode: dio - board_upload.maximum_size: 16777216 - on_boot: - priority: 200.0 - then: - - media_player.volume_set: - id: voice_assistant_media_player - volume: ${speaker_volume} - - switch.turn_on: - id: speaker_switch - - delay: 30s - - if: - condition: - lambda: return id(init_in_progress); - then: - - lambda: id(init_in_progress) = false; - -esp32: - board: esp32-s3-devkitc-1 - variant: ESP32S3 - flash_size: 16MB - framework: - type: esp-idf - sdkconfig_options: - CONFIG_ESP32_S3_BOX_BOARD: "y" - CONFIG_ESP32_WIFI_STATIC_RX_BUFFER_NUM: "16" - CONFIG_ESP32_WIFI_DYNAMIC_RX_BUFFER_NUM: "512" - CONFIG_TCPIP_RECVMBOX_SIZE: "512" - CONFIG_TCP_SND_BUF_DEFAULT: "65535" - CONFIG_TCP_WND_DEFAULT: "512000" - CONFIG_TCP_RECVMBOX_SIZE: "512" - -# Enable logging -logger: - level: DEBUG - -# Enable Home Assistant API -api: - -# Allow Over-The-Air updates -ota: - platform: esphome - password: !secret esphome_ota_password - -wifi: - ssid: !secret esphome_wifi_ssid - password: !secret esphome_wifi_password - ap: - ssid: "$name Hotspot" - password: !secret esphome_ap_password - -### Optional if you want ethernet (then remove above wifi config) -#ethernet: -# type: W5500 -# clk_pin: GPIO12 -# mosi_pin: GPIO11 -# miso_pin: GPIO13 -# cs_pin: GPIO10 -# interrupt_pin: GPIO6 -# reset_pin: GPIO5 - -captive_portal: - -globals: - - id: init_in_progress - type: bool - restore_value: no - initial_value: 'true' - - id: voice_assistant_phase - type: int - restore_value: no - initial_value: ${voice_assist_not_ready_phase_id} - -button: - - platform: restart - id: restart_btn - name: "${friendly_name} REBOOT" - -psram: - mode: octal - speed: 80MHz - -i2s_audio: - - id: i2s_in - i2s_lrclk_pin: GPIO7 - i2s_bclk_pin: GPIO16 - - - id: i2s_out - i2s_lrclk_pin: GPIO8 - i2s_bclk_pin: GPIO18 - - -microphone: - - platform: i2s_audio - adc_type: external - id: voice_assistant_mic - i2s_audio_id: i2s_in - i2s_din_pin: GPIO15 - pdm: false - channel: right - sample_rate: 16000 - bits_per_sample: 32bit - -# use speaker plattform for mix announcements and media player - -speaker: - - platform: i2s_audio - id: i2s_audio_speaker - i2s_audio_id: i2s_out - i2s_dout_pin: GPIO17 - dac_type: external - channel: "stereo" - i2s_mode: primary - buffer_duration: 100ms - timeout: never - sample_rate: 48000 - bits_per_sample: 32bit - - - platform: mixer - id: mixing_speaker - output_speaker: i2s_audio_speaker - num_channels: 2 - source_speakers: - - id: announcement_mixing_input - timeout: never - - id: media_mixing_input - timeout: never - - # Virtual speakers to resample each pipelines' audio, if necessary, as the mixer speaker requires the same sample rate - - platform: resampler - id: announcement_resampling_speaker - output_speaker: announcement_mixing_input - sample_rate: 48000 - bits_per_sample: 16 - - platform: resampler - id: media_resampling_speaker - output_speaker: media_mixing_input - sample_rate: 48000 - bits_per_sample: 16 - - -media_player: - - platform: speaker - id: voice_assistant_media_player - name: "Media Player" - internal: false - announcement_pipeline: - speaker: announcement_resampling_speaker - format: FLAC # FLAC is the least processor intensive codec - num_channels: 1 # Stereo audio is unnecessary for announcements - sample_rate: 48000 - media_pipeline: - speaker: media_resampling_speaker - format: FLAC # FLAC is the least processor intensive codec - num_channels: 2 - sample_rate: 48000 - on_announcement: - - mixer_speaker.apply_ducking: - id: media_mixing_input - decibel_reduction: 20 - duration: 0.0s - on_state: - if: - condition: - and: - - switch.is_off: timer_ringing - - not: - voice_assistant.is_running: - - not: - media_player.is_announcing: - then: - - mixer_speaker.apply_ducking: - id: media_mixing_input - decibel_reduction: 0 - duration: 1.0s - 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 - -light: - - platform: esp32_rmt_led_strip - id: rgb_front_led - name: "${friendly_name} Front LED" - rgb_order: GRB - pin: GPIO42 - num_leds: 3 - rmt_symbols: 96 - chipset: ws2812 - default_transition_length: 0s - effects: - - addressable_color_wipe: - name: "Connecting" - colors: - - red: 60% - green: 60% - blue: 60% - num_leds: 1 - - red: 60% - green: 60% - blue: 0% - num_leds: 1 - add_led_interval: 100ms - reverse: true - - pulse: - name: "Fast Pulse" - transition_length: 100ms - update_interval: 100ms - min_brightness: 50% - max_brightness: 100% - - pulse: - name: "Slow Pulse" - transition_length: 250ms - update_interval: 250ms - min_brightness: 50% - max_brightness: 100% - -micro_wake_word: - models: - - model: ${wake_word} - microphone: voice_assistant_mic - on_wake_word_detected: - - if: - condition: - switch.is_on: use_wake_word - then: - - light.turn_on: - id: rgb_front_led - blue: 0% - red: 0% - green: 100% - brightness: 75% - effect: "Slow Pulse" - # If a timer is ringing: Stop it, do not start the voice assistant (We can stop timer from voice!) - - if: - condition: - switch.is_on: timer_ringing - then: - - switch.turn_off: timer_ringing - - micro_wake_word.start: - # Stop voice assistant if running - else: - - if: - condition: - switch.is_off: ducking_switch - then: - media_player.stop: - - if: - condition: - voice_assistant.is_running: - then: - voice_assistant.stop: - # Stop any other media player announcement - else: - - if: - condition: - media_player.is_announcing: - then: - - media_player.stop: - announcement: true - # Start the voice assistant and play the wake sound, if enabled - else: - - 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: - - -voice_assistant: - id: va - microphone: voice_assistant_mic - media_player: voice_assistant_media_player - use_wake_word: false - #vad_threshold: 3 - #noise_suppression_level: 1 - auto_gain: 31dBFS - #volume_multiplier: 15.0 - on_client_connected: - - lambda: id(init_in_progress) = false; - - if: - condition: - switch.is_on: use_wake_word - then: - - micro_wake_word.start: - - lambda: id(voice_assistant_phase) = ${voice_assist_idle_phase_id}; - - script.execute: reset_led - else: - - lambda: id(voice_assistant_phase) = ${voice_assist_muted_phase_id}; - on_client_disconnected: - - lambda: id(voice_assistant_phase) = ${voice_assist_not_ready_phase_id}; - - voice_assistant.stop - - micro_wake_word.stop - - light.turn_on: - id: rgb_front_led - blue: 0% - red: 100% - green: 100% - brightness: 50% - effect: connecting - on_listening: - - light.turn_on: - id: rgb_front_led - blue: 100% - red: 0% - green: 0% - brightness: 50% - effect: "Slow Pulse" - on_tts_start: - - light.turn_on: - id: rgb_front_led - blue: 0% - red: 0% - green: 100% - brightness: 50% - effect: "Slow Pulse" - on_start: - - mixer_speaker.apply_ducking: - id: media_mixing_input - decibel_reduction: 20 # Number of dB quieter; higher implies more quiet, 0 implies full volume - duration: 0.0s # The duration of the transition (default is no transition) - on_end: - then: - - wait_until: - not: - voice_assistant.is_running: - # Stop ducking audio. - - mixer_speaker.apply_ducking: - id: media_mixing_input - decibel_reduction: 0 - duration: 1.0s - - light.turn_off: - id: rgb_front_led - - voice_assistant.stop - - script.execute: reset_led - - if: - condition: - switch.is_on: use_wake_word - then: - - micro_wake_word.start: - on_error: - - light.turn_on: - id: rgb_front_led - blue: 0% - red: 100% - green: 0% - brightness: 100% - effect: none - - delay: 1s - - script.execute: reset_led - - script.wait: reset_led - - lambda: |- - if (code == "wake-provider-missing" || code == "wake-engine-missing") { - id(use_wake_word).turn_off(); - } - - if: - condition: - switch.is_on: use_wake_word - then: - - micro_wake_word.start: - - script.execute: reset_led - on_timer_finished: - - switch.turn_on: timer_ringing - -script: - - id: reset_led - then: - - if: - condition: - switch.is_on: use_wake_word - then: - - light.turn_on: - id: rgb_front_led - blue: 100% - red: 0% - green: 0% - brightness: 25% - effect: none - else: - - light.turn_off: rgb_front_led - # Script executed when we want to play sounds on the device. - - id: play_sound - parameters: - priority: bool - sound_file: "audio::AudioFile*" - then: - - lambda: |- - if (priority) { - id(voice_assistant_media_player) - ->make_call() - .set_command(media_player::MediaPlayerCommand::MEDIA_PLAYER_COMMAND_STOP) - .set_announcement(true) - .perform(); - } - if ( (id(voice_assistant_media_player).state != media_player::MediaPlayerState::MEDIA_PLAYER_STATE_ANNOUNCING ) || priority) { - id(voice_assistant_media_player) - ->play_file(sound_file, true, false); - } - # Script executed when the timer is ringing, to playback sounds. - - id: enable_repeat_one - then: - # Turn on the repeat mode and pause for 500 ms between playlist items/repeats - - lambda: |- - id(voice_assistant_media_player) - ->make_call() - .set_command(media_player::MediaPlayerCommand::MEDIA_PLAYER_COMMAND_REPEAT_ONE) - .set_announcement(true) - .perform(); - id(voice_assistant_media_player)->set_playlist_delay_ms(speaker::AudioPipelineType::ANNOUNCEMENT, 500); - - id: ring_timer - then: - - script.execute: enable_repeat_one - - script.execute: - id: play_sound - priority: true - sound_file: !lambda return id(timer_finished_sound); - - id: disable_repeat - then: - # Turn off the repeat mode and pause for 0 ms between playlist items/repeats - - lambda: |- - id(voice_assistant_media_player) - ->make_call() - .set_command(media_player::MediaPlayerCommand::MEDIA_PLAYER_COMMAND_REPEAT_OFF) - .set_announcement(true) - .perform(); - id(voice_assistant_media_player)->set_playlist_delay_ms(speaker::AudioPipelineType::ANNOUNCEMENT, 0); - - -switch: - - platform: gpio - id: speaker_switch - icon: "mdi:speaker" - pin: - number: GPIO9 - - platform: template - name: Enable Voice Assistant - id: use_wake_word - optimistic: true - restore_mode: RESTORE_DEFAULT_ON - icon: mdi:assistant - ## When the switch is turned on (on Home Assistant): - ## Start the voice assistant component - ## Set the correct phase and run the script to refresh the LED status - on_turn_on: - - logger.log: "switch on" - - if: - condition: - lambda: return !id(init_in_progress); - then: - - logger.log: "condition 1" - - lambda: id(voice_assistant_phase) = ${voice_assist_idle_phase_id}; - - voice_assistant.stop - - delay: 1s - - if: - condition: - not: - - voice_assistant.is_running - then: - - logger.log: "Starting micro wake word" - - micro_wake_word.start: - - script.execute: reset_led - on_turn_off: - - if: - condition: - lambda: return !id(init_in_progress); - then: - - voice_assistant.stop - - micro_wake_word.stop - - lambda: id(voice_assistant_phase) = ${voice_assist_muted_phase_id}; - - script.execute: reset_led - - - - platform: template - name: "Enable ducking" - id: ducking_switch - optimistic: true - restore_mode: RESTORE_DEFAULT_ON - icon: mdi:volume-high - - - platform: template - id: timer_ringing - optimistic: true - internal: true - restore_mode: ALWAYS_OFF - on_turn_on: - - mixer_speaker.apply_ducking: - id: media_mixing_input - decibel_reduction: 20 - duration: 0.0s - - script.execute: ring_timer - - light.turn_on: - id: rgb_front_led - blue: 0% - red: 0% - green: 100% - brightness: 85% - effect: "Fast Pulse" - - delay: 15min - - switch.turn_off: timer_ringing - on_turn_off: - - script.execute: disable_repeat - # Stop any current annoucement (ie: stop the timer ring mid playback) - - if: - condition: - media_player.is_announcing: - then: - media_player.stop: - announcement: true - - mixer_speaker.apply_ducking: - id: media_mixing_input - decibel_reduction: 0 - duration: 1.0s - - script.execute: reset_led - - - - platform: template - id: wake_sound - name: "${friendly_name} Wake Sound" - optimistic: true - restore_mode: RESTORE_DEFAULT_ON - icon: "mdi:bullhorn" - diff --git a/firmware/esphome/esphome-2025.4.1/echo-solo-b-voice-assist.yaml b/firmware/esphome/esphome-2025.4.1/echo-solo-b-voice-assist.yaml deleted file mode 100644 index 2316867..0000000 --- a/firmware/esphome/esphome-2025.4.1/echo-solo-b-voice-assist.yaml +++ /dev/null @@ -1,553 +0,0 @@ -substitutions: - name: esphome-web-a86af8 - friendly_name: esparagus-solo - ## Preferred wake word, choose from: https://github.com/esphome/micro-wake-word-models/tree/main/models - # github://esphome/micro-wake-word-models/models/okay_nabu.json - wake_word: okay_nabu - ## Set the buildt in speaker volume percentage to prevent it from distorting - speaker_volume: 50% - ## Phases of the Voice Assistant - ## IDLE: The voice assistant is ready to be triggered by a wake-word - voice_assist_idle_phase_id: '1' - ## LISTENING: The voice assistant is ready to listen to a voice command (after being triggered by the wake word) - voice_assist_listening_phase_id: '2' - ## THINKING: The voice assistant is currently processing the command - voice_assist_thinking_phase_id: '3' - ## REPLYING: The voice assistant is replying to the command - voice_assist_replying_phase_id: '4' - ## NOT_READY: The voice assistant is not ready - voice_assist_not_ready_phase_id: '10' - ## ERROR: The voice assistant encountered an error - voice_assist_error_phase_id: '11' - ## MUTED: The voice assistant is muted and will not reply to a wake-word - voice_assist_muted_phase_id: '12' - -esphome: - name: ${name} - friendly_name: ${friendly_name} - name_add_mac_suffix: false - project: - name: esphome.web - version: '1.0' - min_version: 2025.4.1 - platformio_options: - build_flags: -DBOARD_HAS_PSRAM - board_build.flash_mode: dio - board_upload.maximum_size: 16777216 - on_boot: - priority: 200.0 - then: - - media_player.volume_set: - id: voice_assistant_media_player - volume: ${speaker_volume} - - switch.turn_on: - id: speaker_switch - - delay: 30s - - if: - condition: - lambda: return id(init_in_progress); - then: - - lambda: id(init_in_progress) = false; - -esp32: - board: esp32-s3-devkitc-1 - variant: ESP32S3 - flash_size: 16MB - framework: - type: esp-idf - sdkconfig_options: - CONFIG_ESP32_S3_BOX_BOARD: "y" - CONFIG_ESP32_WIFI_STATIC_RX_BUFFER_NUM: "16" - CONFIG_ESP32_WIFI_DYNAMIC_RX_BUFFER_NUM: "512" - CONFIG_TCPIP_RECVMBOX_SIZE: "512" - CONFIG_TCP_SND_BUF_DEFAULT: "65535" - CONFIG_TCP_WND_DEFAULT: "512000" - CONFIG_TCP_RECVMBOX_SIZE: "512" - -# Enable logging -logger: - level: DEBUG - -# Enable Home Assistant API -api: - -# Allow Over-The-Air updates -ota: - platform: esphome - password: !secret esphome_ota_password - -wifi: - ssid: !secret esphome_wifi_ssid - password: !secret esphome_wifi_password - ap: - ssid: "$name Hotspot" - password: !secret esphome_ap_password - -captive_portal: - -globals: - - id: init_in_progress - type: bool - restore_value: no - initial_value: 'true' - - id: voice_assistant_phase - type: int - restore_value: no - initial_value: ${voice_assist_not_ready_phase_id} - -button: - - platform: restart - id: restart_btn - name: "${friendly_name} REBOOT" - -psram: - mode: octal - speed: 80MHz - -i2s_audio: - - id: i2s_in - i2s_lrclk_pin: GPIO7 - i2s_bclk_pin: GPIO16 - - - id: i2s_out - i2s_lrclk_pin: GPIO8 - i2s_bclk_pin: GPIO18 - - -microphone: - - platform: i2s_audio - adc_type: external - id: voice_assistant_mic - i2s_audio_id: i2s_in - i2s_din_pin: GPIO15 - pdm: false - channel: right - sample_rate: 16000 - bits_per_sample: 32bit - -# use speaker plattform for mix announcements and media player - -speaker: - - platform: i2s_audio - id: i2s_audio_speaker - i2s_audio_id: i2s_out - i2s_dout_pin: GPIO17 - dac_type: external - channel: "mono" - i2s_mode: primary - buffer_duration: 100ms - timeout: never - sample_rate: 48000 - bits_per_sample: 32bit - - - platform: mixer - id: mixing_speaker - output_speaker: i2s_audio_speaker - num_channels: 1 - source_speakers: - - id: announcement_mixing_input - timeout: never - - id: media_mixing_input - timeout: never - - # Virtual speakers to resample each pipelines' audio, if necessary, as the mixer speaker requires the same sample rate - - platform: resampler - id: announcement_resampling_speaker - output_speaker: announcement_mixing_input - sample_rate: 48000 - bits_per_sample: 16 - - platform: resampler - id: media_resampling_speaker - output_speaker: media_mixing_input - sample_rate: 48000 - bits_per_sample: 16 - - -media_player: - - platform: speaker - id: voice_assistant_media_player - name: "Media Player" - internal: false - announcement_pipeline: - speaker: announcement_resampling_speaker - format: FLAC # FLAC is the least processor intensive codec - num_channels: 1 # Stereo audio is unnecessary for announcements - sample_rate: 48000 - media_pipeline: - speaker: media_resampling_speaker - format: FLAC # FLAC is the least processor intensive codec - num_channels: 1 - sample_rate: 48000 - on_announcement: - - mixer_speaker.apply_ducking: - id: media_mixing_input - decibel_reduction: 20 - duration: 0.0s - on_state: - if: - condition: - and: - - switch.is_off: timer_ringing - - not: - voice_assistant.is_running: - - not: - media_player.is_announcing: - then: - - mixer_speaker.apply_ducking: - id: media_mixing_input - decibel_reduction: 0 - duration: 1.0s - 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 - -light: - - platform: esp32_rmt_led_strip - id: rgb_front_led - name: "${friendly_name} Front LED" - rgb_order: GRB - pin: GPIO42 - num_leds: 2 - rmt_symbols: 96 - chipset: ws2812 - default_transition_length: 0s - effects: - - addressable_color_wipe: - name: "Connecting" - colors: - - red: 60% - green: 60% - blue: 60% - num_leds: 1 - - red: 60% - green: 60% - blue: 0% - num_leds: 1 - add_led_interval: 100ms - reverse: true - - pulse: - name: "Fast Pulse" - transition_length: 100ms - update_interval: 100ms - min_brightness: 50% - max_brightness: 100% - - pulse: - name: "Slow Pulse" - transition_length: 250ms - update_interval: 250ms - min_brightness: 50% - max_brightness: 100% - -micro_wake_word: - models: - - model: ${wake_word} - microphone: voice_assistant_mic - on_wake_word_detected: - - if: - condition: - switch.is_on: use_wake_word - then: - - light.turn_on: - id: rgb_front_led - blue: 0% - red: 0% - green: 100% - brightness: 75% - effect: "Slow Pulse" - # If a timer is ringing: Stop it, do not start the voice assistant (We can stop timer from voice!) - - if: - condition: - switch.is_on: timer_ringing - then: - - switch.turn_off: timer_ringing - - micro_wake_word.start: - # Stop voice assistant if running - else: - - if: - condition: - switch.is_off: ducking_switch - then: - media_player.stop: - - if: - condition: - voice_assistant.is_running: - then: - voice_assistant.stop: - # Stop any other media player announcement - else: - - if: - condition: - media_player.is_announcing: - then: - - media_player.stop: - announcement: true - # Start the voice assistant and play the wake sound, if enabled - else: - - 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: - - -voice_assistant: - id: va - microphone: voice_assistant_mic - media_player: voice_assistant_media_player - use_wake_word: false - #vad_threshold: 3 - #noise_suppression_level: 1 - auto_gain: 31dBFS - #volume_multiplier: 15.0 - on_client_connected: - - lambda: id(init_in_progress) = false; - - if: - condition: - switch.is_on: use_wake_word - then: - - micro_wake_word.start: - - lambda: id(voice_assistant_phase) = ${voice_assist_idle_phase_id}; - - script.execute: reset_led - else: - - lambda: id(voice_assistant_phase) = ${voice_assist_muted_phase_id}; - on_client_disconnected: - - lambda: id(voice_assistant_phase) = ${voice_assist_not_ready_phase_id}; - - voice_assistant.stop - - micro_wake_word.stop - - light.turn_on: - id: rgb_front_led - blue: 0% - red: 100% - green: 100% - brightness: 50% - effect: connecting - on_listening: - - light.turn_on: - id: rgb_front_led - blue: 100% - red: 0% - green: 0% - brightness: 50% - effect: "Slow Pulse" - on_tts_start: - - light.turn_on: - id: rgb_front_led - blue: 0% - red: 0% - green: 100% - brightness: 50% - effect: "Slow Pulse" - on_start: - - mixer_speaker.apply_ducking: - id: media_mixing_input - decibel_reduction: 20 # Number of dB quieter; higher implies more quiet, 0 implies full volume - duration: 0.0s # The duration of the transition (default is no transition) - on_end: - then: - - wait_until: - not: - voice_assistant.is_running: - # Stop ducking audio. - - mixer_speaker.apply_ducking: - id: media_mixing_input - decibel_reduction: 0 - duration: 1.0s - - light.turn_off: - id: rgb_front_led - - voice_assistant.stop - - script.execute: reset_led - - if: - condition: - switch.is_on: use_wake_word - then: - - micro_wake_word.start: - on_error: - - light.turn_on: - id: rgb_front_led - blue: 0% - red: 100% - green: 0% - brightness: 100% - effect: none - - delay: 1s - - script.execute: reset_led - - script.wait: reset_led - - lambda: |- - if (code == "wake-provider-missing" || code == "wake-engine-missing") { - id(use_wake_word).turn_off(); - } - - if: - condition: - switch.is_on: use_wake_word - then: - - micro_wake_word.start: - - script.execute: reset_led - on_timer_finished: - - switch.turn_on: timer_ringing - -script: - - id: reset_led - then: - - if: - condition: - switch.is_on: use_wake_word - then: - - light.turn_on: - id: rgb_front_led - blue: 100% - red: 0% - green: 0% - brightness: 25% - effect: none - else: - - light.turn_off: rgb_front_led - # Script executed when we want to play sounds on the device. - - id: play_sound - parameters: - priority: bool - sound_file: "audio::AudioFile*" - then: - - lambda: |- - if (priority) { - id(voice_assistant_media_player) - ->make_call() - .set_command(media_player::MediaPlayerCommand::MEDIA_PLAYER_COMMAND_STOP) - .set_announcement(true) - .perform(); - } - if ( (id(voice_assistant_media_player).state != media_player::MediaPlayerState::MEDIA_PLAYER_STATE_ANNOUNCING ) || priority) { - id(voice_assistant_media_player) - ->play_file(sound_file, true, false); - } - # Script executed when the timer is ringing, to playback sounds. - - id: enable_repeat_one - then: - # Turn on the repeat mode and pause for 500 ms between playlist items/repeats - - lambda: |- - id(voice_assistant_media_player) - ->make_call() - .set_command(media_player::MediaPlayerCommand::MEDIA_PLAYER_COMMAND_REPEAT_ONE) - .set_announcement(true) - .perform(); - id(voice_assistant_media_player)->set_playlist_delay_ms(speaker::AudioPipelineType::ANNOUNCEMENT, 500); - - id: ring_timer - then: - - script.execute: enable_repeat_one - - script.execute: - id: play_sound - priority: true - sound_file: !lambda return id(timer_finished_sound); - - id: disable_repeat - then: - # Turn off the repeat mode and pause for 0 ms between playlist items/repeats - - lambda: |- - id(voice_assistant_media_player) - ->make_call() - .set_command(media_player::MediaPlayerCommand::MEDIA_PLAYER_COMMAND_REPEAT_OFF) - .set_announcement(true) - .perform(); - id(voice_assistant_media_player)->set_playlist_delay_ms(speaker::AudioPipelineType::ANNOUNCEMENT, 0); - - -switch: - - platform: gpio - id: speaker_switch - icon: "mdi:speaker" - pin: - number: GPIO9 - - platform: template - name: Enable Voice Assistant - id: use_wake_word - optimistic: true - restore_mode: RESTORE_DEFAULT_ON - icon: mdi:assistant - ## When the switch is turned on (on Home Assistant): - ## Start the voice assistant component - ## Set the correct phase and run the script to refresh the LED status - on_turn_on: - - logger.log: "switch on" - - if: - condition: - lambda: return !id(init_in_progress); - then: - - logger.log: "condition 1" - - lambda: id(voice_assistant_phase) = ${voice_assist_idle_phase_id}; - - voice_assistant.stop - - delay: 1s - - if: - condition: - not: - - voice_assistant.is_running - then: - - logger.log: "Starting micro wake word" - - micro_wake_word.start: - - script.execute: reset_led - on_turn_off: - - if: - condition: - lambda: return !id(init_in_progress); - then: - - voice_assistant.stop - - micro_wake_word.stop - - lambda: id(voice_assistant_phase) = ${voice_assist_muted_phase_id}; - - script.execute: reset_led - - - - platform: template - name: "Enable ducking" - id: ducking_switch - optimistic: true - restore_mode: RESTORE_DEFAULT_ON - icon: mdi:volume-high - - - platform: template - id: timer_ringing - optimistic: true - internal: true - restore_mode: ALWAYS_OFF - on_turn_on: - - mixer_speaker.apply_ducking: - id: media_mixing_input - decibel_reduction: 20 - duration: 0.0s - - script.execute: ring_timer - - light.turn_on: - id: rgb_front_led - blue: 0% - red: 0% - green: 100% - brightness: 85% - effect: "Fast Pulse" - - delay: 15min - - switch.turn_off: timer_ringing - on_turn_off: - - script.execute: disable_repeat - # Stop any current annoucement (ie: stop the timer ring mid playback) - - if: - condition: - media_player.is_announcing: - then: - media_player.stop: - announcement: true - - mixer_speaker.apply_ducking: - id: media_mixing_input - decibel_reduction: 0 - duration: 1.0s - - script.execute: reset_led - - - - platform: template - id: wake_sound - name: "${friendly_name} Wake Sound" - optimistic: true - restore_mode: RESTORE_DEFAULT_ON - icon: "mdi:bullhorn" - diff --git a/firmware/esphome/packages-archive/-audio-voice-assist.yaml b/firmware/esphome/packages-archive/-audio-voice-assist.yaml new file mode 100644 index 0000000..b696f79 --- /dev/null +++ b/firmware/esphome/packages-archive/-audio-voice-assist.yaml @@ -0,0 +1,53 @@ +# ============================================================ +# Audio - I2S Audio Setup and Speaker Configuration +# ============================================================ +# Configures I2S DAC, mixer, and resampler for multi-source audio +# Requires substitutions: +# - i2s_lrclk_pin +# - i2s_bclk_pin +# - i2s_dout_pin +# - task_stack_in_psram +# ============================================================ + +i2s_audio: +- id: i2s_out + i2s_lrclk_pin: ${i2s_lrclk_pin} + i2s_bclk_pin: ${i2s_bclk_pin} + +speaker: + # Primary I2S speaker + - platform: i2s_audio + id: speaker_id + i2s_audio_id: i2s_out + dac_type: external + i2s_dout_pin: ${i2s_dout_pin} + i2s_mode: primary + buffer_duration: 500ms # INCREASED from 100ms (reduces glitches) + channel: stereo + sample_rate: 48000 + timeout: never + bits_per_sample: 16bit # Keep at 32bit for quality + + # Mixer to combine announcement and media feeds + - platform: mixer + id: mixing_speaker + output_speaker: speaker_id + num_channels: 2 + source_speakers: + - id: announcement_mixing_input + timeout: never + - id: media_mixing_input + timeout: never + + # OPTIMIZED resamplers - 32bit for better quality + - platform: resampler + id: announcement_resampling_speaker + output_speaker: announcement_mixing_input + sample_rate: 48000 + bits_per_sample: 16 # Changed from 16 to 32 + + - platform: resampler + id: media_resampling_speaker + output_speaker: media_mixing_input + sample_rate: 48000 + bits_per_sample: 16 # Changed from 16 to 32 diff --git a/firmware/esphome/packages-archive/-audio.yaml b/firmware/esphome/packages-archive/-audio.yaml new file mode 100644 index 0000000..85e03b9 --- /dev/null +++ b/firmware/esphome/packages-archive/-audio.yaml @@ -0,0 +1,50 @@ +# ============================================================ +# Audio - I2S Audio Setup and Speaker Configuration +# ============================================================ +# Configures I2S DAC, mixer, and resampler for multi-source audio +# Requires substitutions: +# - i2s_lrclk_pin +# - i2s_bclk_pin +# - i2s_dout_pin +# - task_stack_in_psram +# ============================================================ + +i2s_audio: + id: i2s_out + i2s_lrclk_pin: ${i2s_lrclk_pin} + i2s_bclk_pin: ${i2s_bclk_pin} + +speaker: + # Primary I2S speaker (PCM5100 DAC output) + - platform: i2s_audio + id: speaker_id + dac_type: external + i2s_dout_pin: ${i2s_dout_pin} + channel: stereo + sample_rate: 48000 + timeout: never + + # Mixer to combine announcement and media feeds + - platform: mixer + id: mixer_speaker_id + task_stack_in_psram: ${task_stack_in_psram} + output_speaker: speaker_id + source_speakers: + - id: announcement_spk_mixer_input + timeout: never + - id: media_spk_mixer_input + timeout: never + + # Resampler for media playback + - platform: resampler + id: media_spk_resampling_input + task_stack_in_psram: ${task_stack_in_psram} + sample_rate: 48000 + output_speaker: media_spk_mixer_input + + # Resampler for announcements + - platform: resampler + id: announcement_spk_resampling_input + task_stack_in_psram: ${task_stack_in_psram} + sample_rate: 48000 + output_speaker: announcement_spk_mixer_input diff --git a/firmware/esphome/packages-archive/-voice-assist-addon-dac-enable.yaml b/firmware/esphome/packages-archive/-voice-assist-addon-dac-enable.yaml new file mode 100644 index 0000000..cc5b277 --- /dev/null +++ b/firmware/esphome/packages-archive/-voice-assist-addon-dac-enable.yaml @@ -0,0 +1,15 @@ + +# ============================================================ +# Voice Assist - External DAC addon +# ============================================================ +# Extends voice-assist.yaml to attach an external DAC component +# to the existing I2S speaker. +# Dependencies: voice-assist.yaml +# ============================================================ + +esphome: + on_boot: + priority: 220.0 + then: + - switch.turn_on: + id: enable_dac \ No newline at end of file diff --git a/firmware/esphome/packages/voice-assist-media-player.yaml b/firmware/esphome/packages-archive/-voice-assist-media-player.yaml similarity index 96% rename from firmware/esphome/packages/voice-assist-media-player.yaml rename to firmware/esphome/packages-archive/-voice-assist-media-player.yaml index 081b0cb..4afb48c 100644 --- a/firmware/esphome/packages/voice-assist-media-player.yaml +++ b/firmware/esphome/packages-archive/-voice-assist-media-player.yaml @@ -24,12 +24,14 @@ media_player: num_channels: 2 sample_rate: 48000 buffer_size: 2000000 # INCREASED buffer (2MB vs default 1MB) - task_stack_in_psram: true # Use PSRAM for audio tasks + task_stack_in_psram: ${task_stack_in_psram} + on_announcement: - mixer_speaker.apply_ducking: - id: media_mixing_input + id: media_mixer_input decibel_reduction: 20 duration: 0.0s + on_state: if: condition: @@ -41,9 +43,10 @@ media_player: media_player.is_announcing: then: - mixer_speaker.apply_ducking: - id: media_mixing_input + id: media_mixer_input decibel_reduction: 0 duration: 1.0s + files: - id: timer_finished_sound file: https://github.com/esphome/home-assistant-voice-pe/raw/dev/sounds/timer_finished.flac diff --git a/firmware/esphome/packages/audio.yaml b/firmware/esphome/packages/audio.yaml index dcaf43a..27d4133 100644 --- a/firmware/esphome/packages/audio.yaml +++ b/firmware/esphome/packages/audio.yaml @@ -9,44 +9,47 @@ # - task_stack_in_psram # ============================================================ +substitutions: + audio_buffer_duration: 100ms + i2s_audio: - id: i2s_out i2s_lrclk_pin: ${i2s_lrclk_pin} i2s_bclk_pin: ${i2s_bclk_pin} - + speaker: - # Primary I2S speaker - platform: i2s_audio - id: speaker_id + id: i2s_speaker_id i2s_audio_id: i2s_out - dac_type: external + sample_rate: 48000 i2s_dout_pin: ${i2s_dout_pin} - i2s_mode: primary - buffer_duration: 500ms # INCREASED from 100ms (reduces glitches) + bits_per_sample: 16bit + dac_type: external channel: stereo - sample_rate: 48000 timeout: never - bits_per_sample: 16bit # Keep at 32bit for quality + buffer_duration: ${audio_buffer_duration} - # Mixer to combine announcement and media feeds - platform: mixer - id: mixing_speaker - output_speaker: speaker_id + id: mixer_speaker_id + output_speaker: i2s_speaker_id num_channels: 2 + task_stack_in_psram: ${task_stack_in_psram} source_speakers: - - id: announcement_mixing_input + - id: announcement_mixer_input timeout: never - - id: media_mixing_input + - id: media_mixer_input timeout: never - # OPTIMIZED resamplers - 32bit for better quality - platform: resampler - id: announcement_resampling_speaker - output_speaker: announcement_mixing_input + id: media_resampling_speaker + output_speaker: media_mixer_input + task_stack_in_psram: ${task_stack_in_psram} sample_rate: 48000 - bits_per_sample: 16 # Changed from 16 to 32 + bits_per_sample: 16 + - platform: resampler - id: media_resampling_speaker - output_speaker: media_mixing_input + id: announcement_resampling_speaker + output_speaker: announcement_mixer_input + task_stack_in_psram: ${task_stack_in_psram} sample_rate: 48000 - bits_per_sample: 16 # Changed from 16 to 32 + bits_per_sample: 16 diff --git a/firmware/esphome/packages/media-player-addon-dac-enable.yaml b/firmware/esphome/packages/media-player-addon-dac-enable.yaml new file mode 100644 index 0000000..d47d67b --- /dev/null +++ b/firmware/esphome/packages/media-player-addon-dac-enable.yaml @@ -0,0 +1,34 @@ +# ============================================================ +# Media Player - AC enable addon +# ============================================================ +# Configures media player with DAC enable control +# Requires substitutions: +# - friendly_name +# - dac_enable_pin +# Dependencies: media-player.yaml (speaker stack) +# dac-switch.yaml (DAC enable switch) +# ============================================================ + +media_player: + - id: !extend external_media_player + + on_play: + - if: + condition: + switch.is_off: enable_dac + then: + switch.turn_on: enable_dac + + on_idle: + - if: + condition: + switch.is_on: enable_dac + then: + switch.turn_off: enable_dac + + on_announcement: + - if: + condition: + switch.is_off: enable_dac + then: + switch.turn_on: enable_dac diff --git a/firmware/esphome/packages/media-player-addon-voice-assist.yaml b/firmware/esphome/packages/media-player-addon-voice-assist.yaml new file mode 100644 index 0000000..01923fa --- /dev/null +++ b/firmware/esphome/packages/media-player-addon-voice-assist.yaml @@ -0,0 +1,33 @@ +# ============================================================ +# Voice Assist extensions for the Media Player component +# ============================================================ +# Configures media player with announcement ducking logic +# Requires substitutions: +# - friendly_name +# - task_stack_in_psram +# Dependencies: media-player.yaml +# ============================================================ + +media_player: + - id: !extend external_media_player + + on_state: + if: + condition: + and: + - switch.is_off: timer_ringing + - not: + voice_assistant.is_running: + - not: + media_player.is_announcing: + then: + - mixer_speaker.apply_ducking: + id: media_mixer_input + decibel_reduction: 0 + duration: 1.0s + + 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 diff --git a/firmware/esphome/packages/media-player.yaml b/firmware/esphome/packages/media-player.yaml new file mode 100644 index 0000000..0493307 --- /dev/null +++ b/firmware/esphome/packages/media-player.yaml @@ -0,0 +1,97 @@ +# ============================================================ +# Media Player - Speaker Platform with Announcement Ducking +# ============================================================ +# Configures media player with announcement ducking logic +# Requires substitutions: +# - friendly_name +# - task_stack_in_psram +# Optional substitutions: +# - media_player_led_control: "true" (default) — set to "false" +# when voice-assist.yaml is also included, to avoid conflicts +# with voice-assist LED state management. +# Dependencies: audio.yaml (speaker stack) +# ============================================================ + +substitutions: + media_player_led_control: "true" + +globals: + - id: current_volume + type: float + initial_value: '0.2' + - id: announcement_triggered + type: bool + initial_value: 'false' + +number: + - platform: template + name: ${friendly_name} Announce Volume + id: announce_volume + icon: mdi:volume-source + unit_of_measurement: "%" + min_value: 0 + max_value: 100 + step: 1 + restore_value: true + initial_value: 50 + optimistic: true + +media_player: + - platform: speaker + name: ${friendly_name} Mediaplayer + id: external_media_player + task_stack_in_psram: ${task_stack_in_psram} + volume_increment: 5% + volume_initial: 45% + media_pipeline: + speaker: media_resampling_speaker + num_channels: 2 + announcement_pipeline: + speaker: announcement_resampling_speaker + num_channels: 1 + + on_announcement: + - if: + condition: + - lambda: return (!id(announcement_triggered)); + then: + - lambda: |- + id(announcement_triggered) = true; + id(current_volume) = id(external_media_player).volume; + - if: + condition: + lambda: return ${media_player_led_control}; + then: + - light.turn_on: + id: rgb_front_led + blue: 100% + red: 0% + green: 100% + brightness: 50% + - mixer_speaker.apply_ducking: + id: media_mixer_input + decibel_reduction: 30 + duration: 0.5s + - wait_until: + media_player.is_announcing: external_media_player + - delay: 0.75s + - media_player.volume_set: + id: external_media_player + volume: !lambda "return id(announce_volume).state / 100.0;" + - wait_until: + - not: + media_player.is_announcing: external_media_player + - mixer_speaker.apply_ducking: + id: media_mixer_input + decibel_reduction: 0 + duration: 1.0s + - media_player.volume_set: + id: external_media_player + volume: !lambda "return id(current_volume);" + - if: + condition: + lambda: return ${media_player_led_control}; + then: + - light.turn_off: + id: rgb_front_led + - lambda: id(announcement_triggered) = false; \ No newline at end of file diff --git a/firmware/esphome/packages/microphone-i2s.yaml b/firmware/esphome/packages/microphone-i2s.yaml new file mode 100644 index 0000000..aee1e2f --- /dev/null +++ b/firmware/esphome/packages/microphone-i2s.yaml @@ -0,0 +1,19 @@ +# ============================================================ +# Micprophone - I2S Audio Setup and Mic Configuration +# ============================================================ +# Configures I2S mic input and related settings for Esparagus-Echo +# Requires substitutions: +# - i2s_lrclk_pin +# - i2s_bclk_pin +# - i2s_dout_pin +# - task_stack_in_psram +# ============================================================ + +substitutions: + # I2S microphone bus + i2s_mic_bus_id: i2s_in + +i2s_audio: +- id: ${i2s_mic_bus_id} + i2s_lrclk_pin: ${i2s_mic_lrclk_pin} + i2s_bclk_pin: ${i2s_mic_bclk_pin} \ No newline at end of file diff --git a/firmware/esphome/packages/microphone.yaml b/firmware/esphome/packages/microphone.yaml index 4269071..e5feec3 100644 --- a/firmware/esphome/packages/microphone.yaml +++ b/firmware/esphome/packages/microphone.yaml @@ -9,16 +9,15 @@ # - task_stack_in_psram # ============================================================ -i2s_audio: -- id: i2s_in - i2s_lrclk_pin: ${i2s_mic_lrclk_pin} - i2s_bclk_pin: ${i2s_mic_bclk_pin} +substitutions: + # I2S microphone bus + i2s_mic_bus_id: i2s_in microphone: - platform: i2s_audio adc_type: external id: voice_assistant_mic - i2s_audio_id: i2s_in + i2s_audio_id: ${i2s_mic_bus_id} i2s_din_pin: ${i2s_mic_din_pin} pdm: false channel: right diff --git a/firmware/esphome/packages/sendspin-addon-dac-enable.yaml b/firmware/esphome/packages/sendspin-addon-dac-enable.yaml new file mode 100644 index 0000000..398d081 --- /dev/null +++ b/firmware/esphome/packages/sendspin-addon-dac-enable.yaml @@ -0,0 +1,32 @@ +# ============================================================ +# Sendspin - DAC enable addon +# ============================================================ +# Configures sendspin component to switch on and off DAC on playback events +# Requires substitutions: +# - task_stack_in_psram +# - dac_enable_pin +# ============================================================ + +media_player: + - id: !extend external_media_player + + on_play: + - if: + condition: + switch.is_off: enable_dac + then: + switch.turn_on: enable_dac + + on_announcement: + - if: + condition: + switch.is_off: enable_dac + then: + switch.turn_on: enable_dac + + on_idle: + - if: + condition: + switch.is_on: enable_dac + then: + switch.turn_off: enable_dac diff --git a/firmware/esphome/packages/sendspin-addon-voice-assist.yaml b/firmware/esphome/packages/sendspin-addon-voice-assist.yaml new file mode 100644 index 0000000..d1010ed --- /dev/null +++ b/firmware/esphome/packages/sendspin-addon-voice-assist.yaml @@ -0,0 +1,42 @@ +# ============================================================ +# Voice Assist extensions for the Media Player component +# ============================================================ +# Configures media player with announcement ducking logic +# Requires substitutions: +# - friendly_name +# - task_stack_in_psram +# Dependencies: media-player.yaml +# ============================================================ + +media_player: + - id: !extend external_media_player + media_pipeline: + sources: + - audio_file_source + + on_state: + if: + condition: + and: + - switch.is_off: timer_ringing + - not: + voice_assistant.is_running: + - not: + media_player.is_announcing: external_media_player + then: + - mixer_speaker.apply_ducking: + id: media_mixer_input + decibel_reduction: 0 + duration: 1.0s + + + +media_source: + - platform: audio_file + id: audio_file_source + +audio_file: + - 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 diff --git a/firmware/esphome/packages/sendspin.yaml b/firmware/esphome/packages/sendspin.yaml new file mode 100644 index 0000000..e4f348b --- /dev/null +++ b/firmware/esphome/packages/sendspin.yaml @@ -0,0 +1,143 @@ +# ============================================================ +# Sendspin - Synchronized Multi-Room Audio +# ============================================================ +# Configures sendspin component for synchronized multi-room audio playback +# Requires substitutions: +# - task_stack_in_psram +# - friendly_name +# Requires i2s_audio to be configured in main config +# !! Requires ESPHome 2026.5.0 or later for latest sendspin features and optimizations !! +# !! at the moment this would only build with ESPHome (Dev) but should be available in ESPHome (main) by the mid-May !! +# ============================================================ + +substitutions: + media_source_buffer_size: 200000 + +globals: + - id: current_volume + type: float + initial_value: '0.2' + - id: announcement_triggered + type: bool + initial_value: 'false' + +text_sensor: + - platform: sendspin + type: title + name: Track Title + + - platform: sendspin + type: artist + name: Artist + + - platform: sendspin + type: album + name: Album + +sendspin: + id: sendspin_hub + task_stack_in_psram: ${task_stack_in_psram} + +media_source: + - platform: sendspin + id: sendspin_source + decode_memory: internal + + - platform: audio_http + id: http_media_source + buffer_size: ${media_source_buffer_size} + + - platform: audio_http + id: http_announcement_source + buffer_size: ${media_source_buffer_size} + +media_player: + - platform: sendspin + id: sendspin_group_media_player + name: "${friendly_name} Sendspin Player" + + - platform: speaker_source + id: external_media_player + name: "${friendly_name} Player" + volume_increment: 0.05 + + announcement_pipeline: + format: FLAC # FLAC is the least processor intensive codec + num_channels: 1 # Stereo audio is unnecessary for announcements + sample_rate: 48000 + speaker: announcement_mixer_input + sources: + - http_announcement_source + + media_pipeline: + speaker: media_resampling_speaker + format: FLAC # FLAC is the least processor intensive codec + num_channels: 2 + sample_rate: 48000 + sources: + - sendspin_source + - http_media_source + + on_announcement: + - if: + condition: + - lambda: return (!id(announcement_triggered)); + then: + - lambda: |- + id(announcement_triggered) = true; + id(current_volume) = id(external_media_player).volume; + - light.turn_on: + id: rgb_front_led + blue: 100% + red: 0% + green: 100% + brightness: 50% + - mixer_speaker.apply_ducking: + id: media_mixer_input + decibel_reduction: 40 + duration: 0.0s + - wait_until: + media_player.is_announcing: external_media_player + - delay: 0.75s + - media_player.volume_set: + id: external_media_player + volume: !lambda "return id(announce_volume).state / 100.0;" + - wait_until: + - not: + media_player.is_announcing: external_media_player + - mixer_speaker.apply_ducking: + id: media_mixer_input + decibel_reduction: 0 + duration: 1.0s + - media_player.volume_set: + id: external_media_player + volume: !lambda "return id(current_volume);" + - light.turn_off: + id: rgb_front_led + - lambda: id(announcement_triggered) = false; + + on_state: + if: + condition: + and: + - lambda: return (!id(announcement_triggered)); + - not: + media_player.is_announcing: external_media_player + then: + - mixer_speaker.apply_ducking: + id: media_mixer_input + decibel_reduction: 0 + duration: 1.0s + +number: + - platform: template + name: ${friendly_name} Announce Volume + id: announce_volume + icon: mdi:volume-source + unit_of_measurement: "%" + min_value: 0 + max_value: 100 + step: 1 + restore_value: true + initial_value: 50 + optimistic: true diff --git a/firmware/esphome/packages/snapclient-addon-dac-enable.yaml b/firmware/esphome/packages/snapclient-addon-dac-enable.yaml new file mode 100644 index 0000000..d16576d --- /dev/null +++ b/firmware/esphome/packages/snapclient-addon-dac-enable.yaml @@ -0,0 +1,25 @@ +# ============================================================ +# Media Player - DAC enable addon +# ============================================================ +# Extends snapclient media player with DAC enable control +# Dependencies: snapclient-new.yaml (speaker stack) +# dac-switch.yaml (DAC enable switch) +# or tas58xx-dac.yaml (DAC enable switch included in DAC component) +# ============================================================ + +media_player: + - id: !extend snapclient_media_player + + on_play: + - if: + condition: + switch.is_off: enable_dac + then: + switch.turn_on: enable_dac + + on_idle: + - if: + condition: + switch.is_on: enable_dac + then: + switch.turn_off: enable_dac \ No newline at end of file diff --git a/firmware/esphome/packages/voice-assist-addon-dac-enable.yaml b/firmware/esphome/packages/voice-assist-addon-dac-enable.yaml index cc5b277..013afda 100644 --- a/firmware/esphome/packages/voice-assist-addon-dac-enable.yaml +++ b/firmware/esphome/packages/voice-assist-addon-dac-enable.yaml @@ -1,15 +1,53 @@ - # ============================================================ -# Voice Assist - External DAC addon +# Voice Assist - DAC enable addon # ============================================================ -# Extends voice-assist.yaml to attach an external DAC component -# to the existing I2S speaker. -# Dependencies: voice-assist.yaml +# Full replacement for sendspin-addon-dac-enable.yaml when voice +# assist is active. Do NOT include both — this file handles all +# DAC enable/disable for external_media_player with voice-assist- +# aware guards on on_idle. +# +# Requires: +# - voice-assist.yaml (voice_assistant_id, external_media_player, +# timer_ringing switch) +# - dac-switch.yaml (enable_dac switch) +# +# on_idle guard rationale: +# - timer_ringing: repeat-one mode has a 500 ms playlist gap; +# the player briefly goes IDLE between repeats — keep DAC on. +# - voice_assistant.is_running: player may hit IDLE momentarily +# between the stop-before-chime and the chime announcement +# itself, or between TTS chunks in a conversation. # ============================================================ -esphome: - on_boot: - priority: 220.0 - then: - - switch.turn_on: - id: enable_dac \ No newline at end of file +media_player: + - id: !extend external_media_player + + # Regular media playback (Sendspin / HTTP). + on_play: + - if: + condition: + switch.is_off: enable_dac + then: + switch.turn_on: enable_dac + + # Wake chime, TTS, timer sounds — all announcements. + on_announcement: + - if: + condition: + switch.is_off: enable_dac + then: + switch.turn_on: enable_dac + + # Disable DAC only when truly idle — not mid-conversation or + # mid-timer. This replaces the unguarded on_idle in + # sendspin-addon-dac-enable.yaml. + on_idle: + - if: + condition: + and: + - switch.is_on: enable_dac + - switch.is_off: timer_ringing + - not: + voice_assistant.is_running: + then: + switch.turn_off: enable_dac diff --git a/firmware/esphome/esphome-2025.10.1/echo-solo-b-voice-assist.yaml b/firmware/esphome/packages/voice-assist-sendspin.yaml similarity index 61% rename from firmware/esphome/esphome-2025.10.1/echo-solo-b-voice-assist.yaml rename to firmware/esphome/packages/voice-assist-sendspin.yaml index bb68adc..55e80b5 100644 --- a/firmware/esphome/esphome-2025.10.1/echo-solo-b-voice-assist.yaml +++ b/firmware/esphome/packages/voice-assist-sendspin.yaml @@ -1,10 +1,7 @@ -# ESPHome 2025.10.2 - substitutions: - name: esparagus-solo - friendly_name: "Esparagus Solo" + # Voice Assistant Configuration wake_word: okay_nabu - speaker_volume: 60% # Increased from 50% + speaker_volume: 60% voice_assist_idle_phase_id: '1' voice_assist_listening_phase_id: '2' voice_assist_thinking_phase_id: '3' @@ -14,22 +11,12 @@ substitutions: voice_assist_muted_phase_id: '12' esphome: - name: ${name} - friendly_name: ${friendly_name} - name_add_mac_suffix: false - min_version: 2025.10.2 - platformio_options: - build_flags: -DBOARD_HAS_PSRAM - board_build.flash_mode: dio - board_upload.maximum_size: 16777216 on_boot: priority: 200.0 then: - media_player.volume_set: - id: voice_assistant_media_player + id: external_media_player volume: ${speaker_volume} - - switch.turn_on: - id: speaker_switch - delay: 30s - if: condition: @@ -37,45 +24,6 @@ esphome: then: - lambda: id(init_in_progress) = false; -esp32: - board: esp32-s3-devkitc-1 - variant: ESP32S3 - flash_size: 16MB - framework: - type: esp-idf - sdkconfig_options: - CONFIG_ESP32_S3_BOX_BOARD: "y" - # OPTIMIZED WiFi buffers for streaming - CONFIG_ESP32_WIFI_STATIC_RX_BUFFER_NUM: "16" - CONFIG_ESP32_WIFI_DYNAMIC_RX_BUFFER_NUM: "32" - CONFIG_TCPIP_RECVMBOX_SIZE: "512" - CONFIG_TCP_SND_BUF_DEFAULT: "65535" - CONFIG_TCP_WND_DEFAULT: "512000" - CONFIG_TCP_RECVMBOX_SIZE: "512" - # Audio priority - CONFIG_FREERTOS_UNICORE: "n" - CONFIG_ESP_TASK_WDT_TIMEOUT_S: "10" - -logger: - level: INFO # Changed from DEBUG to reduce overhead - -api: - -ota: - platform: esphome - -wifi: - ssid: !secret wifi_ssid - password: !secret wifi_password - ap: - ssid: "${name} Hotspot" - -captive_portal: - -psram: - mode: octal - speed: 80MHz - globals: - id: init_in_progress type: bool @@ -86,118 +34,8 @@ globals: restore_value: no initial_value: ${voice_assist_not_ready_phase_id} -# Correct I2S configuration -i2s_audio: - - id: i2s_in - i2s_lrclk_pin: GPIO7 - i2s_bclk_pin: GPIO16 - - id: i2s_out - i2s_lrclk_pin: GPIO8 - i2s_bclk_pin: GPIO18 - -microphone: - - platform: i2s_audio - adc_type: external - id: voice_assistant_mic - i2s_audio_id: i2s_in - i2s_din_pin: GPIO15 - pdm: false - channel: right - sample_rate: 16000 - bits_per_sample: 32bit - use_apll: true # Better clock accuracy - i2s_mode: primary -# OPTIMIZED speaker configuration -speaker: - - platform: i2s_audio - id: i2s_audio_speaker - i2s_audio_id: i2s_out - i2s_dout_pin: GPIO17 - dac_type: external - channel: mono - i2s_mode: primary - buffer_duration: 500ms # INCREASED from 100ms (reduces glitches) - timeout: never - sample_rate: 48000 - bits_per_sample: 16bit # Keep at 32bit for quality - - - - platform: mixer - id: mixing_speaker - output_speaker: i2s_audio_speaker - num_channels: 1 - source_speakers: - - id: announcement_mixing_input - timeout: never - - id: media_mixing_input - timeout: never - - # OPTIMIZED resamplers - 32bit for better quality - - platform: resampler - id: announcement_resampling_speaker - output_speaker: announcement_mixing_input - sample_rate: 48000 - bits_per_sample: 16 # Changed from 16 to 32 - - platform: resampler - id: media_resampling_speaker - output_speaker: media_mixing_input - sample_rate: 48000 - bits_per_sample: 16 # Changed from 16 to 32 - - - -media_player: - - platform: speaker - id: voice_assistant_media_player - name: "${friendly_name} Media Player" - internal: false - announcement_pipeline: - speaker: announcement_resampling_speaker - format: WAV # Changed from FLAC (less CPU) - num_channels: 1 - sample_rate: 48000 - media_pipeline: - speaker: media_resampling_speaker - format: WAV # Changed from FLAC (less CPU) - num_channels: 1 - sample_rate: 48000 - buffer_size: 2000000 # INCREASED buffer (2MB vs default 1MB) - task_stack_in_psram: true # Use PSRAM for audio tasks - on_announcement: - - mixer_speaker.apply_ducking: - id: media_mixing_input - decibel_reduction: 20 - duration: 0.0s - on_state: - if: - condition: - and: - - switch.is_off: timer_ringing - - not: - voice_assistant.is_running: - - not: - media_player.is_announcing: - then: - - mixer_speaker.apply_ducking: - id: media_mixing_input - decibel_reduction: 0 - duration: 1.0s - 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 - light: - - platform: esp32_rmt_led_strip - id: rgb_front_led - name: "${friendly_name} Front LED" - rgb_order: GRB - pin: GPIO42 - num_leds: 2 - rmt_symbols: 96 - chipset: ws2812 - default_transition_length: 0s + - id: !extend rgb_front_led effects: - addressable_color_wipe: name: "Connecting" @@ -254,6 +92,7 @@ micro_wake_word: switch.is_off: ducking_switch then: media_player.stop: + id: external_media_player - if: condition: voice_assistant.is_running: @@ -263,8 +102,10 @@ micro_wake_word: - if: condition: media_player.is_announcing: + id: external_media_player then: - media_player.stop: + id: external_media_player announcement: true else: - if: @@ -281,7 +122,7 @@ micro_wake_word: voice_assistant: id: va microphone: voice_assistant_mic - media_player: voice_assistant_media_player + media_player: external_media_player use_wake_word: false auto_gain: 31dBFS on_client_connected: @@ -324,7 +165,7 @@ voice_assistant: effect: "Slow Pulse" on_start: - mixer_speaker.apply_ducking: - id: media_mixing_input + id: media_mixer_input decibel_reduction: 20 duration: 0.0s on_end: @@ -333,7 +174,7 @@ voice_assistant: not: voice_assistant.is_running: - mixer_speaker.apply_ducking: - id: media_mixing_input + id: media_mixer_input decibel_reduction: 0 duration: 1.0s - light.turn_off: @@ -385,6 +226,7 @@ script: effect: none else: - light.turn_off: rgb_front_led + - id: play_sound parameters: priority: bool @@ -392,25 +234,35 @@ script: then: - lambda: |- if (priority) { - id(voice_assistant_media_player) + id(external_media_player) ->make_call() .set_command(media_player::MediaPlayerCommand::MEDIA_PLAYER_COMMAND_STOP) .set_announcement(true) .perform(); } - if ( (id(voice_assistant_media_player).state != media_player::MediaPlayerState::MEDIA_PLAYER_STATE_ANNOUNCING ) || priority) { - id(voice_assistant_media_player) - ->play_file(sound_file, true, false); + if ( (id(external_media_player).state != media_player::MediaPlayerState::MEDIA_PLAYER_STATE_ANNOUNCING ) || priority) { + for (const auto &named_file : audio_file::get_named_audio_files()) { + if (named_file.file == sound_file) { + id(external_media_player) + ->make_call() + .set_media_url(std::string("audio-file://") + named_file.file_id) + .set_announcement(true) + .perform(); + break; + } + } } + - id: enable_repeat_one then: - lambda: |- - id(voice_assistant_media_player) + id(external_media_player) ->make_call() .set_command(media_player::MediaPlayerCommand::MEDIA_PLAYER_COMMAND_REPEAT_ONE) .set_announcement(true) .perform(); - id(voice_assistant_media_player)->set_playlist_delay_ms(speaker::AudioPipelineType::ANNOUNCEMENT, 500); + id(external_media_player)->set_playlist_delay_ms(speaker_source::ANNOUNCEMENT_PIPELINE, 500); + - id: ring_timer then: - script.execute: enable_repeat_one @@ -418,22 +270,18 @@ script: id: play_sound priority: true sound_file: !lambda return id(timer_finished_sound); + - id: disable_repeat then: - lambda: |- - id(voice_assistant_media_player) + id(external_media_player) ->make_call() .set_command(media_player::MediaPlayerCommand::MEDIA_PLAYER_COMMAND_REPEAT_OFF) .set_announcement(true) .perform(); - id(voice_assistant_media_player)->set_playlist_delay_ms(speaker::AudioPipelineType::ANNOUNCEMENT, 0); + id(external_media_player)->set_playlist_delay_ms(speaker_source::ANNOUNCEMENT_PIPELINE, 0); switch: - - platform: gpio - id: speaker_switch - icon: "mdi:speaker" - pin: - number: GPIO9 - platform: template name: "${friendly_name} Enable Voice Assistant" id: use_wake_word @@ -467,6 +315,7 @@ switch: - micro_wake_word.stop - lambda: id(voice_assistant_phase) = ${voice_assist_muted_phase_id}; - script.execute: reset_led + - platform: template name: "${friendly_name} Enable Ducking" id: ducking_switch @@ -480,7 +329,7 @@ switch: restore_mode: ALWAYS_OFF on_turn_on: - mixer_speaker.apply_ducking: - id: media_mixing_input + id: media_mixer_input decibel_reduction: 20 duration: 0.0s - script.execute: ring_timer @@ -497,38 +346,21 @@ switch: - script.execute: disable_repeat - if: condition: - media_player.is_announcing: + media_player.is_announcing: + id: external_media_player then: - media_player.stop: - announcement: true + media_player.stop: + id: external_media_player + announcement: true - mixer_speaker.apply_ducking: - id: media_mixing_input + id: media_mixer_input decibel_reduction: 0 duration: 1.0s - script.execute: reset_led + - platform: template id: wake_sound name: "${friendly_name} Wake Sound" optimistic: true restore_mode: RESTORE_DEFAULT_ON icon: "mdi:bullhorn" - -button: - - platform: restart - id: restart_btn - name: "${friendly_name} REBOOT" - -sensor: - - platform: wifi_signal - name: "${friendly_name} WiFi Signal" - update_interval: 60s - - platform: uptime - name: "${friendly_name} Uptime" - update_interval: 60s - -text_sensor: - - platform: wifi_info - ip_address: - name: "${friendly_name} IP Address" - - platform: version - name: "${friendly_name} ESPHome Version" \ No newline at end of file diff --git a/firmware/esphome/packages/voice-assist.yaml b/firmware/esphome/packages/voice-assist.yaml index 240492c..ba8462e 100644 --- a/firmware/esphome/packages/voice-assist.yaml +++ b/firmware/esphome/packages/voice-assist.yaml @@ -1,6 +1,7 @@ substitutions: # Voice Assistant Configuration wake_word: okay_nabu + media_player_led_control: "false" # disable media-player LED control; voice-assist manages LEDs speaker_volume: 60% voice_assist_idle_phase_id: '1' voice_assist_listening_phase_id: '2' @@ -15,7 +16,7 @@ esphome: priority: 200.0 then: - media_player.volume_set: - id: voice_assistant_media_player + id: external_media_player volume: ${speaker_volume} - delay: 30s - if: @@ -92,6 +93,7 @@ micro_wake_word: switch.is_off: ducking_switch then: media_player.stop: + id: external_media_player - if: condition: voice_assistant.is_running: @@ -101,8 +103,10 @@ micro_wake_word: - if: condition: media_player.is_announcing: + id: external_media_player then: - media_player.stop: + id: external_media_player announcement: true else: - if: @@ -117,9 +121,9 @@ micro_wake_word: - voice_assistant.start: voice_assistant: - id: va + id: voice_assistant_id microphone: voice_assistant_mic - media_player: voice_assistant_media_player + media_player: external_media_player use_wake_word: false auto_gain: 31dBFS on_client_connected: @@ -162,7 +166,7 @@ voice_assistant: effect: "Slow Pulse" on_start: - mixer_speaker.apply_ducking: - id: media_mixing_input + id: media_mixer_input decibel_reduction: 20 duration: 0.0s on_end: @@ -171,7 +175,7 @@ voice_assistant: not: voice_assistant.is_running: - mixer_speaker.apply_ducking: - id: media_mixing_input + id: media_mixer_input decibel_reduction: 0 duration: 1.0s - light.turn_off: @@ -223,6 +227,7 @@ script: effect: none else: - light.turn_off: rgb_front_led + - id: play_sound parameters: priority: bool @@ -230,25 +235,27 @@ script: then: - lambda: |- if (priority) { - id(voice_assistant_media_player) + id(external_media_player) ->make_call() .set_command(media_player::MediaPlayerCommand::MEDIA_PLAYER_COMMAND_STOP) .set_announcement(true) .perform(); } - if ( (id(voice_assistant_media_player).state != media_player::MediaPlayerState::MEDIA_PLAYER_STATE_ANNOUNCING ) || priority) { - id(voice_assistant_media_player) + if ( (id(external_media_player).state != media_player::MediaPlayerState::MEDIA_PLAYER_STATE_ANNOUNCING ) || priority) { + id(external_media_player) ->play_file(sound_file, true, false); } + - id: enable_repeat_one then: - lambda: |- - id(voice_assistant_media_player) + id(external_media_player) ->make_call() .set_command(media_player::MediaPlayerCommand::MEDIA_PLAYER_COMMAND_REPEAT_ONE) .set_announcement(true) .perform(); - id(voice_assistant_media_player)->set_playlist_delay_ms(speaker::AudioPipelineType::ANNOUNCEMENT, 500); + id(external_media_player)->set_playlist_delay_ms(speaker::AudioPipelineType::ANNOUNCEMENT, 500); + - id: ring_timer then: - script.execute: enable_repeat_one @@ -256,18 +263,18 @@ script: id: play_sound priority: true sound_file: !lambda return id(timer_finished_sound); + - id: disable_repeat then: - lambda: |- - id(voice_assistant_media_player) + id(external_media_player) ->make_call() .set_command(media_player::MediaPlayerCommand::MEDIA_PLAYER_COMMAND_REPEAT_OFF) .set_announcement(true) .perform(); - id(voice_assistant_media_player)->set_playlist_delay_ms(speaker::AudioPipelineType::ANNOUNCEMENT, 0); + id(external_media_player)->set_playlist_delay_ms(speaker::AudioPipelineType::ANNOUNCEMENT, 0); switch: - - platform: template name: "${friendly_name} Enable Voice Assistant" id: use_wake_word @@ -301,6 +308,7 @@ switch: - micro_wake_word.stop - lambda: id(voice_assistant_phase) = ${voice_assist_muted_phase_id}; - script.execute: reset_led + - platform: template name: "${friendly_name} Enable Ducking" id: ducking_switch @@ -314,7 +322,7 @@ switch: restore_mode: ALWAYS_OFF on_turn_on: - mixer_speaker.apply_ducking: - id: media_mixing_input + id: media_mixer_input decibel_reduction: 20 duration: 0.0s - script.execute: ring_timer @@ -331,15 +339,18 @@ switch: - script.execute: disable_repeat - if: condition: - media_player.is_announcing: + media_player.is_announcing: + id: external_media_player then: - media_player.stop: - announcement: true + media_player.stop: + id: external_media_player + announcement: true - mixer_speaker.apply_ducking: - id: media_mixing_input + id: media_mixer_input decibel_reduction: 0 duration: 1.0s - script.execute: reset_led + - platform: template id: wake_sound name: "${friendly_name} Wake Sound"