esphome 2025.6.0b1__py3-none-any.whl → 2025.6.0b3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. esphome/components/api/api_connection.cpp +53 -33
  2. esphome/components/api/api_connection.h +24 -25
  3. esphome/components/api/api_pb2.cpp +1 -0
  4. esphome/components/api/api_pb2.h +65 -226
  5. esphome/components/api/client.py +1 -3
  6. esphome/components/api/proto.h +1 -1
  7. esphome/components/bluetooth_proxy/bluetooth_proxy.cpp +2 -2
  8. esphome/components/bluetooth_proxy/bluetooth_proxy.h +1 -1
  9. esphome/components/bme280_base/bme280_base.cpp +2 -3
  10. esphome/components/datetime/date_entity.cpp +5 -5
  11. esphome/components/datetime/datetime_base.h +0 -5
  12. esphome/components/datetime/datetime_entity.cpp +8 -8
  13. esphome/components/datetime/time_entity.cpp +4 -4
  14. esphome/components/esp32/__init__.py +30 -3
  15. esphome/components/esp32_ble/ble.cpp +90 -45
  16. esphome/components/esp32_ble/ble.h +24 -5
  17. esphome/components/esp32_ble/ble_event.h +172 -32
  18. esphome/components/esp32_ble/ble_scan_result.h +24 -0
  19. esphome/components/esp32_ble/queue.h +53 -27
  20. esphome/components/esp32_ble_tracker/__init__.py +1 -0
  21. esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp +95 -66
  22. esphome/components/esp32_ble_tracker/esp32_ble_tracker.h +16 -16
  23. esphome/components/esp32_camera/esp32_camera.cpp +1 -1
  24. esphome/components/fan/fan.cpp +26 -17
  25. esphome/components/i2s_audio/i2s_audio.cpp +1 -1
  26. esphome/components/i2s_audio/microphone/i2s_audio_microphone.cpp +47 -29
  27. esphome/components/i2s_audio/microphone/i2s_audio_microphone.h +2 -0
  28. esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp +28 -10
  29. esphome/components/i2s_audio/speaker/i2s_audio_speaker.h +1 -0
  30. esphome/components/kmeteriso/kmeteriso.cpp +2 -3
  31. esphome/components/light/light_state.h +15 -15
  32. esphome/components/logger/logger.cpp +2 -15
  33. esphome/components/logger/logger.h +1 -2
  34. esphome/components/mqtt/mqtt_component.cpp +1 -1
  35. esphome/components/nextion/binary_sensor/nextion_binarysensor.cpp +1 -1
  36. esphome/components/nextion/nextion_upload_arduino.cpp +12 -9
  37. esphome/components/nextion/nextion_upload_idf.cpp +11 -9
  38. esphome/components/nextion/sensor/nextion_sensor.cpp +1 -1
  39. esphome/components/nextion/text_sensor/nextion_textsensor.cpp +1 -1
  40. esphome/components/number/number.cpp +1 -1
  41. esphome/components/number/number.h +0 -4
  42. esphome/components/prometheus/__init__.py +0 -1
  43. esphome/components/select/select.cpp +1 -1
  44. esphome/components/select/select.h +0 -4
  45. esphome/components/sensor/sensor.cpp +8 -4
  46. esphome/components/sensor/sensor.h +3 -6
  47. esphome/components/spi/spi_arduino.cpp +22 -9
  48. esphome/components/status_led/light/status_led_light.cpp +2 -2
  49. esphome/components/status_led/light/status_led_light.h +1 -1
  50. esphome/components/switch/switch.h +13 -7
  51. esphome/components/template/alarm_control_panel/template_alarm_control_panel.cpp +14 -9
  52. esphome/components/template/alarm_control_panel/template_alarm_control_panel.h +1 -0
  53. esphome/components/text/text.cpp +1 -1
  54. esphome/components/text/text.h +0 -4
  55. esphome/components/text_sensor/text_sensor.cpp +8 -4
  56. esphome/components/text_sensor/text_sensor.h +6 -6
  57. esphome/components/update/update_entity.cpp +1 -1
  58. esphome/components/update/update_entity.h +0 -3
  59. esphome/components/uptime/sensor/uptime_timestamp_sensor.cpp +1 -1
  60. esphome/components/web_server_idf/__init__.py +0 -2
  61. esphome/components/web_server_idf/web_server_idf.cpp +6 -2
  62. esphome/components/web_server_idf/web_server_idf.h +7 -0
  63. esphome/components/weikai/weikai.cpp +1 -1
  64. esphome/const.py +1 -1
  65. esphome/core/application.cpp +14 -8
  66. esphome/core/application.h +7 -7
  67. esphome/core/component.cpp +36 -15
  68. esphome/core/component.h +30 -13
  69. esphome/core/entity_base.cpp +4 -16
  70. esphome/core/entity_base.h +27 -13
  71. esphome/core/helpers.h +1 -1
  72. esphome/wizard.py +0 -16
  73. {esphome-2025.6.0b1.dist-info → esphome-2025.6.0b3.dist-info}/METADATA +2 -2
  74. {esphome-2025.6.0b1.dist-info → esphome-2025.6.0b3.dist-info}/RECORD +78 -77
  75. {esphome-2025.6.0b1.dist-info → esphome-2025.6.0b3.dist-info}/WHEEL +0 -0
  76. {esphome-2025.6.0b1.dist-info → esphome-2025.6.0b3.dist-info}/entry_points.txt +0 -0
  77. {esphome-2025.6.0b1.dist-info → esphome-2025.6.0b3.dist-info}/licenses/LICENSE +0 -0
  78. {esphome-2025.6.0b1.dist-info → esphome-2025.6.0b3.dist-info}/top_level.txt +0 -0
