From 3575f52cdf3cb2dac04634de0933ba1d1d0947a5 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 12 Mar 2025 09:18:21 -1000 Subject: [PATCH 01/10] Bump mdns library to 1.8.0 (#8378) Co-authored-by: Keith Burzinski --- esphome/components/mdns/__init__.py | 2 +- esphome/idf_component.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/esphome/components/mdns/__init__.py b/esphome/components/mdns/__init__.py index 1bc290b582..9a198280dd 100644 --- a/esphome/components/mdns/__init__.py +++ b/esphome/components/mdns/__init__.py @@ -91,7 +91,7 @@ async def to_code(config): add_idf_component( name="mdns", repo="https://github.com/espressif/esp-protocols.git", - ref="mdns-v1.5.1", + ref="mdns-v1.8.0", path="components/mdns", ) diff --git a/esphome/idf_component.yml b/esphome/idf_component.yml index bd5bcda2fe..32ba414ba3 100644 --- a/esphome/idf_component.yml +++ b/esphome/idf_component.yml @@ -7,7 +7,7 @@ dependencies: version: v2.0.9 mdns: git: https://github.com/espressif/esp-protocols.git - version: mdns-v1.5.1 + version: mdns-v1.8.0 path: components/mdns rules: - if: "idf_version >=5.0" From a2b123a29a79816df11ef6145aecf60d1e923b8d Mon Sep 17 00:00:00 2001 From: Kevin Ahrendt Date: Wed, 12 Mar 2025 14:18:31 -0500 Subject: [PATCH 02/10] [audio, mixer] Memory and CPU performance improvements (#8387) --- esphome/components/audio/__init__.py | 2 +- esphome/components/audio/audio_decoder.cpp | 54 +++++++++++++++---- esphome/components/audio/audio_reader.cpp | 4 +- esphome/components/audio/audio_resampler.cpp | 6 ++- .../audio/audio_transfer_buffer.cpp | 28 ++++++---- .../components/audio/audio_transfer_buffer.h | 9 +++- .../mixer/speaker/mixer_speaker.cpp | 3 +- platformio.ini | 4 +- 8 files changed, 80 insertions(+), 30 deletions(-) diff --git a/esphome/components/audio/__init__.py b/esphome/components/audio/__init__.py index 31d3c39ffa..c5ef781060 100644 --- a/esphome/components/audio/__init__.py +++ b/esphome/components/audio/__init__.py @@ -118,4 +118,4 @@ def final_validate_audio_schema( async def to_code(config): - cg.add_library("esphome/esp-audio-libs", "1.1.1") + cg.add_library("esphome/esp-audio-libs", "1.1.2") diff --git a/esphome/components/audio/audio_decoder.cpp b/esphome/components/audio/audio_decoder.cpp index ab358ad805..60489d7d78 100644 --- a/esphome/components/audio/audio_decoder.cpp +++ b/esphome/components/audio/audio_decoder.cpp @@ -66,19 +66,30 @@ esp_err_t AudioDecoder::start(AudioFileType audio_file_type) { case AudioFileType::FLAC: this->flac_decoder_ = make_unique(); this->free_buffer_required_ = - this->output_transfer_buffer_->capacity(); // We'll revise this after reading the header + this->output_transfer_buffer_->capacity(); // Adjusted and reallocated after reading the header break; #endif #ifdef USE_AUDIO_MP3_SUPPORT case AudioFileType::MP3: this->mp3_decoder_ = esp_audio_libs::helix_decoder::MP3InitDecoder(); + + // MP3 always has 1152 samples per chunk this->free_buffer_required_ = 1152 * sizeof(int16_t) * 2; // samples * size per sample * channels + + // Always reallocate the output transfer buffer to the smallest necessary size + this->output_transfer_buffer_->reallocate(this->free_buffer_required_); break; #endif case AudioFileType::WAV: this->wav_decoder_ = make_unique(); this->wav_decoder_->reset(); + + // Processing WAVs doesn't actually require a specific amount of buffer size, as it is already in PCM format. + // Thus, we don't reallocate to a minimum size. this->free_buffer_required_ = 1024; + if (this->output_transfer_buffer_->capacity() < this->free_buffer_required_) { + this->output_transfer_buffer_->reallocate(this->free_buffer_required_); + } break; case AudioFileType::NONE: default: @@ -116,10 +127,18 @@ AudioDecoderState AudioDecoder::decode(bool stop_gracefully) { uint32_t decoding_start = millis(); + bool first_loop_iteration = true; + + size_t bytes_processed = 0; + size_t bytes_available_before_processing = 0; + while (state == FileDecoderState::MORE_TO_PROCESS) { // Transfer decoded out if (!this->pause_output_) { - size_t bytes_written = this->output_transfer_buffer_->transfer_data_to_sink(pdMS_TO_TICKS(READ_WRITE_TIMEOUT_MS)); + // Never shift the data in the output transfer buffer to avoid unnecessary, slow data moves + size_t bytes_written = + this->output_transfer_buffer_->transfer_data_to_sink(pdMS_TO_TICKS(READ_WRITE_TIMEOUT_MS), false); + if (this->audio_stream_info_.has_value()) { this->accumulated_frames_written_ += this->audio_stream_info_.value().bytes_to_frames(bytes_written); this->playback_ms_ += @@ -138,12 +157,24 @@ AudioDecoderState AudioDecoder::decode(bool stop_gracefully) { // Decode more audio - size_t bytes_read = this->input_transfer_buffer_->transfer_data_from_source(pdMS_TO_TICKS(READ_WRITE_TIMEOUT_MS)); + // Only shift data on the first loop iteration to avoid unnecessary, slow moves + size_t bytes_read = this->input_transfer_buffer_->transfer_data_from_source(pdMS_TO_TICKS(READ_WRITE_TIMEOUT_MS), + first_loop_iteration); - if ((this->potentially_failed_count_ > 0) && (bytes_read == 0)) { + if (!first_loop_iteration && (this->input_transfer_buffer_->available() < bytes_processed)) { + // Less data is available than what was processed in last iteration, so don't attempt to decode. + // This attempts to avoid the decoder from consistently trying to decode an incomplete frame. The transfer buffer + // will shift the remaining data to the start and copy more from the source the next time the decode function is + // called + break; + } + + bytes_available_before_processing = this->input_transfer_buffer_->available(); + + if ((this->potentially_failed_count_ > 10) && (bytes_read == 0)) { // Failed to decode in last attempt and there is no new data - if (this->input_transfer_buffer_->free() == 0) { + if ((this->input_transfer_buffer_->free() == 0) && first_loop_iteration) { // The input buffer is full. Since it previously failed on the exact same data, we can never recover state = FileDecoderState::FAILED; } else { @@ -175,6 +206,9 @@ AudioDecoderState AudioDecoder::decode(bool stop_gracefully) { } } + first_loop_iteration = false; + bytes_processed = bytes_available_before_processing - this->input_transfer_buffer_->available(); + if (state == FileDecoderState::POTENTIALLY_FAILED) { ++this->potentially_failed_count_; } else if (state == FileDecoderState::END_OF_FILE) { @@ -207,13 +241,11 @@ FileDecoderState AudioDecoder::decode_flac_() { size_t bytes_consumed = this->flac_decoder_->get_bytes_index(); this->input_transfer_buffer_->decrease_buffer_length(bytes_consumed); + // Reallocate the output transfer buffer to the smallest necessary size this->free_buffer_required_ = flac_decoder_->get_output_buffer_size_bytes(); - if (this->output_transfer_buffer_->capacity() < this->free_buffer_required_) { - // Output buffer is not big enough - if (!this->output_transfer_buffer_->reallocate(this->free_buffer_required_)) { - // Couldn't reallocate output buffer - return FileDecoderState::FAILED; - } + if (!this->output_transfer_buffer_->reallocate(this->free_buffer_required_)) { + // Couldn't reallocate output buffer + return FileDecoderState::FAILED; } this->audio_stream_info_ = diff --git a/esphome/components/audio/audio_reader.cpp b/esphome/components/audio/audio_reader.cpp index 90b73a1f46..b82c6db9ee 100644 --- a/esphome/components/audio/audio_reader.cpp +++ b/esphome/components/audio/audio_reader.cpp @@ -259,14 +259,14 @@ AudioReaderState AudioReader::file_read_() { } AudioReaderState AudioReader::http_read_() { - this->output_transfer_buffer_->transfer_data_to_sink(pdMS_TO_TICKS(READ_WRITE_TIMEOUT_MS)); + this->output_transfer_buffer_->transfer_data_to_sink(pdMS_TO_TICKS(READ_WRITE_TIMEOUT_MS), false); if (esp_http_client_is_complete_data_received(this->client_)) { if (this->output_transfer_buffer_->available() == 0) { this->cleanup_connection_(); return AudioReaderState::FINISHED; } - } else { + } else if (this->output_transfer_buffer_->free() > 0) { size_t bytes_to_read = this->output_transfer_buffer_->free(); int received_len = esp_http_client_read(this->client_, (char *) this->output_transfer_buffer_->get_buffer_end(), bytes_to_read); diff --git a/esphome/components/audio/audio_resampler.cpp b/esphome/components/audio/audio_resampler.cpp index 05e9ff6ca1..a7621225a1 100644 --- a/esphome/components/audio/audio_resampler.cpp +++ b/esphome/components/audio/audio_resampler.cpp @@ -93,8 +93,9 @@ AudioResamplerState AudioResampler::resample(bool stop_gracefully, int32_t *ms_d } if (!this->pause_output_) { - // Move audio data to the sink - this->output_transfer_buffer_->transfer_data_to_sink(pdMS_TO_TICKS(READ_WRITE_TIMEOUT_MS)); + // Move audio data to the sink without shifting the data in the output transfer buffer to avoid unnecessary, slow + // data moves + this->output_transfer_buffer_->transfer_data_to_sink(pdMS_TO_TICKS(READ_WRITE_TIMEOUT_MS), false); } else { // If paused, block to avoid wasting CPU resources delay(READ_WRITE_TIMEOUT_MS); @@ -115,6 +116,7 @@ AudioResamplerState AudioResampler::resample(bool stop_gracefully, int32_t *ms_d if ((this->input_stream_info_.get_sample_rate() != this->output_stream_info_.get_sample_rate()) || (this->input_stream_info_.get_bits_per_sample() != this->output_stream_info_.get_bits_per_sample())) { + // Adjust gain by -3 dB to avoid clipping due to the resampling process esp_audio_libs::resampler::ResamplerResults results = this->resampler_->resample(this->input_transfer_buffer_->get_buffer_start(), this->output_transfer_buffer_->get_buffer_end(), frames_available, frames_free, -3); diff --git a/esphome/components/audio/audio_transfer_buffer.cpp b/esphome/components/audio/audio_transfer_buffer.cpp index 9b6067aac4..1566884c3d 100644 --- a/esphome/components/audio/audio_transfer_buffer.cpp +++ b/esphome/components/audio/audio_transfer_buffer.cpp @@ -33,12 +33,17 @@ size_t AudioTransferBuffer::free() const { if (this->buffer_size_ == 0) { return 0; } - return this->buffer_size_ - (this->buffer_length_ - (this->data_start_ - this->buffer_)); + return this->buffer_size_ - (this->buffer_length_ + (this->data_start_ - this->buffer_)); } void AudioTransferBuffer::decrease_buffer_length(size_t bytes) { this->buffer_length_ -= bytes; - this->data_start_ += bytes; + if (this->buffer_length_ > 0) { + this->data_start_ += bytes; + } else { + // All the data in the buffer has been consumed, reset the start pointer + this->data_start_ = this->buffer_; + } } void AudioTransferBuffer::increase_buffer_length(size_t bytes) { this->buffer_length_ += bytes; } @@ -71,7 +76,7 @@ bool AudioTransferBuffer::has_buffered_data() const { bool AudioTransferBuffer::reallocate(size_t new_buffer_size) { if (this->buffer_length_ > 0) { - // Already has data in the buffer, fail + // Buffer currently has data, so reallocation is impossible return false; } this->deallocate_buffer_(); @@ -106,12 +111,14 @@ void AudioTransferBuffer::deallocate_buffer_() { this->buffer_length_ = 0; } -size_t AudioSourceTransferBuffer::transfer_data_from_source(TickType_t ticks_to_wait) { - // Shift data in buffer to start - if (this->buffer_length_ > 0) { - memmove(this->buffer_, this->data_start_, this->buffer_length_); +size_t AudioSourceTransferBuffer::transfer_data_from_source(TickType_t ticks_to_wait, bool pre_shift) { + if (pre_shift) { + // Shift data in buffer to start + if (this->buffer_length_ > 0) { + memmove(this->buffer_, this->data_start_, this->buffer_length_); + } + this->data_start_ = this->buffer_; } - this->data_start_ = this->buffer_; size_t bytes_to_read = this->free(); size_t bytes_read = 0; @@ -125,7 +132,7 @@ size_t AudioSourceTransferBuffer::transfer_data_from_source(TickType_t ticks_to_ return bytes_read; } -size_t AudioSinkTransferBuffer::transfer_data_to_sink(TickType_t ticks_to_wait) { +size_t AudioSinkTransferBuffer::transfer_data_to_sink(TickType_t ticks_to_wait, bool post_shift) { size_t bytes_written = 0; if (this->available()) { #ifdef USE_SPEAKER @@ -139,11 +146,14 @@ size_t AudioSinkTransferBuffer::transfer_data_to_sink(TickType_t ticks_to_wait) } this->decrease_buffer_length(bytes_written); + } + if (post_shift) { // Shift unwritten data to the start of the buffer memmove(this->buffer_, this->data_start_, this->buffer_length_); this->data_start_ = this->buffer_; } + return bytes_written; } diff --git a/esphome/components/audio/audio_transfer_buffer.h b/esphome/components/audio/audio_transfer_buffer.h index 4e461db56d..edb484e7d2 100644 --- a/esphome/components/audio/audio_transfer_buffer.h +++ b/esphome/components/audio/audio_transfer_buffer.h @@ -60,6 +60,7 @@ class AudioTransferBuffer { protected: /// @brief Allocates the transfer buffer in external memory, if available. + /// @param buffer_size The number of bytes to allocate /// @return True is successful, false otherwise. bool allocate_buffer_(size_t buffer_size); @@ -89,8 +90,10 @@ class AudioSinkTransferBuffer : public AudioTransferBuffer { /// @brief Writes any available data in the transfer buffer to the sink. /// @param ticks_to_wait FreeRTOS ticks to block while waiting for the sink to have enough space + /// @param post_shift If true, all remaining data is moved to the start of the buffer after transferring to the sink. + /// Defaults to true. /// @return Number of bytes written - size_t transfer_data_to_sink(TickType_t ticks_to_wait); + size_t transfer_data_to_sink(TickType_t ticks_to_wait, bool post_shift = true); /// @brief Adds a ring buffer as the transfer buffer's sink. /// @param ring_buffer weak_ptr to the allocated ring buffer @@ -125,8 +128,10 @@ class AudioSourceTransferBuffer : public AudioTransferBuffer { /// @brief Reads any available data from the sink into the transfer buffer. /// @param ticks_to_wait FreeRTOS ticks to block while waiting for the source to have enough data + /// @param pre_shift If true, any unwritten data is moved to the start of the buffer before transferring from the + /// source. Defaults to true. /// @return Number of bytes read - size_t transfer_data_from_source(TickType_t ticks_to_wait); + size_t transfer_data_from_source(TickType_t ticks_to_wait, bool pre_shift = true); /// @brief Adds a ring buffer as the transfer buffer's source. /// @param ring_buffer weak_ptr to the allocated ring buffer diff --git a/esphome/components/mixer/speaker/mixer_speaker.cpp b/esphome/components/mixer/speaker/mixer_speaker.cpp index 60cff95eb2..d9231154a3 100644 --- a/esphome/components/mixer/speaker/mixer_speaker.cpp +++ b/esphome/components/mixer/speaker/mixer_speaker.cpp @@ -490,7 +490,8 @@ void MixerSpeaker::audio_mixer_task(void *params) { break; } - output_transfer_buffer->transfer_data_to_sink(pdMS_TO_TICKS(TASK_DELAY_MS)); + // Never shift the data in the output transfer buffer to avoid unnecessary, slow data moves + output_transfer_buffer->transfer_data_to_sink(pdMS_TO_TICKS(TASK_DELAY_MS), false); const uint32_t output_frames_free = this_mixer->audio_stream_info_.value().bytes_to_frames(output_transfer_buffer->free()); diff --git a/platformio.ini b/platformio.ini index fab7fda659..a2c2a74ac0 100644 --- a/platformio.ini +++ b/platformio.ini @@ -128,7 +128,7 @@ lib_deps = DNSServer ; captive_portal (Arduino built-in) esphome/ESP32-audioI2S@2.0.7 ; i2s_audio droscy/esp_wireguard@0.4.2 ; wireguard - esphome/esp-audio-libs@1.1.1 ; audio + esphome/esp-audio-libs@1.1.2 ; audio build_flags = ${common:arduino.build_flags} @@ -149,7 +149,7 @@ lib_deps = ${common:idf.lib_deps} droscy/esp_wireguard@0.4.2 ; wireguard kahrendt/ESPMicroSpeechFeatures@1.1.0 ; micro_wake_word - esphome/esp-audio-libs@1.1.1 ; audio + esphome/esp-audio-libs@1.1.2 ; audio build_flags = ${common:idf.build_flags} -Wno-nonnull-compare From bf65b73569037e57d2e6ac2b0273aff7cb176867 Mon Sep 17 00:00:00 2001 From: Kevin Ahrendt Date: Wed, 12 Mar 2025 14:34:38 -0500 Subject: [PATCH 03/10] [speaker, resampler, mixer] Make volume and mute getters virtual (#8391) --- esphome/components/mixer/speaker/mixer_speaker.cpp | 4 ++++ esphome/components/mixer/speaker/mixer_speaker.h | 2 ++ esphome/components/resampler/speaker/resampler_speaker.h | 2 ++ esphome/components/speaker/speaker.h | 4 ++-- 4 files changed, 10 insertions(+), 2 deletions(-) diff --git a/esphome/components/mixer/speaker/mixer_speaker.cpp b/esphome/components/mixer/speaker/mixer_speaker.cpp index d9231154a3..121a62392c 100644 --- a/esphome/components/mixer/speaker/mixer_speaker.cpp +++ b/esphome/components/mixer/speaker/mixer_speaker.cpp @@ -177,11 +177,15 @@ void SourceSpeaker::set_mute_state(bool mute_state) { this->parent_->get_output_speaker()->set_mute_state(mute_state); } +bool SourceSpeaker::get_mute_state() { return this->parent_->get_output_speaker()->get_mute_state(); } + void SourceSpeaker::set_volume(float volume) { this->volume_ = volume; this->parent_->get_output_speaker()->set_volume(volume); } +float SourceSpeaker::get_volume() { return this->parent_->get_output_speaker()->get_volume(); } + size_t SourceSpeaker::process_data_from_source(TickType_t ticks_to_wait) { if (!this->transfer_buffer_.use_count()) { return 0; diff --git a/esphome/components/mixer/speaker/mixer_speaker.h b/esphome/components/mixer/speaker/mixer_speaker.h index b2cb3e1e39..0bd6b5f4c8 100644 --- a/esphome/components/mixer/speaker/mixer_speaker.h +++ b/esphome/components/mixer/speaker/mixer_speaker.h @@ -53,9 +53,11 @@ class SourceSpeaker : public speaker::Speaker, public Component { /// @brief Mute state changes are passed to the parent's output speaker void set_mute_state(bool mute_state) override; + bool get_mute_state() override; /// @brief Volume state changes are passed to the parent's output speaker void set_volume(float volume) override; + float get_volume() override; void set_pause_state(bool pause_state) override { this->pause_state_ = pause_state; } bool get_pause_state() const override { return this->pause_state_; } diff --git a/esphome/components/resampler/speaker/resampler_speaker.h b/esphome/components/resampler/speaker/resampler_speaker.h index c44f740fa2..d5e3f2b6d6 100644 --- a/esphome/components/resampler/speaker/resampler_speaker.h +++ b/esphome/components/resampler/speaker/resampler_speaker.h @@ -34,9 +34,11 @@ class ResamplerSpeaker : public Component, public speaker::Speaker { /// @brief Mute state changes are passed to the parent's output speaker void set_mute_state(bool mute_state) override; + bool get_mute_state() override { return this->output_speaker_->get_mute_state(); } /// @brief Volume state changes are passed to the parent's output speaker void set_volume(float volume) override; + float get_volume() override { return this->output_speaker_->get_volume(); } void set_output_speaker(speaker::Speaker *speaker) { this->output_speaker_ = speaker; } void set_task_stack_in_psram(bool task_stack_in_psram) { this->task_stack_in_psram_ = task_stack_in_psram; } diff --git a/esphome/components/speaker/speaker.h b/esphome/components/speaker/speaker.h index 74c4822eca..c4cf912fa6 100644 --- a/esphome/components/speaker/speaker.h +++ b/esphome/components/speaker/speaker.h @@ -76,7 +76,7 @@ class Speaker { } #endif }; - float get_volume() { return this->volume_; } + virtual float get_volume() { return this->volume_; } virtual void set_mute_state(bool mute_state) { this->mute_state_ = mute_state; @@ -90,7 +90,7 @@ class Speaker { } #endif } - bool get_mute_state() { return this->mute_state_; } + virtual bool get_mute_state() { return this->mute_state_; } #ifdef USE_AUDIO_DAC void set_audio_dac(audio_dac::AudioDac *audio_dac) { this->audio_dac_ = audio_dac; } From 3c5a0091ee7638ad956340f8e0c68d1dcc9e591a Mon Sep 17 00:00:00 2001 From: Kevin Ahrendt Date: Wed, 12 Mar 2025 15:04:05 -0500 Subject: [PATCH 04/10] [core] add reallocation support to RAMAllocator (#8390) --- esphome/core/helpers.h | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/esphome/core/helpers.h b/esphome/core/helpers.h index 3f371cd829..7866eaa9bd 100644 --- a/esphome/core/helpers.h +++ b/esphome/core/helpers.h @@ -719,6 +719,25 @@ template class RAMAllocator { return ptr; } + T *reallocate(T *p, size_t n) { return this->reallocate(p, n, sizeof(T)); } + + T *reallocate(T *p, size_t n, size_t manual_size) { + size_t size = n * sizeof(T); + T *ptr = nullptr; +#ifdef USE_ESP32 + if (this->flags_ & Flags::ALLOC_EXTERNAL) { + ptr = static_cast(heap_caps_realloc(p, size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT)); + } + if (ptr == nullptr && this->flags_ & Flags::ALLOC_INTERNAL) { + ptr = static_cast(heap_caps_realloc(p, size, MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT)); + } +#else + // Ignore ALLOC_EXTERNAL/ALLOC_INTERNAL flags if external allocation is not supported + ptr = static_cast(realloc(p, size)); // NOLINT(cppcoreguidelines-owning-memory,cppcoreguidelines-no-malloc) +#endif + return ptr; + } + void deallocate(T *p, size_t n) { free(p); // NOLINT(cppcoreguidelines-owning-memory,cppcoreguidelines-no-malloc) } From dd113f297213e9ddfa824056b9017393f9603796 Mon Sep 17 00:00:00 2001 From: Kevin Ahrendt Date: Wed, 12 Mar 2025 19:35:10 -0500 Subject: [PATCH 05/10] [api] add voice assistant announce to the api (#8395) --- esphome/components/api/api.proto | 2 ++ esphome/components/api/api_pb2.cpp | 24 ++++++++++++++++++++++++ esphome/components/api/api_pb2.h | 3 +++ 3 files changed, 29 insertions(+) diff --git a/esphome/components/api/api.proto b/esphome/components/api/api.proto index 8b7fdf8b11..d59b5e0d3e 100644 --- a/esphome/components/api/api.proto +++ b/esphome/components/api/api.proto @@ -1567,6 +1567,8 @@ message VoiceAssistantAnnounceRequest { string media_id = 1; string text = 2; + string preannounce_media_id = 3; + bool start_conversation = 4; } message VoiceAssistantAnnounceFinished { diff --git a/esphome/components/api/api_pb2.cpp b/esphome/components/api/api_pb2.cpp index 771f029eae..8001a74b6d 100644 --- a/esphome/components/api/api_pb2.cpp +++ b/esphome/components/api/api_pb2.cpp @@ -7094,6 +7094,16 @@ void VoiceAssistantTimerEventResponse::dump_to(std::string &out) const { out.append("}"); } #endif +bool VoiceAssistantAnnounceRequest::decode_varint(uint32_t field_id, ProtoVarInt value) { + switch (field_id) { + case 4: { + this->start_conversation = value.as_bool(); + return true; + } + default: + return false; + } +} bool VoiceAssistantAnnounceRequest::decode_length(uint32_t field_id, ProtoLengthDelimited value) { switch (field_id) { case 1: { @@ -7104,6 +7114,10 @@ bool VoiceAssistantAnnounceRequest::decode_length(uint32_t field_id, ProtoLength this->text = value.as_string(); return true; } + case 3: { + this->preannounce_media_id = value.as_string(); + return true; + } default: return false; } @@ -7111,6 +7125,8 @@ bool VoiceAssistantAnnounceRequest::decode_length(uint32_t field_id, ProtoLength void VoiceAssistantAnnounceRequest::encode(ProtoWriteBuffer buffer) const { buffer.encode_string(1, this->media_id); buffer.encode_string(2, this->text); + buffer.encode_string(3, this->preannounce_media_id); + buffer.encode_bool(4, this->start_conversation); } #ifdef HAS_PROTO_MESSAGE_DUMP void VoiceAssistantAnnounceRequest::dump_to(std::string &out) const { @@ -7123,6 +7139,14 @@ void VoiceAssistantAnnounceRequest::dump_to(std::string &out) const { out.append(" text: "); out.append("'").append(this->text).append("'"); out.append("\n"); + + out.append(" preannounce_media_id: "); + out.append("'").append(this->preannounce_media_id).append("'"); + out.append("\n"); + + out.append(" start_conversation: "); + out.append(YESNO(this->start_conversation)); + out.append("\n"); out.append("}"); } #endif diff --git a/esphome/components/api/api_pb2.h b/esphome/components/api/api_pb2.h index 1f96e2c151..455e3ff6cf 100644 --- a/esphome/components/api/api_pb2.h +++ b/esphome/components/api/api_pb2.h @@ -1832,6 +1832,8 @@ class VoiceAssistantAnnounceRequest : public ProtoMessage { public: std::string media_id{}; std::string text{}; + std::string preannounce_media_id{}; + bool start_conversation{false}; void encode(ProtoWriteBuffer buffer) const override; #ifdef HAS_PROTO_MESSAGE_DUMP void dump_to(std::string &out) const override; @@ -1839,6 +1841,7 @@ class VoiceAssistantAnnounceRequest : public ProtoMessage { protected: bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; + bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; class VoiceAssistantAnnounceFinished : public ProtoMessage { public: From b3a69c6c05061a54b2a4df6e0c650e472ae710fc Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 12 Mar 2025 15:00:31 -1000 Subject: [PATCH 06/10] Bump aioesphomeapi to 29.6.0 (#8396) --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index a7e322ab6b..bb5b5d7d87 100644 --- a/requirements.txt +++ b/requirements.txt @@ -13,7 +13,7 @@ platformio==6.1.16 # When updating platformio, also update Dockerfile esptool==4.8.1 click==8.1.7 esphome-dashboard==20250212.0 -aioesphomeapi==29.5.1 +aioesphomeapi==29.6.0 zeroconf==0.146.1 puremagic==1.27 ruamel.yaml==0.18.6 # dashboard_import From 0a02c1461e63255a88478eaef8157e1489108704 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 12 Mar 2025 15:16:59 -1000 Subject: [PATCH 07/10] Rework pyproject.toml to make it parseable by dependabot (#8397) --- pyproject.toml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 5cdf4a77b5..69b36cd14a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -43,11 +43,13 @@ include-package-data = true [tool.setuptools.dynamic] dependencies = {file = ["requirements.txt"]} -optional-dependencies.dev = { file = ["requirements_dev.txt"] } -optional-dependencies.test = { file = ["requirements_test.txt"] } -optional-dependencies.displays = { file = ["requirements_optional.txt"] } version = {attr = "esphome.const.__version__"} +[tool.setuptools.dynamic.optional-dependencies] +dev = { file = ["requirements_dev.txt"] } +test = { file = ["requirements_test.txt"] } +displays = { file = ["requirements_optional.txt"] } + [tool.setuptools.packages.find] include = ["esphome*"] From c63cf9d1519116855c4af435e8e1df3dbb5d6c15 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 12 Mar 2025 18:21:45 -1000 Subject: [PATCH 08/10] Bump cryptography to 44.0.2 (#8399) --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index bb5b5d7d87..ba4a1d6b2e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ async_timeout==4.0.3; python_version <= "3.10" -cryptography==43.0.0 +cryptography==44.0.2 voluptuous==0.14.2 PyYAML==6.0.2 paho-mqtt==1.6.1 From f36d400058589c7ae4313ff53ec98ba6fa150838 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 12 Mar 2025 23:22:08 -0500 Subject: [PATCH 09/10] Bump tornado from 6.4 to 6.4.2 (#8398) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index ba4a1d6b2e..46746b08cf 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,7 +5,7 @@ PyYAML==6.0.2 paho-mqtt==1.6.1 colorama==0.4.6 icmplib==3.0.4 -tornado==6.4 +tornado==6.4.2 tzlocal==5.2 # from time tzdata>=2021.1 # from time pyserial==3.5 From 4d95ff2ae00d7947fc5f983a8580316edbf49909 Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Wed, 12 Mar 2025 23:23:27 -0500 Subject: [PATCH 10/10] Bump version to 2025.3.0b2 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 55b8112ee8..add7af55a7 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2025.3.0b1" +__version__ = "2025.3.0b2" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" VALID_SUBSTITUTIONS_CHARACTERS = (