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.
- esphome/components/api/api_pb2.cpp +2 -0
- esphome/components/api/api_pb2.h +1 -0
- esphome/components/esp32_ble/ble.cpp +108 -46
- esphome/components/esp32_ble/ble.h +2 -0
- esphome/components/esp32_ble/ble_event.h +242 -75
- esphome/components/esp32_ble/ble_event_pool.h +72 -0
- esphome/components/esp32_ble/queue.h +14 -11
- esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp +1 -0
- esphome/components/esp32_ble_tracker/esp32_ble_tracker.h +4 -0
- esphome/components/i2s_audio/i2s_audio.cpp +1 -1
- esphome/components/i2s_audio/microphone/i2s_audio_microphone.cpp +37 -22
- esphome/components/i2s_audio/microphone/i2s_audio_microphone.h +2 -0
- esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp +28 -10
- esphome/components/i2s_audio/speaker/i2s_audio_speaker.h +1 -0
- esphome/components/light/light_state.h +15 -15
- esphome/components/nextion/nextion.cpp +2 -10
- esphome/components/openthread/__init__.py +5 -5
- esphome/components/openthread/openthread.cpp +3 -3
- esphome/components/openthread/tlv.py +7 -0
- esphome/components/spi/spi_arduino.cpp +22 -9
- esphome/components/switch/switch.h +13 -7
- esphome/config_validation.py +44 -1
- esphome/const.py +1 -1
- esphome/yaml_util.py +2 -1
- {esphome-2025.6.0b2.dist-info → esphome-2025.6.1.dist-info}/METADATA +1 -1
- {esphome-2025.6.0b2.dist-info → esphome-2025.6.1.dist-info}/RECORD +30 -29
- {esphome-2025.6.0b2.dist-info → esphome-2025.6.1.dist-info}/WHEEL +0 -0
- {esphome-2025.6.0b2.dist-info → esphome-2025.6.1.dist-info}/entry_points.txt +0 -0
- {esphome-2025.6.0b2.dist-info → esphome-2025.6.1.dist-info}/licenses/LICENSE +0 -0
- {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,
|
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
|
-
|
30
|
-
|
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
|
-
|
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
|
-
|
57
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
75
|
-
std::atomic<
|
76
|
-
|
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;
|
@@ -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, "
|
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, "
|
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
|
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
|
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
|
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
|
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
|
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, "
|
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
|
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
|
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
|
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
|
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
|
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->
|
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, "
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
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
|
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
|
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
|
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
|
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, "
|
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
|
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, "
|
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
|
-
///
|
220
|
-
|
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
|
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.
|
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
|
-
|
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
|
-
|
188
|
+
ESP_LOGD(TAG, "Added service: %s", full_service.c_str());
|
189
189
|
}
|
190
190
|
|
191
191
|
otSrpClientEnableAutoStartMode(instance, srp_start_callback, nullptr);
|
192
|
-
|
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
|