Skip to content

Instantly share code, notes, and snippets.

@RealDeco
Last active February 21, 2026 19:41
Show Gist options
  • Select an option

  • Save RealDeco/44bf80640ad599edb9702f55046e1373 to your computer and use it in GitHub Desktop.

Select an option

Save RealDeco/44bf80640ad599edb9702f55046e1373 to your computer and use it in GitHub Desktop.
Simple sendspin mediaplayer for esp32-s3 zero
substitutions:
startup_sound_file: https://github.com/RealDeco/xiaozhi-esphome/raw/main/sounds/Home_Connected.flac
esphome:
name: esphome-web-5911b0
friendly_name: Sendspin Zero
min_version: 2026.1.0
name_add_mac_suffix: false
on_boot:
priority: -100
then:
- script.execute: led_red
esp32:
variant: esp32s3
framework:
type: esp-idf
logger:
level: debug
api:
on_client_connected:
- lambda: |-
if (!id(boot_sound_played)) {
id(boot_sound_played) = true;
if (id(startup_sound_switch).state) {
id(play_sound)->execute(true, std::string("ready_sound"));
}
}
ota:
- platform: esphome
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
ap:
password: "RZ7D3EzJdPM6"
psram:
mode: quad
speed: 80MHz
http_request:
captive_portal:
external_components:
- source:
# https://github.com/esphome/esphome/pull/12429
type: git
url: https://github.com/esphome/esphome
ref: a6c403deb7603d316dce2820897902e01c3695cc
refresh: 0s
components: [file, http_request, media_source, speaker_source]
- source:
# https://github.com/esphome/esphome/pull/12284
type: git
url: https://github.com/esphome/esphome
ref: 119a5c25a483c58b3d58a35c8cec95495c6491c2
components: [generic_image, sendspin]
# The following sources are merged into the dev branch of ESPHome, but not yet in a release.
# We've pinned them to their specific squash merge commit id for reproducable builds.
# These should be available in ESPHome 2026.3.0
- source:
# Dev commit hash for squash merge from PR#10212
type: git
url: https://github.com/esphome/esphome
ref: 903971de12c1b291ca8cd367ce5cdf9af8b304ae
components: [runtime_image]
- source:
# Dev commit hash for squash merge from PR#12258
type: git
url: https://github.com/esphome/esphome
ref: 264c8faedd0ab956b0437ec2ecc0bacae1708923
components: [media_player]
- source:
# Dev commit hash for squash merge from PR#14013
type: git
url: https://github.com/esphome/esphome
ref: ba7134ee3f870ce00d2990d84b142b734028330f
components: [mdns]
- source:
# Dev commit hash for squash merge from PR#14108
type: git
url: https://github.com/esphome/esphome
ref: d2026b4cd74bbeb9b86941bf1887f3d93d639387
components: [audio]
i2s_audio:
- id: i2s_audio_bus
i2s_lrclk_pin: GPIO4
i2s_bclk_pin: GPIO5
speaker:
- platform: i2s_audio
id: box_speaker
i2s_audio_id: i2s_audio_bus
i2s_dout_pin: GPIO6
dac_type: external
channel: stereo
buffer_duration: 300ms
- platform: mixer
id: mixing_speaker
output_speaker: box_speaker
num_channels: 2
source_speakers:
- id: announcement_mixer_input
- id: media_mixer_input
- platform: resampler
id: media_resampling_input
output_speaker: media_mixer_input
sample_rate: 48000
- platform: resampler
id: announcement_resampling_input
output_speaker: announcement_mixer_input
sample_rate: 48000
sendspin:
id: sendspin_hub
task_stack_in_psram: true
kalman_process_error: 0.01
media_source:
- platform: sendspin
id: sendspin_source
- platform: http_request
id: http_source
buffer_size: 500000
- platform: file
id: file_source
files:
- id: ready_sound
file: ${startup_sound_file}
media_player:
- platform: sendspin
id: sendspin_group_media_player
- platform: speaker_source
name: none
id: external_media_player
announcement_speaker: announcement_resampling_input
media_speaker: media_resampling_input
announcement_pipeline:
format: FLAC
num_channels: 1
sample_rate: 48000
media_pipeline:
format: FLAC
num_channels: 2
sample_rate: 48000
volume_increment: 0.05
volume_min: 0.5
volume_max: 0.9
sources:
- http_source
- sendspin_source
- file_source
on_state:
then:
- lambda: |-
using media_player::MediaPlayerState;
auto state = id(external_media_player).state;
if (
state == MediaPlayerState::MEDIA_PLAYER_STATE_PLAYING ||
state == MediaPlayerState::MEDIA_PLAYER_STATE_ANNOUNCING
) {
id(led_green).execute();
} else {
id(led_red).execute();
}
script:
- id: play_sound
parameters:
priority: bool
sound_file: string
then:
- if:
condition:
lambda: return priority;
then:
- media_player.stop:
id: external_media_player
announcement: true
- lambda: |-
if ( (id(external_media_player).state != media_player::MediaPlayerState::MEDIA_PLAYER_STATE_ANNOUNCING ) || priority) {
id(external_media_player)
->make_call()
.set_media_url("file://" + sound_file)
.set_announcement(true)
.perform();
}
- id: led_red
then:
- light.turn_on:
id: led
red: 100%
green: 0%
blue: 0%
brightness: 100%
- id: led_green
then:
- light.turn_on:
id: led
red: 0%
green: 100%
blue: 0%
brightness: 100%
switch:
- platform: template
id: startup_sound_switch
name: Startup sound
icon: "mdi:card-text-outline"
entity_category: config
optimistic: true
restore_mode: RESTORE_DEFAULT_ON
globals:
- id: boot_sound_played
type: bool
restore_value: no
initial_value: "false"
light:
- platform: esp32_rmt_led_strip
id: led
name: LED light
disabled_by_default: false
entity_category: config
pin: GPIO21
default_transition_length: 0s
chipset: WS2812
num_leds: 1
rgb_order: grb
effects:
- pulse:
name: "Slow Pulse"
transition_length: 250ms
update_interval: 250ms
min_brightness: 50%
max_brightness: 100%
- pulse:
name: "Fast Pulse"
transition_length: 100ms
update_interval: 100ms
min_brightness: 50%
max_brightness: 100%
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment