先确定这篇要完成什么
本工程包含完整 ESPHome 配置和本地状态策略,连接实体按键、I2S采集、HA Assist 和回答播放。按一下开始一句话;松手用于准备下一次按键,结束说话由 HA 静音检测决定。 不是松手截断录音的对讲机。第一版不启用BLE、唤醒词、连续对话或AEC。
配置、主机和固件各维结果以网站证据为准。没有实板录音、HA流水线运行或扬声器声音记录;空观察表不是成功结果。主机测试不能证明识别率、声压、延迟或物理静音。
参考:[1] S3 DevKitC1 v1.1固定文档:J1/J3、电源与USB[2] ICS43434 DS-000069 v1.2:电气/边沿/24bit/启动[4] MAX98357A/MAX98357B Rev7:供电/采样率/SD/GAIN/BTL
1. 固定版本和器材
固定 ESPHome 2026.9.1、Python 3.12.13、ESP-IDF 5.5.5;HA配置与源码边界以 Core2026.10.2 为准。主板只覆盖 ESP32-S3-DevKitC-1 v1.1 / WROOM-1-N8R8,8MB Quad Flash、8MB Octal PSRAM。核对实物模组后缀,不能用N8、N8R2、C3代替。
功放外供负端与主板、模块GND共地;外供正端只接功放VIN,不接主板3V3/5V。 主板由一根桥接串口USB线供电,麦克风用主板3V3。不要并接两路稳压器正端。
MAX98357A是BTL输出,扬声器两线接OUT+、OUT−,两端都不是地,不得接GND、耳机地或接地示波器夹。8Ω/1W是扬声器额定值,不是已测输出。首次保持功放断电检查,低音量开始;削波、发热、电源下陷时停止。GPIO不能直接带扬声器。
| 器材 | 具体要求 |
|---|---|
| 麦克风 | 已可靠焊接排针的 Adafruit PID6049 / ICS-43434 I2S;不是PDM,SEL接地选择左槽 |
| 功放 | 已可靠焊接排针的 Adafruit PID3006 / MAX98357A单声道,不是MAX98357B或另一款立体声板 |
| 扬声器 | Adafruit PID1313 / C2464-001,8Ω、1W |
| 外供 | 稳压 3.3V、额定至少1A 的低压电源,只给功放供电 |
| 按键/灯 | 常开按钮、10kΩ上拉;Kingbright WP7113ID 与1kΩ串联电阻 |
| SD控制 | GPIO17→1kΩ→功放SD;SD另接10kΩ到GND。再装低压SPST从SD到GND:闭合强制物理静音,正常播放才断开 |
参考:[1] S3 DevKitC1 v1.1固定文档:J1/J3、电源与USB[2] ICS43434 DS-000069 v1.2:电气/边沿/24bit/启动[3] PID6049固定PCB:3V、SEL与47k数据下拉[4] MAX98357A/MAX98357B Rev7:供电/采样率/SD/GAIN/BTL[5] PID3006固定单声道PCB:VIN/SD1M/BTL端子[6] PID1313扬声器2017-02-14厂家规格:8Ω额定1W[7] PID6049产品页:具体型号与停产边界
2. 断电接线和音频格式
先闭合SD→GND的强制静音开关,再断USB与外供,接完核对极性、共地和短路再上电。开关在1kΩ电阻的SD侧,GPIO17仍经1kΩ限流,不把GPIO直接短路。
输入、输出使用两套独立I2S时钟,不能把4与16、5与15短接。ICS-43434的24bit二补码数据放在32bit槽中,本例16kHz属于芯片低功耗采样范围;SEL=LOW取左槽。ESPHome microphone source再转换为Assist的16bit、单声道、16kHz,gain_factor=1,不是32bit有效精度或声学增益实测。
HA回答按16kHz、16bit、单声道PCM WAV流送到speaker,功放不需要MCLK。强制静音开关断开、GPIO17高时SD设计约3V;实测须高于B2上限1.5V才保守选择左声道,1.4V只是典型阈值。闭合开关将SD拉低;实测低于B0最小0.08V。不能称为SD悬空时的默认左右平均。需在空表实测SD低/高、电源和时钟,目前均未测。GPIO35/36/37涉及本模组Octal PSRAM,不用;日志走UART0桥接USB的43/44,原生USB19/20不接音频。
芯片收到时钟后存在启动有效数据时间,不把最初约20ms当作已验证语音。按键后等串口 LISTENING,再稍停约150ms开口;起音是否丢失仍需实板记录。correct_dc_offset是软件直流校正,不是AEC,不能替代电源和机械布置。
| 主板/电源 | 模块端 |
|---|---|
| 主板3V3 / GND | PID6049 3V / GND |
| GND | PID6049 SEL,固定左槽 |
| GPIO4 / GPIO5 / GPIO6 | PID6049 WS / BCLK / DOUT |
| 外供3.3V / GND | PID3006 VIN / GND;该GND再连主板GND |
| GPIO16 / GPIO15 / GPIO7 | PID3006 LRC / BCLK / DIN |
| 功放VIN | GAIN,选择6dB;悬空不是6dB |
| GPIO17→1kΩ | SD;SD另接10kΩ到GND,并以低压SPST接GND作强制静音 |
| OUT+ / OUT− | 扬声器两线,均不接地 |
| GPIO10 | 按钮一端,另一端GND;GPIO10另接10kΩ到主板3V3 |
| GPIO18→1kΩ→LED阳极 | LED阴极到GND |
参考:[1] S3 DevKitC1 v1.1固定文档:J1/J3、电源与USB[2] ICS43434 DS-000069 v1.2:电气/边沿/24bit/启动[3] PID6049固定PCB:3V、SEL与47k数据下拉[4] MAX98357A/MAX98357B Rev7:供电/采样率/SD/GAIN/BTL[5] PID3006固定单声道PCB:VIN/SD1M/BTL端子[6] PID1313扬声器2017-02-14厂家规格:8Ω额定1W[10] ESPHome2026.9.1 microphone source:位宽/增益转换[11] ESPHome2026.9.1 I2S microphone:slot与驱动[12] ESPHome2026.9.1 I2S speaker:输出与排空
3. 创建本机密钥、校验和刷写
把 secrets.example.yaml 复制为 secrets.yaml,填自己的2.4GHz Wi-Fi、恢复热点密码、Native API密钥。生成32字节随机密钥:
不要上传secrets.yaml,不用构建夹具的占位SSID/密钥。API和本例OTA采用该设备自己的加密密钥,恢复热点密码至少8字符。建立Python3.12环境,安装锁定依赖,复用既有5.5.5 SDK:
替换自己系统的串口名,首次保持功放外供关闭。无法下载时按住BOOT、点按RESET、松开BOOT再试;这只恢复下载入口,不证明音频正常。当前输入必须对应同一次生成CPP、依赖锁和镜像,不能拿另一篇镜像替代。
python3 -c 'import base64,secrets; print(base64.b64encode(secrets.token_bytes(32)).decode())'
python3.12 -m venv .venv
. .venv/bin/activate
python -m pip install -r requirements.lock
python -m esphome config esp32-homeassistant-voice-terminal.yaml
python -m esphome compile esp32-homeassistant-voice-terminal.yaml
python -m esphome upload esp32-homeassistant-voice-terminal.yaml --device /dev/cu.YOUR_BRIDGE_PORT
python -m esphome logs esp32-homeassistant-voice-terminal.yaml --device /dev/cu.YOUR_BRIDGE_PORT参考:[8] ESPHome2026.9.1 VoiceAssistant:状态、事件与停止[9] ESPHome2026.9.1 API_AUDIO与SPEAKER能力
4. 在HA配置一条清楚的数据路径
按以下步骤执行,并保存实际记录。
- 在设置→设备与服务添加ESPHome集成,用设备IP/发现条目及本机密钥连接;核对 Assist Local State 和取消按钮属于这台设备。在线不等于Assist客户端已经订阅,本地还会等待 on_client_connected。
- 在设置→语音助手创建或选择管线,明确语言、会话代理、STT、TTS。先在HA自己的Assist中测试,再为本终端的卫星/语音助手配置选择该管线;入口可能在设备配置或卫星实体设置。
- 本地路线可用已安装的Whisper、HA会话代理和Piper。这些运行在HA或对应服务主机,ESP32不运行Whisper/Piper。选Home Assistant Cloud或其他外部STT/代理/TTS时,音频或文本会交给所选服务;“局域网设备”不代表所有处理都在本地。
- 本例没有直接调用 homeassistant.action/service,无须因此开启设备直接执行HA服务的额外权限。Assist可控制哪些实体,另在HA暴露实体/会话代理中限制;先只暴露实验灯。
- 锁定ESPHome声明API_AUDIO,锁定HA据此选择Native API/TCP音频分支,这是源码分支核查,实机协商/抓包未做。旧版仍有UDP分支,不能把Native API密钥当成给所有UDP自动加密。不要把设备API映射到公网。
参考:[8] ESPHome2026.9.1 VoiceAssistant:状态、事件与停止[9] ESPHome2026.9.1 API_AUDIO与SPEAKER能力[13] HA Core2026.10.2 Assist satellite:Native API/TCP与16k WAV[14] HA Core2026.10.2 WAV流解析:格式检查边界
5. 本地状态、结束与取消
启动先稳定500ms,按钮释放稳定50ms后,新的按下稳定50ms才开始。上电按住、断线一直按住、抖动不会自动启动,忙时额外按键忽略。松手不停止当前句子。
外部LED设计为:就绪熄灭、会话常亮、离线或故障250ms亮/250ms灭。文本状态为READY、RELEASE_BUTTON、PIPELINE_ACTIVE、PLAYING、HA_OFFLINE或FAULT_RELEASE_AND_RETRY。光学表现待实板核对;HA断线时从UART看本地状态,不能期待离线设备向HA继续更新。
HA的on_end可能早于扬声器排空,不在该事件直接宣告完成。已观察到运行/管线事件,且Assist和speaker都空闲,才结束本地会话。只有本地会话仍active时才响应TTS stream start开启功放,stream end、错误、断线、取消或60s本地上限请求关闭。
60s用64位 esp_timer_get_time() 单调时间;它是主循环观察上限,不是被阻塞任务或已提交声音的硬实时撤回保证。取消会关功放输出、停止speaker并请求Assist停止。固定ESPHome在AWAITING_RESPONSE的request_stop只向HA发停止信号,不强制马上IDLE;故障后释放重试还要求组件确实空闲。修好服务仍卡住时先取消,必要时物理RESET,不能反复按键强行重入。
参考:[8] ESPHome2026.9.1 VoiceAssistant:状态、事件与停止[9] ESPHome2026.9.1 API_AUDIO与SPEAKER能力[12] ESPHome2026.9.1 I2S speaker:输出与排空[13] HA Core2026.10.2 Assist satellite:Native API/TCP与16k WAV
6. 故障与恢复
停机、复位和故障时先闭合SD→GND强制静音开关,确认SD低,再关闭功放外供,最后拔USB。MAX98357A的SD绝对上限是VDD+0.3V;不能在功放已断电时仍让GPIO17经电阻持续送高电平。BCLK/LRC/DIN的绝对额定不同,不能类推到SD。软件取消可先请求静音,实物开关用于不依赖HA/任务响应的台架关闭;物理电压/动作仍待测。
本次实际固件首次因ESPHome自动生成的自定义GlobalsComponent出现在用户头文件之前而编译失败。当前改为assist_gate.h中的函数局部静态singleton,全部生产调用使用a06::gate();不改SDK或生成器。旧YAML/头/CPP和失败日志留档,当前singleton版本已实际完整编译,当前头/CPP/镜像SHA另核。
完整恢复顺序:SD开关闭合→功放关→核接线→USB上电确认SD低→开功放外供测电压→核HA管线/低音量→断开SD强制静音→新按键。不要上传、播放现实音频作为自动测试。
| 现象 | 检查与恢复 |
|---|---|
| 闪灯/HA_OFFLINE | 从UART核Wi-Fi/IP、同网段、API密钥和卫星订阅;恢复连接后释放再按,不先改位宽 |
| LISTENING后无文字 | 看HA所选STT/语言;断电核具体麦克风、SEL、4/5/6和3V3,再测试 |
| 有STT没有TTS | 看HA管线调试的会话代理/TTS阶段、服务和WAV格式,不先判功放坏 |
| PLAYING但无声 | 保持低音量,核外供3.3V、共地、SD、GAIN、7/15/16和BTL两线;断电改线,空表记录电压/时钟 |
| 网络/管线报错 | 看错误码与本地灯,先闭合SD强制静音再关外供;修服务→取消→空闲→释放再按,仍卡则RESET |
| 啸叫/削波/发热/重启 | 先闭合SD强制静音再关外供,分离麦克风和扬声器,查压降、GAIN、音量;本例没有AEC |
| OTA不可达 | 查IP/设备专有密钥,必要时从桥接串口重刷,不用未知来源镜像 |
参考:[1] S3 DevKitC1 v1.1固定文档:J1/J3、电源与USB[4] MAX98357A/MAX98357B Rev7:供电/采样率/SD/GAIN/BTL[8] ESPHome2026.9.1 VoiceAssistant:状态、事件与停止[13] HA Core2026.10.2 Assist satellite:Native API/TCP与16k WAV
7. 同源测试与空观察表
测试直接包含生产assist_gate.h,覆盖启动按住、抖动、排空、断线/错误、60s与完整32位周期、松手、重连和指示灯。它不初始化I2S、连接HA或播放声音。hardware-observations.csv只含表头;逐次记录配置/镜像SHA、网络、按键、STT/TTS、SD/电源电压、实际灯和声音、恢复方式。未执行项保持空白,不把编译成功填成语音成功。
clang++ -std=c++17 -Wall -Wextra -Werror -fsanitize=address,undefined tests/host.cpp -o /tmp/a06-gate
/tmp/a06-gate参考:[8] ESPHome2026.9.1 VoiceAssistant:状态、事件与停止[12] ESPHome2026.9.1 I2S speaker:输出与排空
8. 来源与许可
入口配置、状态策略采用本包MIT;ESPHome、IDF和依赖保留各自许可。HA Core只用于固定源码核查,没有运行在ESP32镜像中。器件依据ICS-43434 Rev1.2、MAX98357A/B Rev7、具体Adafruit模块电路及PID1313额定资料;版本锁/网站证据列出固定来源、核查和当前构建SHA。资料核查不能替代本组合实板测量。
参考:[2] ICS43434 DS-000069 v1.2:电气/边沿/24bit/启动[3] PID6049固定PCB:3V、SEL与47k数据下拉[4] MAX98357A/MAX98357B Rev7:供电/采样率/SD/GAIN/BTL[5] PID3006固定单声道PCB:VIN/SD1M/BTL端子[6] PID1313扬声器2017-02-14厂家规格:8Ω额定1W[8] ESPHome2026.9.1 VoiceAssistant:状态、事件与停止[13] HA Core2026.10.2 Assist satellite:Native API/TCP与16k WAV
本次实际完成的检查与仍空白的物理记录
软件结果来自实际命令、当前源码与原始产物。失败和恢复均保留,四个验证维度分别展示;下表没有把主机夹具替代实体实验。
| 检查项 | 实际结果 | 边界 |
|---|---|---|
| 配置 / 固件 | 实际 config Exit0;singleton当前源完整编译Exit0 | 程序916903B,bin917024B;原Globals include-order失败保留 |
| 同源 / 文件 | 9组当前生产策略 ASan/UBSan通过;生成CPP/header与镜像SHA核对 | 不初始化I2S、HA或扬声器 |
| 实板 / HA / 声音 | 未执行,观察CSV只有表头 | 不填识别率、延迟、供电电压或声音成功 |
参考:[8] ESPHome2026.9.1 VoiceAssistant:状态、事件与停止[9] ESPHome2026.9.1 API_AUDIO与SPEAKER能力
完整生产配置
以下逐字对应下载包当前文件;完整依赖、构建说明、许可和空物理记录一起提供。
# ---- esp32-homeassistant-voice-terminal.yaml ----
substitutions:
node_name: hixinwu-a06
esphome:
name: ${node_name}
friendly_name: Hixinwu Assist Button
min_version: 2026.9.1
includes:
- assist_gate.h
on_boot:
priority: -100
then:
- lambda: id(reply_speaker).set_volume(0.15f);
esp32:
board: esp32-s3-devkitc-1
variant: ESP32S3
flash_size: 8MB
flash_mode: qio
flash_frequency: 80MHz
toolchain: esp-idf
framework:
type: esp-idf
version: 5.5.5
psram:
mode: octal
speed: 80MHz
logger:
hardware_uart: UART0
baud_rate: 115200
level: DEBUG
api:
encryption:
key: !secret api_key
reboot_timeout: 0s
ota:
- platform: esphome
encryption:
key: !secret api_key
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
reboot_timeout: 0s
ap:
ssid: "${node_name} Recovery"
password: !secret fallback_password
ap_timeout: 90s
captive_portal:
globals:
- id: ha_voice_ready
type: bool
restore_value: false
initial_value: 'false'
# Separate clock buses use the two S3 I2S controllers. No shared-clock duplex/AEC.
i2s_audio:
- id: mic_bus
i2s_lrclk_pin: GPIO4
i2s_bclk_pin: GPIO5
- id: speaker_bus
i2s_lrclk_pin: GPIO16
i2s_bclk_pin: GPIO15
microphone:
- platform: i2s_audio
id: talk_mic
adc_type: external
i2s_audio_id: mic_bus
i2s_din_pin: GPIO6
pdm: false
sample_rate: 16000
bits_per_sample: 32bit
channel: left
correct_dc_offset: true
speaker:
- platform: i2s_audio
id: reply_speaker
dac_type: external
i2s_audio_id: speaker_bus
i2s_dout_pin: GPIO7
sample_rate: 16000
bits_per_sample: 16bit
channel: mono
buffer_duration: 250ms
timeout: 300ms
output:
- platform: gpio
id: amp_sd
pin: GPIO17
- platform: gpio
id: session_led
pin: GPIO18
binary_sensor:
- platform: gpio
id: talk_button
internal: true
pin:
number: GPIO10
mode: INPUT
inverted: true
text_sensor:
- platform: template
id: assist_status
name: Assist Local State
update_interval: 1s
lambda: return {a06::gate().status(id(ha_voice_ready))};
button:
- platform: template
name: Cancel Assist Session
on_press:
- lambda: |-
a06::gate().fail();
id(amp_sd).turn_off();
id(reply_speaker).stop();
- voice_assistant.stop:
id: assist
voice_assistant:
id: assist
microphone:
microphone: talk_mic
bits_per_sample: 16
channels: 0
gain_factor: 1
speaker: reply_speaker
use_wake_word: false
noise_suppression_level: 0
auto_gain: 0dBFS
volume_multiplier: 1.0
on_client_connected:
- lambda: id(ha_voice_ready) = true;
on_client_disconnected:
- lambda: |-
id(ha_voice_ready) = false;
a06::gate().disconnected();
id(amp_sd).turn_off();
id(reply_speaker).stop();
- voice_assistant.stop:
id: assist
on_start:
- lambda: a06::gate().pipeline_event();
on_listening:
- logger.log: "A06 LISTENING: say one utterance; HA determines end of speech"
on_stt_end:
- logger.log: "A06 STT completed (text deliberately not printed)"
on_tts_stream_start:
- lambda: |-
a06::gate().tts_start();
if (a06::gate().amplifier_enabled()) id(amp_sd).turn_on();
on_tts_stream_end:
- lambda: |-
a06::gate().tts_end();
id(amp_sd).turn_off();
on_end:
- lambda: a06::gate().pipeline_event();
on_error:
- lambda: |-
ESP_LOGW("a06", "Pipeline error code=%s; check HA pipeline log", code.c_str());
a06::gate().fail();
id(amp_sd).turn_off();
id(reply_speaker).stop();
- voice_assistant.stop:
id: assist
interval:
- interval: 20ms
then:
- lambda: |-
const uint64_t now_ms = static_cast<uint64_t>(esp_timer_get_time()) / 1000ULL;
auto action = a06::gate().tick(now_ms, id(talk_button).state,
id(ha_voice_ready), id(assist).is_running(), id(reply_speaker).is_running());
if (action == a06::Action::START) id(assist).request_start(false, true);
if (action == a06::Action::STOP) {
id(amp_sd).turn_off();
id(reply_speaker).stop();
id(assist).request_stop();
}
if (!a06::gate().amplifier_enabled()) id(amp_sd).turn_off();
if (a06::gate().indicator_on(now_ms, id(ha_voice_ready))) id(session_led).turn_on();
else id(session_led).turn_off();参考:[8] ESPHome2026.9.1 VoiceAssistant:状态、事件与停止[9] ESPHome2026.9.1 API_AUDIO与SPEAKER能力[12] ESPHome2026.9.1 I2S speaker:输出与排空
完整本地会话策略
以下逐字对应下载包当前文件;完整依赖、构建说明、许可和空物理记录一起提供。
// ---- assist_gate.h ----
// SPDX-License-Identifier: MIT
#pragma once
#include <cstdint>
namespace a06 {
enum class Action { NONE, START, STOP };
// Pure policy, called only from ESPHome's main loop; now is esp_timer_get_time()/1000.
class Gate {
public:
Action tick(uint64_t now, bool pressed, bool connected, bool running, bool speaker_busy) {
if (!initialized_) { initialized_=true; raw_=stable_=pressed; edge_at_=now; boot_at_=now; }
if (pressed!=raw_) { raw_=pressed; edge_at_=now; }
bool press_edge=false;
if (raw_!=stable_ && now-edge_at_>=50) {
stable_=raw_; press_edge=stable_;
if (!stable_) released_=true;
}
if (!raw_ && !stable_ && now-edge_at_>=50 && now-boot_at_>=500) released_=true;
if (!connected) {
released_=false; playing_=false;
if (active_) { active_=false; fault_=true; return Action::STOP; }
return Action::NONE;
}
if (active_) {
if (running) saw_running_=true;
if (now-start_at_>=60000) {
active_=false; playing_=false; fault_=true; released_=false;
return Action::STOP;
}
// RUN_END may precede draining TTS. Only the actual component idle + speaker idle
// pair ends the session, after observing a running state or an explicit event.
if ((saw_running_ || event_seen_) && !running && !speaker_busy) {
active_=false; playing_=false;
}
return Action::NONE;
}
if (press_edge && released_ && !running && !speaker_busy && now-boot_at_>=500) {
released_=false; fault_=false; active_=true; playing_=false;
saw_running_=false; event_seen_=false; start_at_=now;
return Action::START;
}
return Action::NONE;
}
void pipeline_event() { if (active_) event_seen_=true; }
void tts_start() { if (active_) { playing_=true; event_seen_=true; } }
void tts_end() { playing_=false; if (active_) event_seen_=true; }
void fail() { fault_=true; active_=false; playing_=false; released_=false; }
void disconnected() { fail(); }
bool amplifier_enabled() const { return active_ && playing_ && !fault_; }
bool active() const { return active_; }
bool fault() const { return fault_; }
bool indicator_on(uint64_t now, bool connected) const {
if (!connected || fault_) return (now/250ULL)%2ULL != 0;
return active_;
}
const char *status(bool connected) const {
if (!connected) return "HA_OFFLINE";
if (fault_) return "FAULT_RELEASE_AND_RETRY";
if (playing_ && active_) return "PLAYING";
if (active_) return "PIPELINE_ACTIVE";
return released_ ? "READY" : "RELEASE_BUTTON";
}
private:
bool initialized_=false, raw_=false, stable_=false, released_=false;
bool active_=false, playing_=false, fault_=false, saw_running_=false, event_seen_=false;
uint64_t edge_at_=0, boot_at_=0, start_at_=0;
};
// Avoid a custom-type GlobalsComponent before ESPHome inserts user includes.
// One main-loop-owned RAM object; neither restored nor shared across tasks.
inline Gate &gate() { static Gate instance; return instance; }
} // namespace a06参考:[8] ESPHome2026.9.1 VoiceAssistant:状态、事件与停止[9] ESPHome2026.9.1 API_AUDIO与SPEAKER能力[12] ESPHome2026.9.1 I2S speaker:输出与排空
资料来源与版本说明
固定上游、实际SDK/managed和厂家具体电路章节已核;主机/构建记录不替代指定硬件、HA/云端、光学或声音实测。资料核对日期:。资料核对、程序构建与实板测试分别记录,不互相替代。
- S3 DevKitC1 v1.1固定文档:J1/J3、电源与USB
- ICS43434 DS-000069 v1.2:电气/边沿/24bit/启动
- PID6049固定PCB:3V、SEL与47k数据下拉
- MAX98357A/MAX98357B Rev7:供电/采样率/SD/GAIN/BTL
- PID3006固定单声道PCB:VIN/SD1M/BTL端子
- PID1313扬声器2017-02-14厂家规格:8Ω额定1W
- PID6049产品页:具体型号与停产边界
- ESPHome2026.9.1 VoiceAssistant:状态、事件与停止
- ESPHome2026.9.1 API_AUDIO与SPEAKER能力
- ESPHome2026.9.1 microphone source:位宽/增益转换
- ESPHome2026.9.1 I2S microphone:slot与驱动
- ESPHome2026.9.1 I2S speaker:输出与排空
- HA Core2026.10.2 Assist satellite:Native API/TCP与16k WAV
- HA Core2026.10.2 WAV流解析:格式检查边界
- PID3006单声道引脚:GAIN/SD/扬声器与电源