esphome 2025.6.0b3__py3-none-any.whl → 2025.6.2__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 (31) hide show
  1. esphome/components/api/api_pb2.cpp +2 -0
  2. esphome/components/api/api_pb2.h +1 -0
  3. esphome/components/audio/audio_reader.cpp +41 -21
  4. esphome/components/esp32/__init__.py +2 -2
  5. esphome/components/esp32_ble/ble.cpp +108 -46
  6. esphome/components/esp32_ble/ble.h +2 -0
  7. esphome/components/esp32_ble/ble_event.h +242 -75
  8. esphome/components/esp32_ble/ble_event_pool.h +72 -0
  9. esphome/components/esp32_ble/queue.h +14 -11
  10. esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp +1 -0
  11. esphome/components/esp32_ble_tracker/esp32_ble_tracker.h +4 -0
  12. esphome/components/i2c/__init__.py +33 -0
  13. esphome/components/lvgl/widgets/qrcode.py +4 -6
  14. esphome/components/mcp23xxx_base/mcp23xxx_base.cpp +5 -1
  15. esphome/components/nextion/nextion.cpp +2 -10
  16. esphome/components/openthread/__init__.py +5 -5
  17. esphome/components/openthread/openthread.cpp +3 -3
  18. esphome/components/openthread/tlv.py +7 -0
  19. esphome/components/speaker/media_player/audio_pipeline.cpp +12 -13
  20. esphome/components/voice_assistant/__init__.py +12 -1
  21. esphome/components/voice_assistant/voice_assistant.cpp +36 -2
  22. esphome/components/voice_assistant/voice_assistant.h +4 -0
  23. esphome/config_validation.py +44 -1
  24. esphome/const.py +1 -1
  25. esphome/yaml_util.py +2 -1
  26. {esphome-2025.6.0b3.dist-info → esphome-2025.6.2.dist-info}/METADATA +1 -1
  27. {esphome-2025.6.0b3.dist-info → esphome-2025.6.2.dist-info}/RECORD +31 -30
  28. {esphome-2025.6.0b3.dist-info → esphome-2025.6.2.dist-info}/WHEEL +0 -0
  29. {esphome-2025.6.0b3.dist-info → esphome-2025.6.2.dist-info}/entry_points.txt +0 -0
  30. {esphome-2025.6.0b3.dist-info → esphome-2025.6.2.dist-info}/licenses/LICENSE +0 -0
  31. {esphome-2025.6.0b3.dist-info → esphome-2025.6.2.dist-info}/top_level.txt +0 -0
@@ -516,6 +516,8 @@ template<> const char *proto_enum_to_string<enums::VoiceAssistantEvent>(enums::V
516
516
  return "VOICE_ASSISTANT_TTS_STREAM_START";
517
517
  case enums::VOICE_ASSISTANT_TTS_STREAM_END:
518
518
  return "VOICE_ASSISTANT_TTS_STREAM_END";
519
+ case enums::VOICE_ASSISTANT_INTENT_PROGRESS:
520
+ return "VOICE_ASSISTANT_INTENT_PROGRESS";
519
521
  default:
520
522
  return "UNKNOWN";
521
523
  }
@@ -208,6 +208,7 @@ enum VoiceAssistantEvent : uint32_t {
208
208
  VOICE_ASSISTANT_STT_VAD_END = 12,
209
209
  VOICE_ASSISTANT_TTS_STREAM_START = 98,
210
210
  VOICE_ASSISTANT_TTS_STREAM_END = 99,
211
+ VOICE_ASSISTANT_INTENT_PROGRESS = 100,
211
212
  };
