esphome 2025.6.0b2__py3-none-any.whl → 2025.6.1__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 (30) hide show
  1. esphome/components/api/api_pb2.cpp +2 -0
  2. esphome/components/api/api_pb2.h +1 -0
  3. esphome/components/esp32_ble/ble.cpp +108 -46
  4. esphome/components/esp32_ble/ble.h +2 -0
  5. esphome/components/esp32_ble/ble_event.h +242 -75
  6. esphome/components/esp32_ble/ble_event_pool.h +72 -0
  7. esphome/components/esp32_ble/queue.h +14 -11
  8. esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp +1 -0
  9. esphome/components/esp32_ble_tracker/esp32_ble_tracker.h +4 -0
  10. esphome/components/i2s_audio/i2s_audio.cpp +1 -1
  11. esphome/components/i2s_audio/microphone/i2s_audio_microphone.cpp +37 -22
  12. esphome/components/i2s_audio/microphone/i2s_audio_microphone.h +2 -0
  13. esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp +28 -10
  14. esphome/components/i2s_audio/speaker/i2s_audio_speaker.h +1 -0
  15. esphome/components/light/light_state.h +15 -15
  16. esphome/components/nextion/nextion.cpp +2 -10
  17. esphome/components/openthread/__init__.py +5 -5
  18. esphome/components/openthread/openthread.cpp +3 -3
  19. esphome/components/openthread/tlv.py +7 -0
  20. esphome/components/spi/spi_arduino.cpp +22 -9
  21. esphome/components/switch/switch.h +13 -7
  22. esphome/config_validation.py +44 -1
  23. esphome/const.py +1 -1
  24. esphome/yaml_util.py +2 -1
  25. {esphome-2025.6.0b2.dist-info → esphome-2025.6.1.dist-info}/METADATA +1 -1
  26. {esphome-2025.6.0b2.dist-info → esphome-2025.6.1.dist-info}/RECORD +30 -29
  27. {esphome-2025.6.0b2.dist-info → esphome-2025.6.1.dist-info}/WHEEL +0 -0
  28. {esphome-2025.6.0b2.dist-info → esphome-2025.6.1.dist-info}/entry_points.txt +0 -0
  29. {esphome-2025.6.0b2.dist-info → esphome-2025.6.1.dist-info}/licenses/LICENSE +0 -0
  30. {esphome-2025.6.0b2.dist-info → esphome-2025.6.1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,72 @@
1
+ #pragma once
2
+
3
+ #ifdef USE_ESP32
4
+
5
+ #include <atomic>
6
+ #include <cstddef>
7
+ #include "ble_event.h"
8
+ #include "queue.h"
9
+ #include "esphome/core/helpers.h"
10
+
11
+ namespace esphome {
12
+ namespace esp32_ble {
13
+
14
+ // BLE Event Pool - On-demand pool of BLEEvent objects to avoid heap fragmentation
15
+ // Events are allocated on first use and reused thereafter, growing to peak usage
16
+ template<uint8_t SIZE> class BLEEventPool {
17
+ public:
18
+ BLEEventPool() : total_created_(0) {}
19
+
20
+ ~BLEEventPool() {
21
+ // Clean up any remaining events in the free list
22
+ BLEEvent *event;
23
+ while ((event = this->free_list_.pop()) != nullptr) {
24
+ delete event;
25
+ }
26
+ }
27
+
28
+ // Allocate an event from the pool
29
+ // Returns nullptr if pool is full
30
+ BLEEvent *allocate() {
31
+ // Try to get from free list first
32
+ BLEEvent *event = this->free_list_.pop();
33
+ if (event != nullptr)
34
+ return event;
35
+
36
+ // Need to create a new event
37
+ if (this->total_created_ >= SIZE) {
38
+ // Pool is at capacity
39
+ return nullptr;
40
+ }
41
+
42
+ // Use internal RAM for better performance
43
+ RAMAllocator<BLEEvent> allocator(RAMAllocator<BLEEvent>::ALLOC_INTERNAL);
44
+ event = allocator.allocate(1);
45
+
46
+ if (event == nullptr) {
47
+ // Memory allocation failed
48
+ return nullptr;
49
+ }
50
+
51
+ // Placement new to construct the object
52
+ new (event) BLEEvent();
53
+ this->total_created_++;
54
+ return event;
55
+ }
56
+
57
+ // Return an event to the pool for reuse
58
+ void release(BLEEvent *event) {
59
+ if (event != nullptr) {
60
+ this->free_list_.push(event);
61
+ }
62
+ }
63
+
64
+ private:
65
+ LockFreeQueue<BLEEvent, SIZE> free_list_; // Free events ready for reuse
66
+ uint8_t total_created_; // Total events created (high water mark)
67
+ };
68
+
69
+ } // namespace esp32_ble
70
+ } // namespace esphome
71
+
72
+ #endif
@@ -18,7 +18,7 @@
18
18
  namespace esphome {
19
19
  namespace esp32_ble {
20
20
 
21
- template<class T, size_t SIZE> class LockFreeQueue {
21
+ template<class T, uint8_t SIZE> class LockFreeQueue {
22
22
  public:
23
23
  LockFreeQueue() : head_(0), tail_(0), dropped_count_(0) {}
24
24
 
@@ -26,8 +26,8 @@ template<class T, size_t SIZE> class LockFreeQueue {
26
26
  if (element == nullptr)
27
27
  return false;
28
28
 
29
- size_t current_tail = tail_.load(std::memory_order_relaxed);
30
- size_t next_tail = (current_tail + 1) % SIZE;
29
+ uint8_t current_tail = tail_.load(std::memory_order_relaxed);
30
+ uint8_t next_tail = (current_tail + 1) % SIZE;
31
31
 
32
32
  if (next_tail == head_.load(std::memory_order_acquire)) {
33
33
  // Buffer full
@@ -41,7 +41,7 @@ template<class T, size_t SIZE> class LockFreeQueue {
41
41
  }
42
42
 
43
43
  T *pop() {
44
- size_t current_head = head_.load(std::memory_order_relaxed);
44
+ uint8_t current_head = head_.load(std::memory_order_relaxed);
45
45
 
46
46
  if (current_head == tail_.load(std::memory_order_acquire)) {
47
47
  return nullptr; // Empty
@@ -53,27 +53,30 @@ template<class T, size_t SIZE> class LockFreeQueue {
53
53
  }
54
54
 
55
55
  size_t size() const {
56
- size_t tail = tail_.load(std::memory_order_acquire);
57
- size_t head = head_.load(std::memory_order_acquire);
56
+ uint8_t tail = tail_.load(std::memory_order_acquire);
57
+ uint8_t head = head_.load(std::memory_order_acquire);
58
58
  return (tail - head + SIZE) % SIZE;
59
59
  }
60
60
 
61
- size_t get_and_reset_dropped_count() { return dropped_count_.exchange(0, std::memory_order_relaxed); }
61
+ uint16_t get_and_reset_dropped_count() { return dropped_count_.exchange(0, std::memory_order_relaxed); }
62
62
 
63
63
  void increment_dropped_count() { dropped_count_.fetch_add(1, std::memory_order_relaxed); }
64
64
 
65
65
  bool empty() const { return head_.load(std::memory_order_acquire) == tail_.load(std::memory_order_acquire); }
66
66
 
67
67
  bool full() const {
68
- size_t next_tail = (tail_.load(std::memory_order_relaxed) + 1) % SIZE;
68
+ uint8_t next_tail = (tail_.load(std::memory_order_relaxed) + 1) % SIZE;
69
69
  return next_tail == head_.load(std::memory_order_acquire);
70
70
  }
71
71
 
72
72
  protected:
73
73
  T *buffer_[SIZE];
74
- std::atomic<size_t> head_;
75
- std::atomic<size_t> tail_;
76
- std::atomic<size_t> dropped_count_;
74
+ // Atomic: written by producer (push/increment), read+reset by consumer (get_and_reset)
75
+ std::atomic<uint16_t> dropped_count_; // 65535 max - more than enough for drop tracking
76
+ // Atomic: written by consumer (pop), read by producer (push) to check if full
77
+ std::atomic<uint8_t> head_;
78
+ // Atomic: written by producer (push), read by consumer (pop) to check if empty
79
+ std::atomic<uint8_t> tail_;
77
80
  };
78
81
 
79
82
  } // namespace esp32_ble
@@ -522,6 +522,7 @@ optional<ESPBLEiBeacon> ESPBLEiBeacon::from_manufacturer_data(const ServiceData
522
522
  }
523
523
 
524
524
  void ESPBTDevice::parse_scan_rst(const BLEScanResult &scan_result) {
525
+ this->scan_result_ = &scan_result;
525
526
  for (uint8_t i = 0; i < ESP_BD_ADDR_LEN; i++)
526
527
  this->address_[i] = scan_result.bda[i];
527
528
  this->address_type_ = static_cast<esp_ble_addr_type_t>(scan_result.ble_addr_type);
@@ -85,6 +85,9 @@ class ESPBTDevice {
85
85
 
86
86
  const std::vector<ServiceData> &get_service_datas() const { return service_datas_; }
87
87
 
88
+ // Exposed through a function for use in lambdas
89
+ const BLEScanResult &get_scan_result() const { return *scan_result_; }
90
+
88
91
  bool resolve_irk(const uint8_t *irk) const;
89
92
 
90
93
  optional<ESPBLEiBeacon> get_ibeacon() const {
@@ -111,6 +114,7 @@ class ESPBTDevice {
111
114
  std::vector<ESPBTUUID> service_uuids_{};
112
115
  std::vector<ServiceData> manufacturer_datas_{};
113
116
  std::vector<ServiceData> service_datas_{};
117
+ const BLEScanResult *scan_result_{nullptr};
114
118
  };
115
119
 
116
120
  class ESP32BLETracker;
@@ -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,34 +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
330
  if (this->rx_handle_ != nullptr) {
321
331
  /* Have to stop the channel before deleting it */
322
332
  err = i2s_channel_disable(this->rx_handle_);
323
333
  if (err != ESP_OK) {
324
- ESP_LOGW(TAG, "Error stopping I2S microphone - it may not have started: %s", esp_err_to_name(err));
334
+ ESP_LOGW(TAG, "Error stopping: %s", esp_err_to_name(err));
325
335
  }
326
336
  /* If the handle is not needed any more, delete it to release the channel resources */
327
337
  err = i2s_del_channel(this->rx_handle_);
328
338
  if (err != ESP_OK) {
329
- ESP_LOGW(TAG, "Error deleting I2S channel - it may not have started: %s", esp_err_to_name(err));
339
+ ESP_LOGW(TAG, "Error deleting channel: %s", esp_err_to_name(err));
330
340
  }
331
341
  this->rx_handle_ = nullptr;
332
342
  }
333
343
  #endif
334
- this->parent_->unlock();
344
+ if (this->locked_driver_) {
345
+ this->parent_->unlock();
346
+ this->locked_driver_ = false;
347
+ }
335
348
  }
336
349
 
337
350
  void I2SAudioMicrophone::mic_task(void *params) {
@@ -403,7 +416,7 @@ size_t I2SAudioMicrophone::read_(uint8_t *buf, size_t len, TickType_t ticks_to_w
403
416
  // Ignore ESP_ERR_TIMEOUT if ticks_to_wait = 0, as it will read the data on the next call
404
417
  if (!this->status_has_warning()) {
405
418
  // Avoid spamming the logs with the error message if its repeated
406
- 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));
407
420
  }
408
421
  this->status_set_warning();
409
422
  return 0;
@@ -431,19 +444,19 @@ void I2SAudioMicrophone::loop() {
431
444
  uint32_t event_group_bits = xEventGroupGetBits(this->event_group_);
432
445
 
433
446
  if (event_group_bits & MicrophoneEventGroupBits::TASK_STARTING) {
434
- ESP_LOGD(TAG, "Task started, attempting to allocate buffer");
447
+ ESP_LOGV(TAG, "Task started, attempting to allocate buffer");
435
448
  xEventGroupClearBits(this->event_group_, MicrophoneEventGroupBits::TASK_STARTING);
436
449
  }
437
450
 
438
451
  if (event_group_bits & MicrophoneEventGroupBits::TASK_RUNNING) {
439
- ESP_LOGD(TAG, "Task is running and reading data");
452
+ ESP_LOGV(TAG, "Task is running and reading data");
440
453
 
441
454
  xEventGroupClearBits(this->event_group_, MicrophoneEventGroupBits::TASK_RUNNING);
442
455
  this->state_ = microphone::STATE_RUNNING;
443
456
  }
444
457
 
445
458
  if ((event_group_bits & MicrophoneEventGroupBits::TASK_STOPPED)) {
446
- ESP_LOGD(TAG, "Task finished, freeing resources and uninstalling I2S driver");
459
+ ESP_LOGV(TAG, "Task finished, freeing resources and uninstalling driver");
447
460
 
448
461
  vTaskDelete(this->task_handle_);
449
462
  this->task_handle_ = nullptr;
@@ -473,7 +486,8 @@ void I2SAudioMicrophone::loop() {
473
486
  }
474
487
 
475
488
  if (!this->start_driver_()) {
476
- 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);
477
491
  this->stop_driver_(); // Stop/frees whatever possibly started
478
492
  break;
479
493
  }
@@ -483,7 +497,8 @@ void I2SAudioMicrophone::loop() {
483
497
  &this->task_handle_);
484
498
 
485
499
  if (this->task_handle_ == nullptr) {
486
- 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);
487
502
  this->stop_driver_(); // Stops the driver to return the lock; will be reloaded in next attempt
488
503
  }
489
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; }
@@ -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
  };
@@ -33,6 +33,7 @@ bool Nextion::send_command_(const std::string &command) {
33
33
 
34
34
  #ifdef USE_NEXTION_COMMAND_SPACING
35
35
  if (!this->ignore_is_setup_ && !this->command_pacer_.can_send()) {
36
+ ESP_LOGN(TAG, "Command spacing: delaying command '%s'", command.c_str());
36
37
  return false;
37
38
  }
38
39
  #endif // USE_NEXTION_COMMAND_SPACING
@@ -43,10 +44,6 @@ bool Nextion::send_command_(const std::string &command) {
43
44
  const uint8_t to_send[3] = {0xFF, 0xFF, 0xFF};
44
45
  this->write_array(to_send, sizeof(to_send));
45
46
 
46
- #ifdef USE_NEXTION_COMMAND_SPACING
47
- this->command_pacer_.mark_sent();
48
- #endif // USE_NEXTION_COMMAND_SPACING
49
-
50
47
  return true;
51
48
  }
52
49
 
@@ -377,12 +374,6 @@ void Nextion::process_nextion_commands_() {
377
374
  size_t commands_processed = 0;
378
375
  #endif // USE_NEXTION_MAX_COMMANDS_PER_LOOP
379
376
 
380
- #ifdef USE_NEXTION_COMMAND_SPACING
381
- if (!this->command_pacer_.can_send()) {
382
- return; // Will try again in next loop iteration
383
- }
384
- #endif
385
-
386
377
  size_t to_process_length = 0;
387
378
  std::string to_process;
388
379
 
@@ -430,6 +421,7 @@ void Nextion::process_nextion_commands_() {
430
421
  }
431
422
  #ifdef USE_NEXTION_COMMAND_SPACING
432
423
  this->command_pacer_.mark_sent(); // Here is where we should mark the command as sent
424
+ ESP_LOGN(TAG, "Command spacing: marked command sent at %u ms", millis());
433
425
  #endif
434
426
  break;
435
427
  case 0x02: // invalid Component ID or name was used
@@ -46,7 +46,7 @@ def set_sdkconfig_options(config):
46
46
  add_idf_sdkconfig_option("CONFIG_OPENTHREAD_NETWORK_PANID", config[CONF_PAN_ID])
47
47
  add_idf_sdkconfig_option("CONFIG_OPENTHREAD_NETWORK_CHANNEL", config[CONF_CHANNEL])
48
48
  add_idf_sdkconfig_option(
49
- "CONFIG_OPENTHREAD_NETWORK_MASTERKEY", f"{config[CONF_NETWORK_KEY]:X}"
49
+ "CONFIG_OPENTHREAD_NETWORK_MASTERKEY", f"{config[CONF_NETWORK_KEY]:X}".lower()
50
50
  )
51
51
 
52
52
  if network_name := config.get(CONF_NETWORK_NAME):
@@ -54,14 +54,14 @@ def set_sdkconfig_options(config):
54
54
 
55
55
  if (ext_pan_id := config.get(CONF_EXT_PAN_ID)) is not None:
56
56
  add_idf_sdkconfig_option(
57
- "CONFIG_OPENTHREAD_NETWORK_EXTPANID", f"{ext_pan_id:X}"
57
+ "CONFIG_OPENTHREAD_NETWORK_EXTPANID", f"{ext_pan_id:X}".lower()
58
58
  )
59
59
  if (mesh_local_prefix := config.get(CONF_MESH_LOCAL_PREFIX)) is not None:
60
60
  add_idf_sdkconfig_option(
61
- "CONFIG_OPENTHREAD_MESH_LOCAL_PREFIX", f"{mesh_local_prefix:X}"
61
+ "CONFIG_OPENTHREAD_MESH_LOCAL_PREFIX", f"{mesh_local_prefix}".lower()
62
62
  )
63
63
  if (pskc := config.get(CONF_PSKC)) is not None:
64
- add_idf_sdkconfig_option("CONFIG_OPENTHREAD_NETWORK_PSKC", f"{pskc:X}")
64
+ add_idf_sdkconfig_option("CONFIG_OPENTHREAD_NETWORK_PSKC", f"{pskc:X}".lower())
65
65
 
66
66
  if CONF_FORCE_DATASET in config:
67
67
  if config[CONF_FORCE_DATASET]:
@@ -98,7 +98,7 @@ _CONNECTION_SCHEMA = cv.Schema(
98
98
  cv.Optional(CONF_EXT_PAN_ID): cv.hex_int,
99
99
  cv.Optional(CONF_NETWORK_NAME): cv.string_strict,
100
100
  cv.Optional(CONF_PSKC): cv.hex_int,
101
- cv.Optional(CONF_MESH_LOCAL_PREFIX): cv.hex_int,
101
+ cv.Optional(CONF_MESH_LOCAL_PREFIX): cv.ipv6network,
102
102
  }
103
103
  )
104
104
 
@@ -137,7 +137,7 @@ void OpenThreadSrpComponent::setup() {
137
137
  // Copy the mdns services to our local instance so that the c_str pointers remain valid for the lifetime of this
138
138
  // component
139
139
  this->mdns_services_ = this->mdns_->get_services();
140
- ESP_LOGW(TAG, "Setting up SRP services. count = %d\n", this->mdns_services_.size());
140
+ ESP_LOGD(TAG, "Setting up SRP services. count = %d\n", this->mdns_services_.size());
141
141
  for (const auto &service : this->mdns_services_) {
142
142
  otSrpClientBuffersServiceEntry *entry = otSrpClientBuffersAllocateService(instance);
143
143
  if (!entry) {
@@ -185,11 +185,11 @@ void OpenThreadSrpComponent::setup() {
185
185
  if (error != OT_ERROR_NONE) {
186
186
  ESP_LOGW(TAG, "Failed to add service: %s", otThreadErrorToString(error));
187
187
  }
188
- ESP_LOGW(TAG, "Added service: %s", full_service.c_str());
188
+ ESP_LOGD(TAG, "Added service: %s", full_service.c_str());
189
189
  }
190
190
 
191
191
  otSrpClientEnableAutoStartMode(instance, srp_start_callback, nullptr);
192
- ESP_LOGW(TAG, "Finished SRP setup");
192
+ ESP_LOGD(TAG, "Finished SRP setup");
193
193
  }
194
194
 
195
195
  void *OpenThreadSrpComponent::pool_alloc_(size_t size) {
@@ -1,5 +1,6 @@
1
1
  # Sourced from https://gist.github.com/agners/0338576e0003318b63ec1ea75adc90f9
2
2
  import binascii
3
+ import ipaddress
3
4
 
4
5
  from esphome.const import CONF_CHANNEL
5
6
 
@@ -37,6 +38,12 @@ def parse_tlv(tlv) -> dict:
37
38
  if tag in TLV_TYPES:
38
39
  if tag == 3:
39
40
  output[TLV_TYPES[tag]] = val.decode("utf-8")
41
+ elif tag == 7:
42
+ mesh_local_prefix = binascii.hexlify(val).decode("utf-8")
43
+ mesh_local_prefix_str = f"{mesh_local_prefix}0000000000000000"
44
+ ipv6_bytes = bytes.fromhex(mesh_local_prefix_str)
45
+ ipv6_address = ipaddress.IPv6Address(ipv6_bytes)
46
+ output[TLV_TYPES[tag]] = f"{ipv6_address}/64"
40
47
  else:
41
48
  output[TLV_TYPES[tag]] = int.from_bytes(val)
42
49
  return output