ESPHome 2026.8.0-dev
Loading...
Searching...
No Matches
voice_assistant.h
Go to the documentation of this file.
1#pragma once
2
4
5#ifdef USE_VOICE_ASSISTANT
6
10
16#ifdef USE_MEDIA_PLAYER
18#endif
19#ifdef USE_MICRO_WAKE_WORD
21#endif
22#ifdef USE_SPEAKER
24#endif
26
27#include <span>
28#include <vector>
29
31
32// Version 1: Initial version
33// Version 2: Adds raw speaker support
34static const uint32_t LEGACY_INITIAL_VERSION = 1;
35static const uint32_t LEGACY_SPEAKER_SUPPORT = 2;
36
46
62
67
68struct Timer {
69 std::string id;
70 std::string name;
74
76 static constexpr size_t TO_STR_BUFFER_SIZE = 128;
78 const char *to_str(std::span<char, TO_STR_BUFFER_SIZE> buffer) const {
79 snprintf(buffer.data(), buffer.size(),
80 "Timer(id=%s, name=%s, total_seconds=%" PRIu32 ", seconds_left=%" PRIu32 ", is_active=%s)",
81 this->id.c_str(), this->name.c_str(), this->total_seconds, this->seconds_left, YESNO(this->is_active));
82 return buffer.data();
83 }
84};
85
86struct WakeWord {
87 std::string id;
88 std::string wake_word;
89 std::vector<std::string> trained_languages;
90};
91
93 std::vector<WakeWord> available_wake_words;
94 std::vector<std::string> active_wake_words;
96};
97
98#ifdef USE_MEDIA_PLAYER
100 IDLE,
101 URL_SENT,
102 PLAYING,
103 FINISHED,
104};
105#endif
106
107class VoiceAssistant final : public Component {
108 public:
110
111 void loop() override;
112 void setup() override;
113 float get_setup_priority() const override;
114 void start_streaming();
115 void start_streaming(struct sockaddr_storage *addr, uint16_t port);
116 void failed_to_start();
117
118 void set_microphone_source(microphone::MicrophoneSource *mic_source) { this->mic_source_ = mic_source; }
119 void set_microphone_source2(microphone::MicrophoneSource *mic_source2) { this->mic_source2_ = mic_source2; }
120#ifdef USE_MICRO_WAKE_WORD
122#endif
123#ifdef USE_SPEAKER
125 this->speaker_ = speaker;
126 this->local_output_ = true;
127 }
128#endif
129#ifdef USE_MEDIA_PLAYER
131 this->media_player_ = media_player;
132 this->local_output_ = true;
133 }
134#endif
135
137#ifdef USE_SPEAKER
138 if (this->speaker_ != nullptr) {
139 return LEGACY_SPEAKER_SUPPORT;
140 }
141#endif
142 return LEGACY_INITIAL_VERSION;
143 }
144
146 uint32_t flags = 0;
149 if (this->mic_source2_ != nullptr) {
151 }
152#ifdef USE_SPEAKER
153 if (this->speaker_ != nullptr) {
155 }
156#endif
157
158 if (this->has_timers_) {
160 }
161
162#ifdef USE_MEDIA_PLAYER
163 if (this->media_player_ != nullptr) {
166 }
167#endif
168
169 return flags;
170 }
171
172 void request_start(bool continuous, bool silence_detection);
173 void request_stop();
174
176 void on_audio(const api::VoiceAssistantAudio &msg);
179 void on_set_configuration(const std::vector<std::string> &active_wake_words);
181
182 bool is_running() const { return this->state_ != State::IDLE; }
183 void set_continuous(bool continuous) { this->continuous_ = continuous; }
184 bool is_continuous() const { return this->continuous_; }
185
186 void set_use_wake_word(bool use_wake_word) { this->use_wake_word_ = use_wake_word; }
187
188 void set_noise_suppression_level(uint8_t noise_suppression_level) {
189 this->noise_suppression_level_ = noise_suppression_level;
190 }
191 void set_auto_gain(uint8_t auto_gain) { this->auto_gain_ = auto_gain; }
192 void set_volume_multiplier(float volume_multiplier) { this->volume_multiplier_ = volume_multiplier; }
193 void set_conversation_timeout(uint32_t conversation_timeout) { this->conversation_timeout_ = conversation_timeout; }
195
204#ifdef USE_SPEAKER
207#endif
214
217
218 void client_subscription(api::APIConnection *client, bool subscribe);
220
221 void set_wake_word(const std::string &wake_word) { this->wake_word_ = wake_word; }
222
228 void set_has_timers(bool has_timers) { this->has_timers_ = has_timers; }
229 const std::vector<Timer> &get_timers() const { return this->timers_; }
230
231 protected:
232 bool allocate_buffers_();
233 void clear_buffers_();
234 void deallocate_buffers_();
235
236 void set_state_(State state);
237 void set_state_(State state, State desired_state);
238 void signal_stop_();
240
241 // Drains the exposed microphone audio and sends it to Home Assistant over the API in one loop() pass.
242 void stream_api_audio_();
243 // Handles a pass where at least one configured channel has no audio exposed, timing out a channel that
244 // stalls. See audio_channel_stall_start_.
245 void handle_channel_stall_(size_t available, size_t available2);
246
247 std::unique_ptr<socket::Socket> socket_ = nullptr;
249
257#ifdef USE_SPEAKER
260#endif
268
271
273
274 std::vector<Timer> timers_;
275 void timer_tick_();
281 bool has_timers_{false};
283
286#ifdef USE_SPEAKER
287 void write_speaker_();
289 uint8_t *speaker_buffer_{nullptr};
294 bool stream_ended_{false};
295#endif
296#ifdef USE_MEDIA_PLAYER
298 std::string tts_response_url_;
300
302#endif
303
304 bool local_output_{false};
305
306 std::string conversation_id_;
307
308 std::string wake_word_;
309
310 // Zero-copy sources that read directly from each microphone channel's ring buffer internal storage.
311 // Each source owns its ring buffer; the matching ``ring_buffer_``/``ring_buffer2_`` weak_ptr is used by
312 // the microphone callback (a different thread) to write into it.
313 std::unique_ptr<audio::RingBufferAudioSource> audio_source_;
314 std::unique_ptr<audio::RingBufferAudioSource> audio_source2_;
315 std::weak_ptr<ring_buffer::RingBuffer> ring_buffer_;
316 std::weak_ptr<ring_buffer::RingBuffer> ring_buffer2_;
317
318 // When streaming multiple channels, the send loop holds an exposed chunk on one channel until the other
319 // channel also has audio so the channels are always sent together (an empty payload looks like
320 // end-of-stream to Home Assistant). Home Assistant has no stream timeout, so a channel that stops
321 // producing entirely would hang streaming forever. This records when such an imbalance began so a
322 // prolonged one can be detected and stopped; 0 means no imbalance is currently being timed.
324
327 uint8_t auto_gain_;
330
331 bool continuous_{false};
333
335
338
341 bool start_udp_socket_();
342
344
345#ifdef USE_MICRO_WAKE_WORD
347#endif
348};
349
350template<typename... Ts> class StartAction final : public Action<Ts...>, public Parented<VoiceAssistant> {
351 TEMPLATABLE_VALUE(std::string, wake_word);
352
353 public:
354 void play(const Ts &...x) override {
355 this->parent_->set_wake_word(this->wake_word_.value(x...));
356 this->parent_->request_start(false, this->silence_detection_);
357 }
358
359 void set_silence_detection(bool silence_detection) { this->silence_detection_ = silence_detection; }
360
361 protected:
363};
364
365template<typename... Ts> class StartContinuousAction final : public Action<Ts...>, public Parented<VoiceAssistant> {
366 public:
367 void play(const Ts &...x) override { this->parent_->request_start(true, true); }
368};
369
370template<typename... Ts> class StopAction final : public Action<Ts...>, public Parented<VoiceAssistant> {
371 public:
372 void play(const Ts &...x) override { this->parent_->request_stop(); }
373};
374
375template<typename... Ts> class IsRunningCondition final : public Condition<Ts...>, public Parented<VoiceAssistant> {
376 public:
377 bool check(const Ts &...x) override { return this->parent_->is_running() || this->parent_->is_continuous(); }
378};
379
380template<typename... Ts> class ConnectedCondition final : public Condition<Ts...>, public Parented<VoiceAssistant> {
381 public:
382 bool check(const Ts &...x) override { return this->parent_->get_api_connection() != nullptr; }
383};
384
385extern VoiceAssistant *global_voice_assistant; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
386
387} // namespace esphome::voice_assistant
388
389#endif // USE_VOICE_ASSISTANT
Base class for all automation conditions.
Definition automation.h:438
Helper class to easily give an object a parent of type T.
Definition helpers.h:1881
void play(const Ts &...x) override
void set_silence_detection(bool silence_detection)
void play(const Ts &...x) override
std::unique_ptr< socket::Socket > socket_
void set_conversation_timeout(uint32_t conversation_timeout)
microphone::MicrophoneSource * mic_source2_
Trigger< std::string > * get_stt_end_trigger()
Trigger< std::string > * get_intent_progress_trigger()
void on_timer_event(const api::VoiceAssistantTimerEventResponse &msg)
void on_audio(const api::VoiceAssistantAudio &msg)
Trigger< std::string > * get_tts_end_trigger()
std::unique_ptr< audio::RingBufferAudioSource > audio_source_
const std::vector< Timer > & get_timers() const
std::weak_ptr< ring_buffer::RingBuffer > ring_buffer2_
std::weak_ptr< ring_buffer::RingBuffer > ring_buffer_
media_player::MediaPlayer * media_player_
void set_media_player(media_player::MediaPlayer *media_player)
void client_subscription(api::APIConnection *client, bool subscribe)
MediaPlayerResponseState media_player_response_state_
void on_event(const api::VoiceAssistantEventResponse &msg)
Trigger< std::string, std::string > error_trigger_
Trigger< const std::vector< Timer > & > timer_tick_trigger_
Trigger< std::string > intent_progress_trigger_
void set_microphone_source2(microphone::MicrophoneSource *mic_source2)
void on_announce(const api::VoiceAssistantAnnounceRequest &msg)
void request_start(bool continuous, bool silence_detection)
void set_speaker(speaker::Speaker *speaker)
Trigger< std::string > * get_tts_start_trigger()
api::APIConnection * get_api_connection() const
void set_microphone_source(microphone::MicrophoneSource *mic_source)
void handle_channel_stall_(size_t available, size_t available2)
std::unique_ptr< audio::RingBufferAudioSource > audio_source2_
void set_wake_word(const std::string &wake_word)
void set_micro_wake_word(micro_wake_word::MicroWakeWord *mww)
void set_volume_multiplier(float volume_multiplier)
microphone::MicrophoneSource * mic_source_
micro_wake_word::MicroWakeWord * micro_wake_word_
Trigger< std::string, std::string > * get_error_trigger()
void set_noise_suppression_level(uint8_t noise_suppression_level)
void on_set_configuration(const std::vector< std::string > &active_wake_words)
Trigger< const std::vector< Timer > & > * get_timer_tick_trigger()
uint16_t flags
bool state
Definition fan.h:2
VoiceAssistant * global_voice_assistant
static void uint32_t
std::vector< WakeWord > available_wake_words
std::vector< std::string > active_wake_words
static constexpr size_t TO_STR_BUFFER_SIZE
Buffer size for to_str() - sufficient for typical timer names.
const char * to_str(std::span< char, TO_STR_BUFFER_SIZE > buffer) const
Format to buffer, returns pointer to buffer (may truncate long names)
std::vector< std::string > trained_languages
uint16_t x
Definition tt21100.cpp:5