212
213
  enum VoiceAssistantTimerEvent : uint32_t {
213
214
  VOICE_ASSISTANT_TIMER_STARTED = 0,
@@ -5,6 +5,7 @@
5
5
  #include "esphome/core/defines.h"
6
6
  #include "esphome/core/hal.h"
7
7
  #include "esphome/core/helpers.h"
8
+ #include "esphome/core/log.h"
8
9
 
9
10
  #if CONFIG_MBEDTLS_CERTIFICATE_BUNDLE
10
11
  #include "esp_crt_bundle.h"
@@ -16,13 +17,13 @@ namespace audio {
16
17
  static const uint32_t READ_WRITE_TIMEOUT_MS = 20;
17
18
 
18
19
  static const uint32_t CONNECTION_TIMEOUT_MS = 5000;
19
-
20
- // The number of times the http read times out with no data before throwing an error
21
- static const uint32_t ERROR_COUNT_NO_DATA_READ_TIMEOUT = 100;
20
+ static const uint8_t MAX_FETCHING_HEADER_ATTEMPTS = 6;
22
21
 
23
22
  static const size_t HTTP_STREAM_BUFFER_SIZE = 2048;
24
23
 
25
- static const uint8_t MAX_REDIRECTION = 5;
24
+ static const uint8_t MAX_REDIRECTIONS = 5;
25
+
26
+ static const char *const TAG = "audio_reader";
26
27
 
27
28
  // Some common HTTP status codes - borrowed from http_request component accessed 20241224
28
29
  enum HttpStatus {
@@ -94,7 +95,7 @@ esp_err_t AudioReader::start(const std::string &uri, AudioFileType &file_type) {
94
95
  client_config.url = uri.c_str();
95
96
  client_config.cert_pem = nullptr;
96
97
  client_config.disable_auto_redirect = false;
97
- client_config.max_redirection_count = 10;
98
+ client_config.max_redirection_count = MAX_REDIRECTIONS;
98
99
  client_config.event_handler = http_event_handler;
99
100
  client_config.user_data = this;
100
101
  client_config.buffer_size = HTTP_STREAM_BUFFER_SIZE;
@@ -116,12 +117,29 @@ esp_err_t AudioReader::start(const std::string &uri, AudioFileType &file_type) {
116
117
  esp_err_t err = esp_http_client_open(this->client_, 0);
117
118
 
118
119
  if (err != ESP_OK) {
120
+ ESP_LOGE(TAG, "Failed to open URL");
119
121
  this->cleanup_connection_();
120
122
  return err;
121
123
  }
122
124
 
123
125
  int64_t header_length = esp_http_client_fetch_headers(this->client_);
126
+ uint8_t reattempt_count = 0;
127
+ while ((header_length < 0) && (reattempt_count < MAX_FETCHING_HEADER_ATTEMPTS)) {
128
+ this->cleanup_connection_();
129
+ if (header_length != -ESP_ERR_HTTP_EAGAIN) {
130
+ // Serious error, no recovery
131
+ return ESP_FAIL;
132
+ } else {
133
+ // Reconnect from a fresh state to avoid a bug where it never reads the headers even if made available
134
+ this->client_ = esp_http_client_init(&client_config);
135
+ esp_http_client_open(this->client_, 0);
136
+ header_length = esp_http_client_fetch_headers(this->client_);
137
+ ++reattempt_count;
138
+ }
139
+ }
140
+
124
141
  if (header_length < 0) {
142
+ ESP_LOGE(TAG, "Failed to fetch headers");
125
143
  this->cleanup_connection_();
126
144
  return ESP_FAIL;
127
145
  }
@@ -135,7 +153,7 @@ esp_err_t AudioReader::start(const std::string &uri, AudioFileType &file_type) {
135
153
 
136
154
  ssize_t redirect_count = 0;
137
155
 
138
- while ((esp_http_client_set_redirection(this->client_) == ESP_OK) && (redirect_count < MAX_REDIRECTION)) {
156
+ while ((esp_http_client_set_redirection(this->client_) == ESP_OK) && (redirect_count < MAX_REDIRECTIONS)) {
139
157
  err = esp_http_client_open(this->client_, 0);
140
158
  if (err != ESP_OK) {
141
159
  this->cleanup_connection_();
@@ -267,27 +285,29 @@ AudioReaderState AudioReader::http_read_() {
267
285
  return AudioReaderState::FINISHED;
268
286
  }
269
287
  } else if (this->output_transfer_buffer_->free() > 0) {
270
- size_t bytes_to_read = this->output_transfer_buffer_->free();
271
- int received_len =
272
- esp_http_client_read(this->client_, (char *) this->output_transfer_buffer_->get_buffer_end(), bytes_to_read);
288
+ int received_len = esp_http_client_read(this->client_, (char *) this->output_transfer_buffer_->get_buffer_end(),
289
+ this->output_transfer_buffer_->free());
273
290
 
274
291
  if (received_len > 0) {
275
292
  this->output_transfer_buffer_->increase_buffer_length(received_len);
276
293
  this->last_data_read_ms_ = millis();
277
- } else if (received_len < 0) {
294
+ return AudioReaderState::READING;
295
+ } else if (received_len <= 0) {
278
296
  // HTTP read error
279
- this->cleanup_connection_();
280
- return AudioReaderState::FAILED;
281
- } else {
282
- if (bytes_to_read > 0) {
283
- // Read timed out
284
- if ((millis() - this->last_data_read_ms_) > CONNECTION_TIMEOUT_MS) {
285
- this->cleanup_connection_();
286
- return AudioReaderState::FAILED;
287
- }
288
-
289
- delay(READ_WRITE_TIMEOUT_MS);
297
+ if (received_len == -1) {
298
+ // A true connection error occured, no chance at recovery
299
+ this->cleanup_connection_();
300
+ return AudioReaderState::FAILED;
290
301
  }
302
+
303
+ // Read timed out, manually verify if it has been too long since the last successful read
304
+ if ((millis() - this->last_data_read_ms_) > MAX_FETCHING_HEADER_ATTEMPTS * CONNECTION_TIMEOUT_MS) {
305
+ ESP_LOGE(TAG, "Timed out");
306
+ this->cleanup_connection_();
307
+ return AudioReaderState::FAILED;
308
+ }
309
+
310
+ delay(READ_WRITE_TIMEOUT_MS);
291
311
  }
292
312
  }
293
313
 
@@ -605,7 +605,7 @@ ESP_IDF_FRAMEWORK_SCHEMA = cv.All(
605
605
  CONF_ENABLE_LWIP_DHCP_SERVER, "wifi", default=False
606
606
  ): cv.boolean,
607
607
  cv.Optional(
608
- CONF_ENABLE_LWIP_MDNS_QUERIES, default=False
608
+ CONF_ENABLE_LWIP_MDNS_QUERIES, default=True
609
609
  ): cv.boolean,
610
610
  cv.Optional(
611
611
  CONF_ENABLE_LWIP_BRIDGE_INTERFACE, default=False
@@ -760,7 +760,7 @@ async def to_code(config):
760
760
  and not advanced[CONF_ENABLE_LWIP_DHCP_SERVER]
761
761
  ):
762
762
  add_idf_sdkconfig_option("CONFIG_LWIP_DHCPS", False)
763
- if not advanced.get(CONF_ENABLE_LWIP_MDNS_QUERIES, False):
763
+ if not advanced.get(CONF_ENABLE_LWIP_MDNS_QUERIES, True):
764
764
  add_idf_sdkconfig_option("CONFIG_LWIP_DNS_SUPPORT_MDNS_QUERIES", False)
765
765
  if not advanced.get(CONF_ENABLE_LWIP_BRIDGE_INTERFACE, False):
766
766
  add_idf_sdkconfig_option("CONFIG_LWIP_BRIDGEIF_MAX_PORTS", 0)
@@ -1,6 +1,7 @@
1
1
  #ifdef USE_ESP32
2
2
 
3
3
  #include "ble.h"
4
+ #include "ble_event_pool.h"
4
5
 
5
6
  #include "esphome/core/application.h"
6
7
  #include "esphome/core/log.h"
@@ -23,9 +24,6 @@ namespace esp32_ble {
23
24
 
24
25
  static const char *const TAG = "esp32_ble";
25
26
 
26
- static RAMAllocator<BLEEvent> EVENT_ALLOCATOR( // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
27
- RAMAllocator<BLEEvent>::ALLOW_FAILURE | RAMAllocator<BLEEvent>::ALLOC_INTERNAL);
28
-
29
27
  void ESP32BLE::setup() {
30
28
  global_ble = this;
31
29
  ESP_LOGCONFIG(TAG, "Running setup");
@@ -326,32 +324,77 @@ void ESP32BLE::loop() {
326
324
  }
327
325
  case BLEEvent::GAP: {
328
326
  esp_gap_ble_cb_event_t gap_event = ble_event->event_.gap.gap_event;
329
- if (gap_event == ESP_GAP_BLE_SCAN_RESULT_EVT) {
330
- // Use the new scan event handler - no memcpy!
331
- for (auto *scan_handler : this->gap_scan_event_handlers_) {
332
- scan_handler->gap_scan_event_handler(ble_event->scan_result());
333
- }
334
- } else if (gap_event == ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT ||
335
- gap_event == ESP_GAP_BLE_SCAN_START_COMPLETE_EVT ||
336
- gap_event == ESP_GAP_BLE_SCAN_STOP_COMPLETE_EVT) {
337
- // All three scan complete events have the same structure with just status
338
- // The scan_complete struct matches ESP-IDF's layout exactly, so this reinterpret_cast is safe
339
- // This is verified at compile-time by static_assert checks in ble_event.h
340
- // The struct already contains our copy of the status (copied in BLEEvent constructor)
341
- ESP_LOGV(TAG, "gap_event_handler - %d", gap_event);
342
- for (auto *gap_handler : this->gap_event_handlers_) {
343
- gap_handler->gap_event_handler(
344
- gap_event, reinterpret_cast<esp_ble_gap_cb_param_t *>(&ble_event->event_.gap.scan_complete));
345
- }
327
+ switch (gap_event) {
328
+ case ESP_GAP_BLE_SCAN_RESULT_EVT:
329
+ // Use the new scan event handler - no memcpy!
330
+ for (auto *scan_handler : this->gap_scan_event_handlers_) {
331
+ scan_handler->gap_scan_event_handler(ble_event->scan_result());
332
+ }
333
+ break;
334
+
335
+ // Scan complete events
336
+ case ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT:
337
+ case ESP_GAP_BLE_SCAN_START_COMPLETE_EVT:
338
+ case ESP_GAP_BLE_SCAN_STOP_COMPLETE_EVT:
339
+ // All three scan complete events have the same structure with just status
340
+ // The scan_complete struct matches ESP-IDF's layout exactly, so this reinterpret_cast is safe
341
+ // This is verified at compile-time by static_assert checks in ble_event.h
342
+ // The struct already contains our copy of the status (copied in BLEEvent constructor)
343
+ ESP_LOGV(TAG, "gap_event_handler - %d", gap_event);
344
+ for (auto *gap_handler : this->gap_event_handlers_) {
345
+ gap_handler->gap_event_handler(
346
+ gap_event, reinterpret_cast<esp_ble_gap_cb_param_t *>(&ble_event->event_.gap.scan_complete));
347
+ }
348
+ break;
349
+
350
+ // Advertising complete events
351
+ case ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT:
352
+ case ESP_GAP_BLE_SCAN_RSP_DATA_SET_COMPLETE_EVT:
353
+ case ESP_GAP_BLE_ADV_DATA_RAW_SET_COMPLETE_EVT:
354
+ case ESP_GAP_BLE_ADV_START_COMPLETE_EVT:
355
+ case ESP_GAP_BLE_ADV_STOP_COMPLETE_EVT:
356
+ // All advertising complete events have the same structure with just status
357
+ ESP_LOGV(TAG, "gap_event_handler - %d", gap_event);
358
+ for (auto *gap_handler : this->gap_event_handlers_) {
359
+ gap_handler->gap_event_handler(
360
+ gap_event, reinterpret_cast<esp_ble_gap_cb_param_t *>(&ble_event->event_.gap.adv_complete));
361
+ }
362
+ break;
363
+
364
+ // RSSI complete event
365
+ case ESP_GAP_BLE_READ_RSSI_COMPLETE_EVT:
366
+ ESP_LOGV(TAG, "gap_event_handler - %d", gap_event);
367
+ for (auto *gap_handler : this->gap_event_handlers_) {
368
+ gap_handler->gap_event_handler(
369
+ gap_event, reinterpret_cast<esp_ble_gap_cb_param_t *>(&ble_event->event_.gap.read_rssi_complete));
370
+ }
371
+ break;
372
+
373
+ // Security events
374
+ case ESP_GAP_BLE_AUTH_CMPL_EVT:
375
+ case ESP_GAP_BLE_SEC_REQ_EVT:
376
+ case ESP_GAP_BLE_PASSKEY_NOTIF_EVT:
377
+ case ESP_GAP_BLE_PASSKEY_REQ_EVT:
378
+ case ESP_GAP_BLE_NC_REQ_EVT:
379
+ ESP_LOGV(TAG, "gap_event_handler - %d", gap_event);
380
+ for (auto *gap_handler : this->gap_event_handlers_) {
381
+ gap_handler->gap_event_handler(
382
+ gap_event, reinterpret_cast<esp_ble_gap_cb_param_t *>(&ble_event->event_.gap.security));
383
+ }
384
+ break;
385
+
386
+ default:
387
+ // Unknown/unhandled event
388
+ ESP_LOGW(TAG, "Unhandled GAP event type in loop: %d", gap_event);
389
+ break;
346
390
  }
347
391
  break;
348
392
  }
349
393
  default:
350
394
  break;
351
395
  }
352
- // Destructor will clean up external allocations for GATTC/GATTS
353
- ble_event->~BLEEvent();
354
- EVENT_ALLOCATOR.deallocate(ble_event, 1);
396
+ // Return the event to the pool
397
+ this->ble_event_pool_.release(ble_event);
355
398
  ble_event = this->ble_events_.pop();
356
399
  }
357
400
  if (this->advertising_ != nullptr) {
@@ -359,37 +402,41 @@ void ESP32BLE::loop() {
359
402
  }
360
403
 
361
404
  // Log dropped events periodically
362
- size_t dropped = this->ble_events_.get_and_reset_dropped_count();
405
+ uint16_t dropped = this->ble_events_.get_and_reset_dropped_count();
363
406
  if (dropped > 0) {
364
- ESP_LOGW(TAG, "Dropped %zu BLE events due to buffer overflow", dropped);
407
+ ESP_LOGW(TAG, "Dropped %u BLE events due to buffer overflow", dropped);
365
408
  }
366
409
  }
367
410
 
411
+ // Helper function to load new event data based on type
412
+ void load_ble_event(BLEEvent *event, esp_gap_ble_cb_event_t e, esp_ble_gap_cb_param_t *p) {
413
+ event->load_gap_event(e, p);
414
+ }
415
+
416
+ void load_ble_event(BLEEvent *event, esp_gattc_cb_event_t e, esp_gatt_if_t i, esp_ble_gattc_cb_param_t *p) {
417
+ event->load_gattc_event(e, i, p);
418
+ }
419
+
420
+ void load_ble_event(BLEEvent *event, esp_gatts_cb_event_t e, esp_gatt_if_t i, esp_ble_gatts_cb_param_t *p) {
421
+ event->load_gatts_event(e, i, p);
422
+ }
423
+
368
424
  template<typename... Args> void enqueue_ble_event(Args... args) {
369
- // Check if queue is full before allocating
370
- if (global_ble->ble_events_.full()) {
371
- // Queue is full, drop the event
425
+ // Allocate an event from the pool
426
+ BLEEvent *event = global_ble->ble_event_pool_.allocate();
427
+ if (event == nullptr) {
428
+ // No events available - queue is full or we're out of memory
372
429
  global_ble->ble_events_.increment_dropped_count();
373
430
  return;
374
431
  }
375
432
 
376
- BLEEvent *new_event = EVENT_ALLOCATOR.allocate(1);
377
- if (new_event == nullptr) {
378
- // Memory too fragmented to allocate new event. Can only drop it until memory comes back
379
- global_ble->ble_events_.increment_dropped_count();
380
- return;
381
- }
382
- new (new_event) BLEEvent(args...);
383
-
384
- // Push the event - since we're the only producer and we checked full() above,
385
- // this should always succeed unless we have a bug
386
- if (!global_ble->ble_events_.push(new_event)) {
387
- // This should not happen in SPSC queue with single producer
388
- ESP_LOGE(TAG, "BLE queue push failed unexpectedly");
389
- new_event->~BLEEvent();
390
- EVENT_ALLOCATOR.deallocate(new_event, 1);
391
- }
392
- } // NOLINT(clang-analyzer-unix.Malloc)
433
+ // Load new event data (replaces previous event)
434
+ load_ble_event(event, args...);
435
+
436
+ // Push the event to the queue
437
+ global_ble->ble_events_.push(event);
438
+ // Push always succeeds because we're the only producer and the pool ensures we never exceed queue size
439
+ }
393
440
 
394
441
  // Explicit template instantiations for the friend function
395
442
  template void enqueue_ble_event(esp_gap_ble_cb_event_t, esp_ble_gap_cb_param_t *);
@@ -398,11 +445,26 @@ template void enqueue_ble_event(esp_gattc_cb_event_t, esp_gatt_if_t, esp_ble_gat
398
445
 
399
446
  void ESP32BLE::gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) {
400
447
  switch (event) {
401
- // Only queue the 4 GAP events we actually handle
448
+ // Queue GAP events that components need to handle
449
+ // Scanning events - used by esp32_ble_tracker
402
450
  case ESP_GAP_BLE_SCAN_RESULT_EVT:
403
451
  case ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT:
404
452
  case ESP_GAP_BLE_SCAN_START_COMPLETE_EVT:
405
453
  case ESP_GAP_BLE_SCAN_STOP_COMPLETE_EVT:
454
+ // Advertising events - used by esp32_ble_beacon and esp32_ble server
455
+ case ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT:
456
+ case ESP_GAP_BLE_SCAN_RSP_DATA_SET_COMPLETE_EVT:
457
+ case ESP_GAP_BLE_ADV_DATA_RAW_SET_COMPLETE_EVT:
458
+ case ESP_GAP_BLE_ADV_START_COMPLETE_EVT:
459
+ case ESP_GAP_BLE_ADV_STOP_COMPLETE_EVT:
460
+ // Connection events - used by ble_client
461
+ case ESP_GAP_BLE_READ_RSSI_COMPLETE_EVT:
462
+ // Security events - used by ble_client and bluetooth_proxy
463
+ case ESP_GAP_BLE_AUTH_CMPL_EVT:
464
+ case ESP_GAP_BLE_SEC_REQ_EVT:
465
+ case ESP_GAP_BLE_PASSKEY_NOTIF_EVT:
466
+ case ESP_GAP_BLE_PASSKEY_REQ_EVT:
467
+ case ESP_GAP_BLE_NC_REQ_EVT:
406
468
  enqueue_ble_event(event, param);
407
469
  return;
408
470
 
@@ -12,6 +12,7 @@
12
12
  #include "esphome/core/helpers.h"
13
13
 
14
14
  #include "ble_event.h"
15
+ #include "ble_event_pool.h"
15
16
  #include "queue.h"
16
17
 
17
18
  #ifdef USE_ESP32
@@ -148,6 +149,7 @@ class ESP32BLE : public Component {
148
149
  BLEComponentState state_{BLE_COMPONENT_STATE_OFF};
149
150
 
150
151
  LockFreeQueue<BLEEvent, MAX_BLE_QUEUE_SIZE> ble_events_;
152
+ BLEEventPool<MAX_BLE_QUEUE_SIZE> ble_event_pool_;
151
153
  BLEAdvertising *advertising_{};
152
154
  esp_ble_io_cap_t io_cap_{ESP_IO_CAP_NONE};
153
155
  uint32_t advertising_cycle_time_{};