1
0
mirror of https://github.com/esphome/esphome.git synced 2025-09-01 10:52:19 +01:00

Merge branch 'fix_string_lifetime_fill_and_encode_entity_info' into integration

This commit is contained in:
J. Nick Koston
2025-08-28 13:13:18 -05:00
4 changed files with 68 additions and 30 deletions

View File

@@ -303,11 +303,13 @@ class APIConnection final : public APIServerConnection {
msg.key = entity->get_object_id_hash(); msg.key = entity->get_object_id_hash();
// Try to use static reference first to avoid allocation // Try to use static reference first to avoid allocation
StringRef static_ref = entity->get_object_id_ref_for_api_(); StringRef static_ref = entity->get_object_id_ref_for_api_();
// Store dynamic string outside the if-else to maintain lifetime
std::string object_id;
if (!static_ref.empty()) { if (!static_ref.empty()) {
msg.set_object_id(static_ref); msg.set_object_id(static_ref);
} else { } else {
// Dynamic case - need to allocate // Dynamic case - need to allocate
std::string object_id = entity->get_object_id(); object_id = entity->get_object_id();
msg.set_object_id(StringRef(object_id)); msg.set_object_id(StringRef(object_id));
} }

View File

@@ -138,11 +138,37 @@ void Rtttl::stop() {
this->set_state_(STATE_STOPPING); this->set_state_(STATE_STOPPING);
} }
#endif #endif
this->position_ = this->rtttl_.length();
this->note_duration_ = 0;
}
void Rtttl::finish_() {
ESP_LOGV(TAG, "Rtttl::finish_()");
#ifdef USE_OUTPUT
if (this->output_ != nullptr) {
this->output_->set_level(0.0);
this->set_state_(State::STATE_STOPPED);
}
#endif
#ifdef USE_SPEAKER
if (this->speaker_ != nullptr) {
SpeakerSample sample[2];
sample[0].left = 0;
sample[0].right = 0;
sample[1].left = 0;
sample[1].right = 0;
this->speaker_->play((uint8_t *) (&sample), 8);
this->speaker_->finish();
this->set_state_(State::STATE_STOPPING);
}
#endif
// Ensure no more notes are played in case finish_() is called for an error.
this->position_ = this->rtttl_.length();
this->note_duration_ = 0; this->note_duration_ = 0;
} }
void Rtttl::loop() { void Rtttl::loop() {
if (this->note_duration_ == 0 || this->state_ == State::STATE_STOPPED) { if (this->state_ == State::STATE_STOPPED) {
this->disable_loop(); this->disable_loop();
return; return;
} }
@@ -152,6 +178,8 @@ void Rtttl::loop() {
if (this->state_ == State::STATE_STOPPING) { if (this->state_ == State::STATE_STOPPING) {
if (this->speaker_->is_stopped()) { if (this->speaker_->is_stopped()) {
this->set_state_(State::STATE_STOPPED); this->set_state_(State::STATE_STOPPED);
} else {
return;
} }
} else if (this->state_ == State::STATE_INIT) { } else if (this->state_ == State::STATE_INIT) {
if (this->speaker_->is_stopped()) { if (this->speaker_->is_stopped()) {
@@ -207,7 +235,7 @@ void Rtttl::loop() {
if (this->output_ != nullptr && millis() - this->last_note_ < this->note_duration_) if (this->output_ != nullptr && millis() - this->last_note_ < this->note_duration_)
return; return;
#endif #endif
if (!this->rtttl_[this->position_]) { if (this->position_ >= this->rtttl_.length()) {
this->finish_(); this->finish_();
return; return;
} }
@@ -346,31 +374,6 @@ void Rtttl::loop() {
this->last_note_ = millis(); this->last_note_ = millis();
} }
void Rtttl::finish_() {
#ifdef USE_OUTPUT
if (this->output_ != nullptr) {
this->output_->set_level(0.0);
this->set_state_(State::STATE_STOPPED);
}
#endif
#ifdef USE_SPEAKER
if (this->speaker_ != nullptr) {
SpeakerSample sample[2];
sample[0].left = 0;
sample[0].right = 0;
sample[1].left = 0;
sample[1].right = 0;
this->speaker_->play((uint8_t *) (&sample), 8);
this->speaker_->finish();
this->set_state_(State::STATE_STOPPING);
}
#endif
this->note_duration_ = 0;
this->on_finished_playback_callback_.call();
ESP_LOGD(TAG, "Playback finished");
}
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_DEBUG #if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_DEBUG
static const LogString *state_to_string(State state) { static const LogString *state_to_string(State state) {
switch (state) { switch (state) {
@@ -397,7 +400,11 @@ void Rtttl::set_state_(State state) {
LOG_STR_ARG(state_to_string(state))); LOG_STR_ARG(state_to_string(state)));
// Clear loop_done when transitioning from STOPPED to any other state // Clear loop_done when transitioning from STOPPED to any other state
if (old_state == State::STATE_STOPPED && state != State::STATE_STOPPED) { if (state == State::STATE_STOPPED) {
this->disable_loop();
this->on_finished_playback_callback_.call();
ESP_LOGD(TAG, "Playback finished");
} else if (old_state == State::STATE_STOPPED) {
this->enable_loop(); this->enable_loop();
} }
} }

View File

@@ -60,35 +60,60 @@ class Rtttl : public Component {
} }
return ret; return ret;
} }
/**
* @brief Finalizes the playback of the RTTTL string.
*
* This method is called internally when the end of the RTTTL string is reached
* or when a parsing error occurs. It stops the output, sets the component state,
* and triggers the on_finished_playback_callback_.
*/
void finish_(); void finish_();
void set_state_(State state); void set_state_(State state);
/// The RTTTL string to play.
std::string rtttl_{""}; std::string rtttl_{""};
/// The current position in the RTTTL string.
size_t position_{0}; size_t position_{0};
/// The duration of a whole note in milliseconds.
uint16_t wholenote_; uint16_t wholenote_;
/// The default duration of a note (e.g. 4 for a quarter note).
uint16_t default_duration_; uint16_t default_duration_;
/// The default octave for a note.
uint16_t default_octave_; uint16_t default_octave_;
/// The time the last note was started.
uint32_t last_note_; uint32_t last_note_;
/// The duration of the current note in milliseconds.
uint16_t note_duration_; uint16_t note_duration_;
/// The frequency of the current note in Hz.
uint32_t output_freq_; uint32_t output_freq_;
/// The gain of the output.
float gain_{0.6f}; float gain_{0.6f};
/// The current state of the RTTTL player.
State state_{State::STATE_STOPPED}; State state_{State::STATE_STOPPED};
#ifdef USE_OUTPUT #ifdef USE_OUTPUT
/// The output to write the sound to.
output::FloatOutput *output_; output::FloatOutput *output_;
#endif #endif
#ifdef USE_SPEAKER #ifdef USE_SPEAKER
/// The speaker to write the sound to.
speaker::Speaker *speaker_{nullptr}; speaker::Speaker *speaker_{nullptr};
/// The sample rate of the speaker.
int sample_rate_{16000}; int sample_rate_{16000};
/// The number of samples for one full cycle of a note's waveform, in Q10 fixed-point format.
int samples_per_wave_{0}; int samples_per_wave_{0};
/// The number of samples sent.
int samples_sent_{0}; int samples_sent_{0};
/// The total number of samples to send.
int samples_count_{0}; int samples_count_{0};
/// The number of samples for the gap between notes.
int samples_gap_{0}; int samples_gap_{0};
#endif #endif
/// The callback to call when playback is finished.
CallbackManager<void()> on_finished_playback_callback_; CallbackManager<void()> on_finished_playback_callback_;
}; };

View File

@@ -151,6 +151,8 @@ void WiFiComponent::loop() {
this->status_set_warning("waiting to reconnect"); this->status_set_warning("waiting to reconnect");
if (millis() - this->action_started_ > 5000) { if (millis() - this->action_started_ > 5000) {
if (this->fast_connect_ || this->retry_hidden_) { if (this->fast_connect_ || this->retry_hidden_) {
if (!this->selected_ap_.get_bssid().has_value())
this->selected_ap_ = this->sta_[0];
this->start_connecting(this->selected_ap_, false); this->start_connecting(this->selected_ap_, false);
} else { } else {
this->start_scanning(); this->start_scanning();
@@ -670,10 +672,12 @@ void WiFiComponent::check_connecting_finished() {
return; return;
} }
ESP_LOGI(TAG, "Connected");
// We won't retry hidden networks unless a reconnect fails more than three times again // We won't retry hidden networks unless a reconnect fails more than three times again
if (this->retry_hidden_ && !this->selected_ap_.get_hidden())
ESP_LOGW(TAG, "Network '%s' should be marked as hidden", this->selected_ap_.get_ssid().c_str());
this->retry_hidden_ = false; this->retry_hidden_ = false;
ESP_LOGI(TAG, "Connected");
this->print_connect_params_(); this->print_connect_params_();
if (this->has_ap()) { if (this->has_ap()) {