@@ -6,6 +6,7 @@
6
6
  #include "esphome/core/helpers.h"
7
7
 
8
8
  #include <array>
9
+ #include <atomic>
9
10
  #include <string>
10
11
  #include <vector>
11
12
 
@@ -62,7 +63,7 @@ class ESPBLEiBeacon {
62
63
 
63
64
  class ESPBTDevice {
64
65
  public:
65
- void parse_scan_rst(const esp_ble_gap_cb_param_t::ble_scan_result_evt_param &param);
66
+ void parse_scan_rst(const BLEScanResult &scan_result);
66
67
 
67
68
  std::string address_str() const;
68
69
 
@@ -84,8 +85,6 @@ class ESPBTDevice {
84
85
 
85
86
  const std::vector<ServiceData> &get_service_datas() const { return service_datas_; }
86
87
 
87
- const esp_ble_gap_cb_param_t::ble_scan_result_evt_param &get_scan_result() const { return scan_result_; }
88
-
89
88
  bool resolve_irk(const uint8_t *irk) const;
90
89
 
91
90
  optional<ESPBLEiBeacon> get_ibeacon() const {
@@ -98,7 +97,7 @@ class ESPBTDevice {
98
97
  }
99
98
 
100
99
  protected:
101
- void parse_adv_(const esp_ble_gap_cb_param_t::ble_scan_result_evt_param &param);
100
+ void parse_adv_(const uint8_t *payload, uint8_t len);
102
101
 
103
102
  esp_bd_addr_t address_{
104
103
  0,
@@ -112,7 +111,6 @@ class ESPBTDevice {
112
111
  std::vector<ESPBTUUID> service_uuids_{};
113
112
  std::vector<ServiceData> manufacturer_datas_{};
114
113
  std::vector<ServiceData> service_datas_{};
115
- esp_ble_gap_cb_param_t::ble_scan_result_evt_param scan_result_{};
116
114
  };
117
115
 
118
116
  class ESP32BLETracker;
@@ -121,9 +119,7 @@ class ESPBTDeviceListener {
121
119
  public:
122
120
  virtual void on_scan_end() {}
123
121
  virtual bool parse_device(const ESPBTDevice &device) = 0;
124
- virtual bool parse_devices(esp_ble_gap_cb_param_t::ble_scan_result_evt_param *advertisements, size_t count) {
125
- return false;
126
- };
122
+ virtual bool parse_devices(const BLEScanResult *scan_results, size_t count) { return false; };
127
123
  virtual AdvertisementParserType get_advertisement_parser_type() {
128
124
  return AdvertisementParserType::PARSED_ADVERTISEMENTS;
129
125
  };
@@ -210,6 +206,7 @@ class ESPBTClient : public ESPBTDeviceListener {
210
206
 
211
207
  class ESP32BLETracker : public Component,
212
208
  public GAPEventHandler,
209
+ public GAPScanEventHandler,
213
210
  public GATTcEventHandler,
214
211
  public BLEStatusEventHandler,
215
212
  public Parented<ESP32BLE> {
@@ -240,6 +237,7 @@ class ESP32BLETracker : public Component,
240
237
  void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
241
238
  esp_ble_gattc_cb_param_t *param) override;
242
239
  void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) override;
240
+ void gap_scan_event_handler(const BLEScanResult &scan_result) override;
243
241
  void ble_before_disabled_event_handler() override;
244
242
 
245
243
  void add_scanner_state_callback(std::function<void(ScannerState)> &&callback) {
@@ -285,14 +283,16 @@ class ESP32BLETracker : public Component,
285
283
  bool ble_was_disabled_{true};
286
284
  bool raw_advertisements_{false};
287
285
  bool parse_advertisements_{false};
288
- SemaphoreHandle_t scan_result_lock_;
289
- size_t scan_result_index_{0};
290
- #ifdef USE_PSRAM
291
- const static u_int8_t SCAN_RESULT_BUFFER_SIZE = 32;
292
- #else
293
- const static u_int8_t SCAN_RESULT_BUFFER_SIZE = 20;
294
- #endif // USE_PSRAM
295
- esp_ble_gap_cb_param_t::ble_scan_result_evt_param *scan_result_buffer_;
286
+
287
+ // Lock-free Single-Producer Single-Consumer (SPSC) ring buffer for scan results
288
+ // Producer: ESP-IDF Bluetooth stack callback (gap_scan_event_handler)
289
+ // Consumer: ESPHome main loop (loop() method)
290
+ // This design ensures zero blocking in the BT callback and prevents scan result loss
291
+ BLEScanResult *scan_ring_buffer_;
292
+ std::atomic<size_t> ring_write_index_{0}; // Written only by BT callback (producer)
293
+ std::atomic<size_t> ring_read_index_{0}; // Written only by main loop (consumer)
294
+ std::atomic<size_t> scan_results_dropped_{0}; // Tracks buffer overflow events
295
+
296
296
  esp_bt_status_t scan_start_failed_{ESP_BT_STATUS_SUCCESS};
297
297
  esp_bt_status_t scan_set_param_failed_{ESP_BT_STATUS_SUCCESS};
298
298
  int connecting_{0};
@@ -57,7 +57,7 @@ void ESP32Camera::dump_config() {
57
57
  " External Clock: Pin:%d Frequency:%u\n"
58
58
  " I2C Pins: SDA:%d SCL:%d\n"
59
59
  " Reset Pin: %d",
60
- this->name_.c_str(), YESNO(this->internal_), conf.pin_d0, conf.pin_d1, conf.pin_d2, conf.pin_d3,
60
+ this->name_.c_str(), YESNO(this->is_internal()), conf.pin_d0, conf.pin_d1, conf.pin_d2, conf.pin_d3,
61
61
  conf.pin_d4, conf.pin_d5, conf.pin_d6, conf.pin_d7, conf.pin_vsync, conf.pin_href, conf.pin_pclk,
62
62
  conf.pin_xclk, conf.xclk_freq_hz, conf.pin_sccb_sda, conf.pin_sccb_scl, conf.pin_reset);
63
63
  switch (this->config_.frame_size) {
@@ -41,39 +41,48 @@ void FanCall::perform() {
41
41
  void FanCall::validate_() {
42
42
  auto traits = this->parent_.get_traits();
43
43
 
44
- if (this->speed_.has_value())
44
+ if (this->speed_.has_value()) {
45
45
  this->speed_ = clamp(*this->speed_, 1, traits.supported_speed_count());
46
46
 
47
- if (this->binary_state_.has_value() && *this->binary_state_) {
48
- // when turning on, if neither current nor new speed available, set speed to 100%
49
- if (traits.supports_speed() && !this->parent_.state && this->parent_.speed == 0 && !this->speed_.has_value()) {
50
- this->speed_ = traits.supported_speed_count();
47
+ // https://developers.home-assistant.io/docs/core/entity/fan/#preset-modes
48
+ // "Manually setting a speed must disable any set preset mode"
49
+ this->preset_mode_.clear();
50
+ }
51
+
52
+ if (!this->preset_mode_.empty()) {
53
+ const auto &preset_modes = traits.supported_preset_modes();
54
+ if (preset_modes.find(this->preset_mode_) == preset_modes.end()) {
55
+ ESP_LOGW(TAG, "%s: Preset mode '%s' not supported", this->parent_.get_name().c_str(), this->preset_mode_.c_str());
56
+ this->preset_mode_.clear();
51
57
  }
52
58
  }
53
59
 
60
+ // when turning on...
61
+ if (!this->parent_.state && this->binary_state_.has_value() &&
62
+ *this->binary_state_
63
+ // ..,and no preset mode will be active...
64
+ && this->preset_mode_.empty() &&
65
+ this->parent_.preset_mode.empty()
66
+ // ...and neither current nor new speed is available...
67
+ && traits.supports_speed() && this->parent_.speed == 0 && !this->speed_.has_value()) {
68
+ // ...set speed to 100%
69
+ this->speed_ = traits.supported_speed_count();
70
+ }
71
+
54
72
  if (this->oscillating_.has_value() && !traits.supports_oscillation()) {
55
- ESP_LOGW(TAG, "'%s' - This fan does not support oscillation!", this->parent_.get_name().c_str());
73
+ ESP_LOGW(TAG, "%s: Oscillation not supported", this->parent_.get_name().c_str());
56
74
  this->oscillating_.reset();
57
75
  }
58
76
 
59
77
  if (this->speed_.has_value() && !traits.supports_speed()) {
60
- ESP_LOGW(TAG, "'%s' - This fan does not support speeds!", this->parent_.get_name().c_str());
78
+ ESP_LOGW(TAG, "%s: Speed control not supported", this->parent_.get_name().c_str());
61
79
  this->speed_.reset();
62
80
  }
63
81
 
64
82
  if (this->direction_.has_value() && !traits.supports_direction()) {
65
- ESP_LOGW(TAG, "'%s' - This fan does not support directions!", this->parent_.get_name().c_str());
83
+ ESP_LOGW(TAG, "%s: Direction control not supported", this->parent_.get_name().c_str());
66
84
  this->direction_.reset();
67
85
  }
68
-
69
- if (!this->preset_mode_.empty()) {
70
- const auto &preset_modes = traits.supported_preset_modes();
71
- if (preset_modes.find(this->preset_mode_) == preset_modes.end()) {
72
- ESP_LOGW(TAG, "'%s' - This fan does not support preset mode '%s'!", this->parent_.get_name().c_str(),
73
- this->preset_mode_.c_str());
74
- this->preset_mode_.clear();
75
- }
76
- }
77
86
  }
78
87
 
79
88
  FanCall FanRestoreState::to_call(Fan &fan) {
@@ -18,7 +18,7 @@ void I2SAudioComponent::setup() {
18
18
 
19
19
  static i2s_port_t next_port_num = I2S_NUM_0;
20
20
  if (next_port_num >= I2S_NUM_MAX) {
21
- ESP_LOGE(TAG, "Too many I2S Audio components");
21
+ ESP_LOGE(TAG, "Too many components");
22
22
  this->mark_failed();
23
23
  return;
24
24
  }
@@ -45,7 +45,7 @@ void I2SAudioMicrophone::setup() {
45
45
  #if SOC_I2S_SUPPORTS_ADC
46
46
  if (this->adc_) {
47
47
  if (this->parent_->get_port() != I2S_NUM_0) {
48
- ESP_LOGE(TAG, "Internal ADC only works on I2S0!");
48
+ ESP_LOGE(TAG, "Internal ADC only works on I2S0");
49
49
  this->mark_failed();
50
50
  return;
51
51
  }
@@ -55,7 +55,7 @@ void I2SAudioMicrophone::setup() {
55
55
  {
56
56
  if (this->pdm_) {
57
57
  if (this->parent_->get_port() != I2S_NUM_0) {
58
- ESP_LOGE(TAG, "PDM only works on I2S0!");
58
+ ESP_LOGE(TAG, "PDM only works on I2S0");
59
59
  this->mark_failed();
60
60
  return;
61
61
  }
@@ -64,14 +64,14 @@ void I2SAudioMicrophone::setup() {
64
64
 
65
65
  this->active_listeners_semaphore_ = xSemaphoreCreateCounting(MAX_LISTENERS, MAX_LISTENERS);
66
66
  if (this->active_listeners_semaphore_ == nullptr) {
67
- ESP_LOGE(TAG, "Failed to create semaphore");
67
+ ESP_LOGE(TAG, "Creating semaphore failed");
68
68
  this->mark_failed();
69
69
  return;
70
70
  }
71
71
 
72
72
  this->event_group_ = xEventGroupCreate();
73
73
  if (this->event_group_ == nullptr) {
74
- ESP_LOGE(TAG, "Failed to create event group");
74
+ ESP_LOGE(TAG, "Creating event group failed");
75
75
  this->mark_failed();
76
76
  return;
77
77
  }
@@ -79,6 +79,15 @@ void I2SAudioMicrophone::setup() {
79
79
  this->configure_stream_settings_();
80
80
  }
81
81
 
82
+ void I2SAudioMicrophone::dump_config() {
83
+ ESP_LOGCONFIG(TAG,
84
+ "Microphone:\n"
85
+ " Pin: %d\n"
86
+ " PDM: %s\n"
87
+ " DC offset correction: %s",
88
+ static_cast<int8_t>(this->din_pin_), YESNO(this->pdm_), YESNO(this->correct_dc_offset_));
89
+ }
90
+
82
91
  void I2SAudioMicrophone::configure_stream_settings_() {
83
92
  uint8_t channel_count = 1;
84
93
  #ifdef USE_I2S_LEGACY
@@ -127,6 +136,7 @@ bool I2SAudioMicrophone::start_driver_() {
127
136
  if (!this->parent_->try_lock()) {
128
137
  return false; // Waiting for another i2s to return lock
129
138
  }
139
+ this->locked_driver_ = true;
130
140
  esp_err_t err;
131
141
 
132
142
  #ifdef USE_I2S_LEGACY
@@ -151,7 +161,7 @@ bool I2SAudioMicrophone::start_driver_() {
151
161
  config.mode = (i2s_mode_t) (config.mode | I2S_MODE_ADC_BUILT_IN);
152
162
  err = i2s_driver_install(this->parent_->get_port(), &config, 0, nullptr);
153
163
  if (err != ESP_OK) {
154
- ESP_LOGE(TAG, "Error installing I2S driver: %s", esp_err_to_name(err));
164
+ ESP_LOGE(TAG, "Error installing driver: %s", esp_err_to_name(err));
155
165
  return false;
156
166
  }
157
167
 
@@ -174,7 +184,7 @@ bool I2SAudioMicrophone::start_driver_() {
174
184
 
175
185
  err = i2s_driver_install(this->parent_->get_port(), &config, 0, nullptr);
176
186
  if (err != ESP_OK) {
177
- ESP_LOGE(TAG, "Error installing I2S driver: %s", esp_err_to_name(err));
187
+ ESP_LOGE(TAG, "Error installing driver: %s", esp_err_to_name(err));
178
188
  return false;
179
189
  }
180
190
 
@@ -183,7 +193,7 @@ bool I2SAudioMicrophone::start_driver_() {
183
193
 
184
194
  err = i2s_set_pin(this->parent_->get_port(), &pin_config);
185
195
  if (err != ESP_OK) {
186
- ESP_LOGE(TAG, "Error setting I2S pin: %s", esp_err_to_name(err));
196
+ ESP_LOGE(TAG, "Error setting pin: %s", esp_err_to_name(err));
187
197
  return false;
188
198
  }
189
199
  }
@@ -198,7 +208,7 @@ bool I2SAudioMicrophone::start_driver_() {
198
208
  /* Allocate a new RX channel and get the handle of this channel */
199
209
  err = i2s_new_channel(&chan_cfg, NULL, &this->rx_handle_);
200
210
  if (err != ESP_OK) {
201
- ESP_LOGE(TAG, "Error creating new I2S channel: %s", esp_err_to_name(err));
211
+ ESP_LOGE(TAG, "Error creating channel: %s", esp_err_to_name(err));
202
212
  return false;
203
213
  }
204
214
 
@@ -270,14 +280,14 @@ bool I2SAudioMicrophone::start_driver_() {
270
280
  err = i2s_channel_init_std_mode(this->rx_handle_, &std_cfg);
271
281
  }
272
282
  if (err != ESP_OK) {
273
- ESP_LOGE(TAG, "Error initializing I2S channel: %s", esp_err_to_name(err));
283
+ ESP_LOGE(TAG, "Error initializing channel: %s", esp_err_to_name(err));
274
284
  return false;
275
285
  }
276
286
 
277
287
  /* Before reading data, start the RX channel first */
278
288
  i2s_channel_enable(this->rx_handle_);
279
289
  if (err != ESP_OK) {
280
- ESP_LOGE(TAG, "Error enabling I2S Microphone: %s", esp_err_to_name(err));
290
+ ESP_LOGE(TAG, "Enabling failed: %s", esp_err_to_name(err));
281
291
  return false;
282
292
  }
283
293
  #endif
@@ -304,31 +314,37 @@ void I2SAudioMicrophone::stop_driver_() {
304
314
  if (this->adc_) {
305
315
  err = i2s_adc_disable(this->parent_->get_port());
306
316
  if (err != ESP_OK) {
307
- ESP_LOGW(TAG, "Error disabling ADC - it may not have started: %s", esp_err_to_name(err));
317
+ ESP_LOGW(TAG, "Error disabling ADC: %s", esp_err_to_name(err));
308
318
  }
309
319
  }
310
320
  #endif
311
321
  err = i2s_stop(this->parent_->get_port());
312
322
  if (err != ESP_OK) {
313
- ESP_LOGW(TAG, "Error stopping I2S microphone - it may not have started: %s", esp_err_to_name(err));
323
+ ESP_LOGW(TAG, "Error stopping: %s", esp_err_to_name(err));
314
324
  }
315
325
  err = i2s_driver_uninstall(this->parent_->get_port());
316
326
  if (err != ESP_OK) {
317
- ESP_LOGW(TAG, "Error uninstalling I2S driver - it may not have started: %s", esp_err_to_name(err));
327
+ ESP_LOGW(TAG, "Error uninstalling driver: %s", esp_err_to_name(err));
318
328
  }
319
329
  #else
320
- /* Have to stop the channel before deleting it */
321
- err = i2s_channel_disable(this->rx_handle_);
322
- if (err != ESP_OK) {
323
- ESP_LOGW(TAG, "Error stopping I2S microphone - it may not have started: %s", esp_err_to_name(err));
324
- }
325
- /* If the handle is not needed any more, delete it to release the channel resources */
326
- err = i2s_del_channel(this->rx_handle_);
327
- if (err != ESP_OK) {
328
- ESP_LOGW(TAG, "Error deleting I2S channel - it may not have started: %s", esp_err_to_name(err));
330
+ if (this->rx_handle_ != nullptr) {
331
+ /* Have to stop the channel before deleting it */
332
+ err = i2s_channel_disable(this->rx_handle_);
333
+ if (err != ESP_OK) {
334
+ ESP_LOGW(TAG, "Error stopping: %s", esp_err_to_name(err));
335
+ }
336
+ /* If the handle is not needed any more, delete it to release the channel resources */
337
+ err = i2s_del_channel(this->rx_handle_);
338
+ if (err != ESP_OK) {
339
+ ESP_LOGW(TAG, "Error deleting channel: %s", esp_err_to_name(err));
340
+ }
341
+ this->rx_handle_ = nullptr;
329
342
  }
330
343
  #endif
331
- this->parent_->unlock();
344
+ if (this->locked_driver_) {
345
+ this->parent_->unlock();
346
+ this->locked_driver_ = false;
347
+ }
332
348
  }
333
349
 
334
350
  void I2SAudioMicrophone::mic_task(void *params) {
@@ -400,7 +416,7 @@ size_t I2SAudioMicrophone::read_(uint8_t *buf, size_t len, TickType_t ticks_to_w
400
416
  // Ignore ESP_ERR_TIMEOUT if ticks_to_wait = 0, as it will read the data on the next call
401
417
  if (!this->status_has_warning()) {
402
418
  // Avoid spamming the logs with the error message if its repeated
403
- ESP_LOGW(TAG, "Error reading from I2S microphone: %s", esp_err_to_name(err));
419
+ ESP_LOGW(TAG, "Read error: %s", esp_err_to_name(err));
404
420
  }
405
421
  this->status_set_warning();
406
422
  return 0;
@@ -428,19 +444,19 @@ void I2SAudioMicrophone::loop() {
428
444
  uint32_t event_group_bits = xEventGroupGetBits(this->event_group_);
429
445
 
430
446
  if (event_group_bits & MicrophoneEventGroupBits::TASK_STARTING) {
431
- ESP_LOGD(TAG, "Task started, attempting to allocate buffer");
447
+ ESP_LOGV(TAG, "Task started, attempting to allocate buffer");
432
448
  xEventGroupClearBits(this->event_group_, MicrophoneEventGroupBits::TASK_STARTING);
433
449
  }
434
450
 
435
451
  if (event_group_bits & MicrophoneEventGroupBits::TASK_RUNNING) {
436
- ESP_LOGD(TAG, "Task is running and reading data");
452
+ ESP_LOGV(TAG, "Task is running and reading data");
437
453
 
438
454
  xEventGroupClearBits(this->event_group_, MicrophoneEventGroupBits::TASK_RUNNING);
439
455
  this->state_ = microphone::STATE_RUNNING;
440
456
  }
441
457
 
442
458
  if ((event_group_bits & MicrophoneEventGroupBits::TASK_STOPPED)) {
443
- ESP_LOGD(TAG, "Task finished, freeing resources and uninstalling I2S driver");
459
+ ESP_LOGV(TAG, "Task finished, freeing resources and uninstalling driver");
444
460
 
445
461
  vTaskDelete(this->task_handle_);
446
462
  this->task_handle_ = nullptr;
@@ -470,7 +486,8 @@ void I2SAudioMicrophone::loop() {
470
486
  }
471
487
 
472
488
  if (!this->start_driver_()) {
473
- this->status_momentary_error("I2S driver failed to start, unloading it and attempting again in 1 second", 1000);
489
+ ESP_LOGE(TAG, "Driver failed to start; retrying in 1 second");
490
+ this->status_momentary_error("driver_fail", 1000);
474
491
  this->stop_driver_(); // Stop/frees whatever possibly started
475
492
  break;
476
493
  }
@@ -480,7 +497,8 @@ void I2SAudioMicrophone::loop() {
480
497
  &this->task_handle_);
481
498
 
482
499
  if (this->task_handle_ == nullptr) {
483
- this->status_momentary_error("Task failed to start, attempting again in 1 second", 1000);
500
+ ESP_LOGE(TAG, "Task failed to start, retrying in 1 second");
501
+ this->status_momentary_error("task_fail", 1000);
484
502
  this->stop_driver_(); // Stops the driver to return the lock; will be reloaded in next attempt
485
503
  }
486
504
  }
@@ -18,6 +18,7 @@ namespace i2s_audio {
18
18
  class I2SAudioMicrophone : public I2SAudioIn, public microphone::Microphone, public Component {
19
19
  public:
20
20
  void setup() override;
21
+ void dump_config() override;
21
22
  void start() override;
22
23
  void stop() override;
23
24
 
@@ -80,6 +81,7 @@ class I2SAudioMicrophone : public I2SAudioIn, public microphone::Microphone, pub
80
81
  bool pdm_{false};
81
82
 
82
83
  bool correct_dc_offset_;
84
+ bool locked_driver_{false};
83
85
  int32_t dc_offset_{0};
84
86
  };
85
87
 
@@ -110,29 +110,48 @@ void I2SAudioSpeaker::setup() {
110
110
  }
111
111
  }
112
112
 
113
+ void I2SAudioSpeaker::dump_config() {
114
+ ESP_LOGCONFIG(TAG,
115
+ "Speaker:\n"
116
+ " Pin: %d\n"
117
+ " Buffer duration: %" PRIu32,
118
+ static_cast<int8_t>(this->dout_pin_), this->buffer_duration_ms_);
119
+ if (this->timeout_.has_value()) {
120
+ ESP_LOGCONFIG(TAG, " Timeout: %" PRIu32 " ms", this->timeout_.value());
121
+ }
122
+ #ifdef USE_I2S_LEGACY
123
+ #if SOC_I2S_SUPPORTS_DAC
124
+ ESP_LOGCONFIG(TAG, " Internal DAC mode: %d", static_cast<int8_t>(this->internal_dac_mode_));
125
+ #endif
126
+ ESP_LOGCONFIG(TAG, " Communication format: %d", static_cast<int8_t>(this->i2s_comm_fmt_));
127
+ #else
128
+ ESP_LOGCONFIG(TAG, " Communication format: %s", this->i2s_comm_fmt_.c_str());
129
+ #endif
130
+ }
131
+
113
132
  void I2SAudioSpeaker::loop() {
114
133
  uint32_t event_group_bits = xEventGroupGetBits(this->event_group_);
115
134
 
116
135
  if (event_group_bits & SpeakerEventGroupBits::STATE_STARTING) {
117
- ESP_LOGD(TAG, "Starting Speaker");
136
+ ESP_LOGD(TAG, "Starting");
118
137
  this->state_ = speaker::STATE_STARTING;
119
138
  xEventGroupClearBits(this->event_group_, SpeakerEventGroupBits::STATE_STARTING);
120
139
  }
121
140
  if (event_group_bits & SpeakerEventGroupBits::STATE_RUNNING) {
122
- ESP_LOGD(TAG, "Started Speaker");
141
+ ESP_LOGD(TAG, "Started");
123
142
  this->state_ = speaker::STATE_RUNNING;
124
143
  xEventGroupClearBits(this->event_group_, SpeakerEventGroupBits::STATE_RUNNING);
125
144
  this->status_clear_warning();
126
145
  this->status_clear_error();
127
146
  }
128
147
  if (event_group_bits & SpeakerEventGroupBits::STATE_STOPPING) {
129
- ESP_LOGD(TAG, "Stopping Speaker");
148
+ ESP_LOGD(TAG, "Stopping");
130
149
  this->state_ = speaker::STATE_STOPPING;
131
150
  xEventGroupClearBits(this->event_group_, SpeakerEventGroupBits::STATE_STOPPING);
132
151
  }
133
152
  if (event_group_bits & SpeakerEventGroupBits::STATE_STOPPED) {
134
153
  if (!this->task_created_) {
135
- ESP_LOGD(TAG, "Stopped Speaker");
154
+ ESP_LOGD(TAG, "Stopped");
136
155
  this->state_ = speaker::STATE_STOPPED;
137
156
  xEventGroupClearBits(this->event_group_, SpeakerEventGroupBits::ALL_BITS);
138
157
  this->speaker_task_handle_ = nullptr;
@@ -140,20 +159,19 @@ void I2SAudioSpeaker::loop() {
140
159
  }
141
160
 
142
161
  if (event_group_bits & SpeakerEventGroupBits::ERR_TASK_FAILED_TO_START) {
143
- this->status_set_error("Failed to start speaker task");
162
+ this->status_set_error("Failed to start task");
144
163
  xEventGroupClearBits(this->event_group_, SpeakerEventGroupBits::ERR_TASK_FAILED_TO_START);
145
164
  }
146
165
 
147
166
  if (event_group_bits & SpeakerEventGroupBits::ALL_ERR_ESP_BITS) {
148
167
  uint32_t error_bits = event_group_bits & SpeakerEventGroupBits::ALL_ERR_ESP_BITS;
149
- ESP_LOGW(TAG, "Error writing to I2S: %s", esp_err_to_name(err_bit_to_esp_err(error_bits)));
168
+ ESP_LOGW(TAG, "Writing failed: %s", esp_err_to_name(err_bit_to_esp_err(error_bits)));
150
169
  this->status_set_warning();
151
170
  }
152
171
 
153
172
  if (event_group_bits & SpeakerEventGroupBits::ERR_ESP_NOT_SUPPORTED) {
154
- this->status_set_error("Failed to adjust I2S bus to match the incoming audio");
155
- ESP_LOGE(TAG,
156
- "Incompatible audio format: sample rate = %" PRIu32 ", channels = %" PRIu8 ", bits per sample = %" PRIu8,
173
+ this->status_set_error("Failed to adjust bus to match incoming audio");
174
+ ESP_LOGE(TAG, "Incompatible audio format: sample rate = %" PRIu32 ", channels = %u, bits per sample = %u",
157
175
  this->audio_stream_info_.get_sample_rate(), this->audio_stream_info_.get_channels(),
158
176
  this->audio_stream_info_.get_bits_per_sample());
159
177
  }
@@ -202,7 +220,7 @@ void I2SAudioSpeaker::set_mute_state(bool mute_state) {
202
220
 
203
221
  size_t I2SAudioSpeaker::play(const uint8_t *data, size_t length, TickType_t ticks_to_wait) {
204
222
  if (this->is_failed()) {
205
- ESP_LOGE(TAG, "Cannot play audio, speaker failed to setup");
223
+ ESP_LOGE(TAG, "Setup failed; cannot play audio");
206
224
  return 0;
207
225
  }
208
226
  if (this->state_ != speaker::STATE_RUNNING && this->state_ != speaker::STATE_STARTING) {
@@ -24,6 +24,7 @@ class I2SAudioSpeaker : public I2SAudioOut, public speaker::Speaker, public Comp
24
24
  float get_setup_priority() const override { return esphome::setup_priority::PROCESSOR; }
25
25
 
26
26
  void setup() override;
27
+ void dump_config() override;
27
28
  void loop() override;
28
29
 
29
30
  void set_buffer_duration(uint32_t buffer_duration_ms) { this->buffer_duration_ms_ = buffer_duration_ms; }
@@ -19,9 +19,8 @@ void KMeterISOComponent::setup() {
19
19
 
20
20
  // Mark as not failed before initializing. Some devices will turn off sensors to save on batteries
21
21
  // and when they come back on, the COMPONENT_STATE_FAILED bit must be unset on the component.
22
- if ((this->component_state_ & COMPONENT_STATE_MASK) == COMPONENT_STATE_FAILED) {
23
- this->component_state_ &= ~COMPONENT_STATE_MASK;
24
- this->component_state_ |= COMPONENT_STATE_CONSTRUCTION;
22
+ if (this->is_failed()) {
23
+ this->reset_to_construction_state();
25
24
  }
26
25
 
27
26
  auto err = this->bus_->writev(this->address_, nullptr, 0);
@@ -17,7 +17,7 @@ namespace light {
17
17
 
18
18
  class LightOutput;
19
19
 
20
- enum LightRestoreMode {
20
+ enum LightRestoreMode : uint8_t {
21
21
  LIGHT_RESTORE_DEFAULT_OFF,
22
22
  LIGHT_RESTORE_DEFAULT_ON,
23
23
  LIGHT_ALWAYS_OFF,
@@ -212,12 +212,18 @@ class LightState : public EntityBase, public Component {
212
212
 
213
213
  /// Store the output to allow effects to have more access.
214
214
  LightOutput *output_;
215
- /// Value for storing the index of the currently active effect. 0 if no effect is active
216
- uint32_t active_effect_index_{};
217
215
  /// The currently active transformer for this light (transition/flash).
218
216
  std::unique_ptr<LightTransformer> transformer_{nullptr};
219
- /// Whether the light value should be written in the next cycle.
220
- bool next_write_{true};
217
+ /// List of effects for this light.
218
+ std::vector<LightEffect *> effects_;
219
+ /// Value for storing the index of the currently active effect. 0 if no effect is active
220
+ uint32_t active_effect_index_{};
221
+ /// Default transition length for all transitions in ms.
222
+ uint32_t default_transition_length_{};
223
+ /// Transition length to use for flash transitions.
224
+ uint32_t flash_transition_length_{};
225
+ /// Gamma correction factor for the light.
226
+ float gamma_correct_{};
221
227
 
222
228
  /// Object used to store the persisted values of the light.
223
229
  ESPPreferenceObject rtc_;
@@ -236,19 +242,13 @@ class LightState : public EntityBase, public Component {
236
242
  */
237
243
  CallbackManager<void()> target_state_reached_callback_{};
238
244
 
239
- /// Default transition length for all transitions in ms.
240
- uint32_t default_transition_length_{};
241
- /// Transition length to use for flash transitions.
242
- uint32_t flash_transition_length_{};
243
- /// Gamma correction factor for the light.
244
- float gamma_correct_{};
245
- /// Restore mode of the light.
246
- LightRestoreMode restore_mode_;
247
245
  /// Initial state of the light.
248
246
  optional<LightStateRTCState> initial_state_{};
249
- /// List of effects for this light.
250
- std::vector<LightEffect *> effects_;
251
247
 
248
+ /// Restore mode of the light.
249
+ LightRestoreMode restore_mode_;
250
+ /// Whether the light value should be written in the next cycle.
251
+ bool next_write_{true};
252
252
  // for effects, true if a transformer (transition) is active.
253
253
  bool is_transformer_active_ = false;
254
254
  };
@@ -116,7 +116,7 @@ void Logger::log_vprintf_(int level, const char *tag, int line, const __FlashStr
116
116
  if (this->baud_rate_ > 0) {
117
117
  this->write_msg_(this->tx_buffer_ + msg_start);
118
118
  }
119
- this->call_log_callbacks_(level, tag, this->tx_buffer_ + msg_start);
119
+ this->log_callback_.call(level, tag, this->tx_buffer_ + msg_start);
120
120
 
121
121
  global_recursion_guard_ = false;
122
122
  }
@@ -129,19 +129,6 @@ inline int Logger::level_for(const char *tag) {
129
129
  return this->current_level_;
130
130
  }
131
131
 
132
- void HOT Logger::call_log_callbacks_(int level, const char *tag, const char *msg) {
133
- #ifdef USE_ESP32
134
- // Suppress network-logging if memory constrained
135
- // In some configurations (eg BLE enabled) there may be some transient
136
- // memory exhaustion, and trying to log when OOM can lead to a crash. Skipping
137
- // here usually allows the stack to recover instead.
138
- // See issue #1234 for analysis.
139
- if (xPortGetFreeHeapSize() < 2048)
140
- return;
141
- #endif
142
- this->log_callback_.call(level, tag, msg);
143
- }
144
-
145
132
  Logger::Logger(uint32_t baud_rate, size_t tx_buffer_size) : baud_rate_(baud_rate), tx_buffer_size_(tx_buffer_size) {
146
133
  // add 1 to buffer size for null terminator
147
134
  this->tx_buffer_ = new char[this->tx_buffer_size_ + 1]; // NOLINT
@@ -189,7 +176,7 @@ void Logger::loop() {
189
176
  this->tx_buffer_size_);
190
177
  this->write_footer_to_buffer_(this->tx_buffer_, &this->tx_buffer_at_, this->tx_buffer_size_);
191
178
  this->tx_buffer_[this->tx_buffer_at_] = '\0';
192
- this->call_log_callbacks_(message->level, message->tag, this->tx_buffer_);
179
+ this->log_callback_.call(message->level, message->tag, this->tx_buffer_);
193
180
  // At this point all the data we need from message has been transferred to the tx_buffer
194
181
  // so we can release the message to allow other tasks to use it as soon as possible.
195
182
  this->log_buffer_->release_message_main_loop(received_token);
@@ -156,7 +156,6 @@ class Logger : public Component {
156
156
  #endif
157
157
 
158
158
  protected:
159
- void call_log_callbacks_(int level, const char *tag, const char *msg);
160
159
  void write_msg_(const char *msg);
161
160
 
162
161
  // Format a log message with printf-style arguments and write it to a buffer with header, footer, and null terminator
@@ -191,7 +190,7 @@ class Logger : public Component {
191
190
  if (this->baud_rate_ > 0) {
192
191
  this->write_msg_(this->tx_buffer_); // If logging is enabled, write to console
193
192
  }
194
- this->call_log_callbacks_(level, tag, this->tx_buffer_);
193
+ this->log_callback_.call(level, tag, this->tx_buffer_);
195
194
  }
196
195
 
197
196
  // Write the body of the log message to the buffer
@@ -153,7 +153,7 @@ bool MQTTComponent::send_discovery_() {
153
153
  if (node_friendly_name.empty()) {
154
154
  node_friendly_name = node_name;
155
155
  }
156
- const std::string &node_area = App.get_area();
156
+ std::string node_area = App.get_area();
157
157
 
158
158
  JsonObject device_info = root.createNestedObject(MQTT_DEVICE);
159
159
  const auto mac = get_mac_address();
@@ -56,7 +56,7 @@ void NextionBinarySensor::set_state(bool state, bool publish, bool send_to_nexti
56
56
  this->publish_state(state);
57
57
  } else {
58
58
  this->state = state;
59
- this->has_state_ = true;
59
+ this->set_has_state(true);
60
60
  }
61
61
 
62
62
  this->update_component_settings();