Date the changelog entry for the mic gain fixes and move the pinned release tag in the example config and README from v1.0.0 to v1.0.1. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
8.9 KiB
Changelog
[1.0.1] - 2026-09-20
Patch release fixing the mic gain control. Tagged v1.0.1; the example config
now pins this tag.
Fixed
- Mic gain slider snapped back to its old value in Home Assistant. The
Mic gain (ES7210)number had aset_actionbut was notoptimistic, so the new value was never published back. The ES7210 was reprogrammed correctly (the device log showed the new gain), but HA kept displaying the previous value and reset the slider on the next visit. The number now publishes its state after every change. - A saved mic gain was not applied to the ES7210 after a reboot. A template
number restores its value from flash in
setup()and publishes it, but does not runset_action, so HA showed the saved gain while the codec started at the default fromaudio_adc.on_boot(priority -100) now pushes the restored value to the ES7210.set_mic_gain()is safe to call there because the driver stores the value even before its own setup completes.
[1.0.0] - 2026-07-18
First stable release. The full voice assistant is confirmed on hardware, the
regressions found during bring-up testing are fixed, and the project ships with
complete documentation and a wiki. Tagged v1.0.0; the example config pins this
tag so a build is reproducible instead of tracking a moving main.
Fixed
- Boot loop into safe mode after selecting a ring effect. The three
effect selects restore their saved option during
setup()(at HARDWARE priority), which firedon_valueand ran the LED state machine before the light/RMT and voice_assistant were initialised, painting an effect on an uninitialised strip and crashing the boot. Theon_valueis now gated oninit_in_progress(cleared byon_bootat priority -100), so it only runs once setup is complete; a live effect change from HA still repaints the ring.
Changed
- Boot chime is now a bundled sound, not the wake-word beep.
base/sounds/startup.mp3(16 kHz mono, ~11 KB) plays on connect to HA and is swappable through theboot_sound_filesubstitution (any URL or local MP3/FLAC/WAV; the media player decodes all three).
Removed
- The Flicker ring effect (weak visually); 15 effects remain.
[0.2.0] - 2026-07-18
First on-hardware bring-up. The full voice assistant works: on-device wake word, STT/intent/TTS, clean playback, no boot hiss - all on stock ESPHome components.
Changed
- Audio reworked to two I2S buses with the mic as master; the patched es8311
and
force_masterare gone. The board shares BCLK/LRCLK between the DAC and ADC, and ESPHome can't run one bus full-duplex (the speaker hits "Parent bus is busy"). Two buses over the shared pins, with the always-capturing mic mastering the clock and the speaker slaving to it, gives simultaneous capture and playback on stock components. The mic is pinned to 16-bit so its master frame matches the DAC (a 32-bit frame played back as noise). - The amplifier is gated on playback (
ALWAYS_OFFat boot, turned on by the media_playeron_state) to remove the idle hiss the always-on amp produced before the first playback. - API encryption dropped (LAN-only device);
secrets.yamlis now just Wi-Fi. Re-enable it via the commented block inbase/core.yamlif you want it. - Timers are visible in HA (the "Next timer" / "Next timer name" sensors are
no longer
disabled_by_default). - Boot chime: a short "ready" sound plays once the device connects to HA,
played after the mic (I2S master) is clocking so the slave speaker can output
it. New
boot_soundswitch toggles it. The sound ships with the repo (base/sounds/startup.mp3, 16 kHz mono) and is swappable via theboot_sound_filesubstitution (any URL or local MP3/FLAC/WAV).
Added
- Per-phase ring animation, pickable from HA. New "Listening effect",
"Thinking effect" and "Replying effect" selects choose the animation for those
voice-assistant phases (the phase colour stays fixed). 15 effects to choose
from: solid, three pulses, Breathe, Wipe, Scan, Spinner, Comet, Twinkle,
Random Twinkle, Fireworks, Fire, and two rainbows. Breathe / Spinner /
Comet / Fire are custom
addressable_lambdaeffects.
Fixed
- The Pulse LED effects showed a solid colour instead of pulsing. Their
update_interval(16 ms) was shorter than the transition (300-1000 ms);update_intervalis how often the pulse flips its brightness target, so it flipped faster than the brightness could move. Now ~2x the transition, with 30-100% min/max brightness. - Compile-time issues found during bring-up: a quoted
mclk_multiplesubstitution (string vs the int acv.one_ofwants), atemplate selectmissing itsoptions:,select.state->current_option(), the dead microWakeWord model URLs (404), and a/in a switch name.
Removed
- The
stopwake word. This board has no usable hardware AEC, so the mic hears the device's own TTS far louder than the user; "stop" is detected only weakly and too late to be useful. - The daily-alarm entities (Alarm time / Alarm on / Alarm action), the
device-clock text sensor, and the diagnostic mic-disable switch are now
internal:(hidden from HA). The logic stays; the clutter is gone. - The dead "Mute and unmute sound" switch and its unused sound files.
components/es8311/and theexternal_components:block - no longer needed.
[0.1.0] - 2026-07-17
First cut. A working single-file config for the Waveshare ESP32-S3-AUDIO-Board,
restructured into a core package + a thin user config, with the patched
es8311 component brought in-tree so nothing depends on an upstream repo that
has gone quiet.
Added
base/core.yaml: the always-on core with ES8311 speaker, ES7210 dual mic, on-device wake word (alexa+okay_nabu), the HA Assist pipeline, music/announcement mixing with ducking, the 7x WS2812 status ring state machine, the three onboard buttons, voice timers and an alarm clock.waveshare-va.yaml: thin user config. Pulls the core from GitHub at compile time, so it is the only file you keep.components/es8311/: vendored fork of ESPHome'ses8311addingforce_master+mclk_multiple. Origin, credits and the licensing situation are in the README.docs/HARDWARE.md: board pinout and the I2C device map.skill/waveshare-esp32-s3-audio/: Claude Code skill with pinout + gotchas.scripts/validate.py: offline YAML check (syntax, substitutions, duplicate ids) so a typo does not cost a dashboard round trip.scripts/esplog.py: stream device logs over the native API.
Fixed
Bugs carried over from the config this started as:
- The mic was stopped on every boot. The
diag_disable_miccheck was inverted: with the switch in its default OFF positionon_bootranmicro_wake_word.stop+microphone.stop_capture. It only ever recovered becauseon_client_connectedrestarted the wake word, so the switch also did not actually work, in either direction. - "Microphone Mute" did not mute. It dropped the ES7210 gain to
0.0f, but 0 dB is unity gain, not silence. The mic kept hearing the room and only the wake-word handler ignored it. It now uses ESPHome's ownmicrophone.mute, which hands every consumer a zero-filled buffer, so the wake word hears actual silence with no I2S restart. - A test sound fired on every LED repaint during boot. A leftover
id(play_sound).execute(1, id(wake_word_triggered_sound)); //TESTsat in theinit_in_progressbranch ofcontrol_leds. - Wake word sensitivity did nothing. The select only set cutoffs on
okay_nabu, while the primary wake word isalexa. It now sets both. - Mic gain slider promised 42 dB. The ES7210 caps at 37.5 dB and the driver
silently clamps, so the top third of the slider was a lie. Range is now
0 to 37.5 dB in 1.5 dB steps (the chip's real granularity), and the boot value
in
audio_adcmatches the number entity instead of contradicting it (24 dB vs a 32 dB global). - The API encryption key was hard-coded in the config. It now comes from
!secret api_encryption_key. - The
time:block was half-commented-out, leavingid: rtcdangling underplatform: homeassistant. Cleaned up. PCF85063 support is not in this build. - Two
on_boot: priority: -100blocks ran in an order nobody had chosen. Merged into one.
Changed
- All pins, audio format and the HA-facing values are
substitutions:with documented defaults, instead of literals scattered through the file. ${mic_channel_${which_mic}}nested-substitution trickery replaced with two plainly named knobs:mic_channel(the I2S slot) andmic_va_channel(the index handed to Assist). These are genuinely different things and the old names implied they were the same one.- Dropped dead substitutions (
i2s_bits_per_sample,i2s_mode_speaker,rtc_int,mic_channel_2) and the now-unusedmic_gain_savedglobal. - Timezone is a
posix_timezonesubstitution rather than a hard-codedUTC0.