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.
- esphome/components/api/api_connection.cpp +53 -33
- esphome/components/api/api_connection.h +24 -25
- esphome/components/api/api_pb2.cpp +1 -0
- esphome/components/api/api_pb2.h +65 -226
- esphome/components/api/client.py +1 -3
- esphome/components/api/proto.h +1 -1
- esphome/components/bluetooth_proxy/bluetooth_proxy.cpp +2 -2
- esphome/components/bluetooth_proxy/bluetooth_proxy.h +1 -1
- esphome/components/bme280_base/bme280_base.cpp +2 -3
- esphome/components/datetime/date_entity.cpp +5 -5
- esphome/components/datetime/datetime_base.h +0 -5
- esphome/components/datetime/datetime_entity.cpp +8 -8
- esphome/components/datetime/time_entity.cpp +4 -4
- esphome/components/esp32/__init__.py +30 -3
- esphome/components/esp32_ble/ble.cpp +90 -45
- esphome/components/esp32_ble/ble.h +24 -5
- esphome/components/esp32_ble/ble_event.h +172 -32
- esphome/components/esp32_ble/ble_scan_result.h +24 -0
- esphome/components/esp32_ble/queue.h +53 -27
- esphome/components/esp32_ble_tracker/__init__.py +1 -0
- esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp +95 -66
- esphome/components/esp32_ble_tracker/esp32_ble_tracker.h +16 -16
- esphome/components/esp32_camera/esp32_camera.cpp +1 -1
- esphome/components/fan/fan.cpp +26 -17
- esphome/components/i2s_audio/i2s_audio.cpp +1 -1
- esphome/components/i2s_audio/microphone/i2s_audio_microphone.cpp +47 -29
- 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/kmeteriso/kmeteriso.cpp +2 -3
- esphome/components/light/light_state.h +15 -15
- esphome/components/logger/logger.cpp +2 -15
- esphome/components/logger/logger.h +1 -2
- esphome/components/mqtt/mqtt_component.cpp +1 -1
- esphome/components/nextion/binary_sensor/nextion_binarysensor.cpp +1 -1
- esphome/components/nextion/nextion_upload_arduino.cpp +12 -9
- esphome/components/nextion/nextion_upload_idf.cpp +11 -9
- esphome/components/nextion/sensor/nextion_sensor.cpp +1 -1
- esphome/components/nextion/text_sensor/nextion_textsensor.cpp +1 -1
- esphome/components/number/number.cpp +1 -1
- esphome/components/number/number.h +0 -4
- esphome/components/prometheus/__init__.py +0 -1
- esphome/components/select/select.cpp +1 -1
- esphome/components/select/select.h +0 -4
- esphome/components/sensor/sensor.cpp +8 -4
- esphome/components/sensor/sensor.h +3 -6
- esphome/components/spi/spi_arduino.cpp +22 -9
- esphome/components/status_led/light/status_led_light.cpp +2 -2
- esphome/components/status_led/light/status_led_light.h +1 -1
- esphome/components/switch/switch.h +13 -7
- esphome/components/template/alarm_control_panel/template_alarm_control_panel.cpp +14 -9
- esphome/components/template/alarm_control_panel/template_alarm_control_panel.h +1 -0
- esphome/components/text/text.cpp +1 -1
- esphome/components/text/text.h +0 -4
- esphome/components/text_sensor/text_sensor.cpp +8 -4
- esphome/components/text_sensor/text_sensor.h +6 -6
- esphome/components/update/update_entity.cpp +1 -1
- esphome/components/update/update_entity.h +0 -3
- esphome/components/uptime/sensor/uptime_timestamp_sensor.cpp +1 -1
- esphome/components/web_server_idf/__init__.py +0 -2
- esphome/components/web_server_idf/web_server_idf.cpp +6 -2
- esphome/components/web_server_idf/web_server_idf.h +7 -0
- esphome/components/weikai/weikai.cpp +1 -1
- esphome/const.py +1 -1
- esphome/core/application.cpp +14 -8
- esphome/core/application.h +7 -7
- esphome/core/component.cpp +36 -15
- esphome/core/component.h +30 -13
- esphome/core/entity_base.cpp +4 -16
- esphome/core/entity_base.h +27 -13
- esphome/core/helpers.h +1 -1
- esphome/wizard.py +0 -16
- {esphome-2025.6.0b1.dist-info → esphome-2025.6.0b3.dist-info}/METADATA +2 -2
- {esphome-2025.6.0b1.dist-info → esphome-2025.6.0b3.dist-info}/RECORD +78 -77
- {esphome-2025.6.0b1.dist-info → esphome-2025.6.0b3.dist-info}/WHEEL +0 -0
- {esphome-2025.6.0b1.dist-info → esphome-2025.6.0b3.dist-info}/entry_points.txt +0 -0
- {esphome-2025.6.0b1.dist-info → esphome-2025.6.0b3.dist-info}/licenses/LICENSE +0 -0
- {esphome-2025.6.0b1.dist-info → esphome-2025.6.0b3.dist-info}/top_level.txt +0 -0
esphome/components/api/client.py
CHANGED
@@ -46,12 +46,10 @@ async def async_run_logs(config: dict[str, Any], address: str) -> None:
|
|
46
46
|
time_ = datetime.now()
|
47
47
|
message: bytes = msg.message
|
48
48
|
text = message.decode("utf8", "backslashreplace")
|
49
|
-
if dashboard:
|
50
|
-
text = text.replace("\033", "\\033")
|
51
49
|
for parsed_msg in parse_log_message(
|
52
50
|
text, f"[{time_.hour:02}:{time_.minute:02}:{time_.second:02}]"
|
53
51
|
):
|
54
|
-
print(parsed_msg)
|
52
|
+
print(parsed_msg.replace("\033", "\\033") if dashboard else parsed_msg)
|
55
53
|
|
56
54
|
stop = await async_run(cli, on_log, name=name)
|
57
55
|
try:
|
esphome/components/api/proto.h
CHANGED
@@ -216,7 +216,7 @@ class ProtoWriteBuffer {
|
|
216
216
|
this->buffer_->insert(this->buffer_->end(), data, data + len);
|
217
217
|
}
|
218
218
|
void encode_string(uint32_t field_id, const std::string &value, bool force = false) {
|
219
|
-
this->encode_string(field_id, value.data(), value.size());
|
219
|
+
this->encode_string(field_id, value.data(), value.size(), force);
|
220
220
|
}
|
221
221
|
void encode_bytes(uint32_t field_id, const uint8_t *data, size_t len, bool force = false) {
|
222
222
|
this->encode_string(field_id, reinterpret_cast<const char *>(data), len, force);
|
@@ -58,7 +58,7 @@ static std::vector<api::BluetoothLERawAdvertisement> &get_batch_buffer() {
|
|
58
58
|
return batch_buffer;
|
59
59
|
}
|
60
60
|
|
61
|
-
bool BluetoothProxy::parse_devices(
|
61
|
+
bool BluetoothProxy::parse_devices(const esp32_ble::BLEScanResult *scan_results, size_t count) {
|
62
62
|
if (!api::global_api_server->is_connected() || this->api_connection_ == nullptr || !this->raw_advertisements_)
|
63
63
|
return false;
|
64
64
|
|
@@ -73,7 +73,7 @@ bool BluetoothProxy::parse_devices(esp_ble_gap_cb_param_t::ble_scan_result_evt_p
|
|
73
73
|
|
74
74
|
// Add new advertisements to the batch buffer
|
75
75
|
for (size_t i = 0; i < count; i++) {
|
76
|
-
auto &result =
|
76
|
+
auto &result = scan_results[i];
|
77
77
|
uint8_t length = result.adv_data_len + result.scan_rsp_len;
|
78
78
|
|
79
79
|
batch_buffer.emplace_back();
|
@@ -52,7 +52,7 @@ class BluetoothProxy : public esp32_ble_tracker::ESPBTDeviceListener, public Com
|
|
52
52
|
public:
|
53
53
|
BluetoothProxy();
|
54
54
|
bool parse_device(const esp32_ble_tracker::ESPBTDevice &device) override;
|
55
|
-
bool parse_devices(
|
55
|
+
bool parse_devices(const esp32_ble::BLEScanResult *scan_results, size_t count) override;
|
56
56
|
void dump_config() override;
|
57
57
|
void setup() override;
|
58
58
|
void loop() override;
|
@@ -93,9 +93,8 @@ void BME280Component::setup() {
|
|
93
93
|
|
94
94
|
// Mark as not failed before initializing. Some devices will turn off sensors to save on batteries
|
95
95
|
// and when they come back on, the COMPONENT_STATE_FAILED bit must be unset on the component.
|
96
|
-
if (
|
97
|
-
this->
|
98
|
-
this->component_state_ |= COMPONENT_STATE_CONSTRUCTION;
|
96
|
+
if (this->is_failed()) {
|
97
|
+
this->reset_to_construction_state();
|
99
98
|
}
|
100
99
|
|
101
100
|
if (!this->read_byte(BME280_REGISTER_CHIPID, &chip_id)) {
|
@@ -11,25 +11,25 @@ static const char *const TAG = "datetime.date_entity";
|
|
11
11
|
|
12
12
|
void DateEntity::publish_state() {
|
13
13
|
if (this->year_ == 0 || this->month_ == 0 || this->day_ == 0) {
|
14
|
-
this->
|
14
|
+
this->set_has_state(false);
|
15
15
|
return;
|
16
16
|
}
|
17
17
|
if (this->year_ < 1970 || this->year_ > 3000) {
|
18
|
-
this->
|
18
|
+
this->set_has_state(false);
|
19
19
|
ESP_LOGE(TAG, "Year must be between 1970 and 3000");
|
20
20
|
return;
|
21
21
|
}
|
22
22
|
if (this->month_ < 1 || this->month_ > 12) {
|
23
|
-
this->
|
23
|
+
this->set_has_state(false);
|
24
24
|
ESP_LOGE(TAG, "Month must be between 1 and 12");
|
25
25
|
return;
|
26
26
|
}
|
27
27
|
if (this->day_ > days_in_month(this->month_, this->year_)) {
|
28
|
-
this->
|
28
|
+
this->set_has_state(false);
|
29
29
|
ESP_LOGE(TAG, "Day must be between 1 and %d for month %d", days_in_month(this->month_, this->year_), this->month_);
|
30
30
|
return;
|
31
31
|
}
|
32
|
-
this->
|
32
|
+
this->set_has_state(true);
|
33
33
|
ESP_LOGD(TAG, "'%s': Sending date %d-%d-%d", this->get_name().c_str(), this->year_, this->month_, this->day_);
|
34
34
|
this->state_callback_.call();
|
35
35
|
}
|
@@ -13,9 +13,6 @@ namespace datetime {
|
|
13
13
|
|
14
14
|
class DateTimeBase : public EntityBase {
|
15
15
|
public:
|
16
|
-
/// Return whether this Datetime has gotten a full state yet.
|
17
|
-
bool has_state() const { return this->has_state_; }
|
18
|
-
|
19
16
|
virtual ESPTime state_as_esptime() const = 0;
|
20
17
|
|
21
18
|
void add_on_state_callback(std::function<void()> &&callback) { this->state_callback_.add(std::move(callback)); }
|
@@ -31,8 +28,6 @@ class DateTimeBase : public EntityBase {
|
|
31
28
|
#ifdef USE_TIME
|
32
29
|
time::RealTimeClock *rtc_;
|
33
30
|
#endif
|
34
|
-
|
35
|
-
bool has_state_{false};
|
36
31
|
};
|
37
32
|
|
38
33
|
#ifdef USE_TIME
|
@@ -11,40 +11,40 @@ static const char *const TAG = "datetime.datetime_entity";
|
|
11
11
|
|
12
12
|
void DateTimeEntity::publish_state() {
|
13
13
|
if (this->year_ == 0 || this->month_ == 0 || this->day_ == 0) {
|
14
|
-
this->
|
14
|
+
this->set_has_state(false);
|
15
15
|
return;
|
16
16
|
}
|
17
17
|
if (this->year_ < 1970 || this->year_ > 3000) {
|
18
|
-
this->
|
18
|
+
this->set_has_state(false);
|
19
19
|
ESP_LOGE(TAG, "Year must be between 1970 and 3000");
|
20
20
|
return;
|
21
21
|
}
|
22
22
|
if (this->month_ < 1 || this->month_ > 12) {
|
23
|
-
this->
|
23
|
+
this->set_has_state(false);
|
24
24
|
ESP_LOGE(TAG, "Month must be between 1 and 12");
|
25
25
|
return;
|
26
26
|
}
|
27
27
|
if (this->day_ > days_in_month(this->month_, this->year_)) {
|
28
|
-
this->
|
28
|
+
this->set_has_state(false);
|
29
29
|
ESP_LOGE(TAG, "Day must be between 1 and %d for month %d", days_in_month(this->month_, this->year_), this->month_);
|
30
30
|
return;
|
31
31
|
}
|
32
32
|
if (this->hour_ > 23) {
|
33
|
-
this->
|
33
|
+
this->set_has_state(false);
|
34
34
|
ESP_LOGE(TAG, "Hour must be between 0 and 23");
|
35
35
|
return;
|
36
36
|
}
|
37
37
|
if (this->minute_ > 59) {
|
38
|
-
this->
|
38
|
+
this->set_has_state(false);
|
39
39
|
ESP_LOGE(TAG, "Minute must be between 0 and 59");
|
40
40
|
return;
|
41
41
|
}
|
42
42
|
if (this->second_ > 59) {
|
43
|
-
this->
|
43
|
+
this->set_has_state(false);
|
44
44
|
ESP_LOGE(TAG, "Second must be between 0 and 59");
|
45
45
|
return;
|
46
46
|
}
|
47
|
-
this->
|
47
|
+
this->set_has_state(true);
|
48
48
|
ESP_LOGD(TAG, "'%s': Sending datetime %04u-%02u-%02u %02d:%02d:%02d", this->get_name().c_str(), this->year_,
|
49
49
|
this->month_, this->day_, this->hour_, this->minute_, this->second_);
|
50
50
|
this->state_callback_.call();
|
@@ -11,21 +11,21 @@ static const char *const TAG = "datetime.time_entity";
|
|
11
11
|
|
12
12
|
void TimeEntity::publish_state() {
|
13
13
|
if (this->hour_ > 23) {
|
14
|
-
this->
|
14
|
+
this->set_has_state(false);
|
15
15
|
ESP_LOGE(TAG, "Hour must be between 0 and 23");
|
16
16
|
return;
|
17
17
|
}
|
18
18
|
if (this->minute_ > 59) {
|
19
|
-
this->
|
19
|
+
this->set_has_state(false);
|
20
20
|
ESP_LOGE(TAG, "Minute must be between 0 and 59");
|
21
21
|
return;
|
22
22
|
}
|
23
23
|
if (this->second_ > 59) {
|
24
|
-
this->
|
24
|
+
this->set_has_state(false);
|
25
25
|
ESP_LOGE(TAG, "Second must be between 0 and 59");
|
26
26
|
return;
|
27
27
|
}
|
28
|
-
this->
|
28
|
+
this->set_has_state(true);
|
29
29
|
ESP_LOGD(TAG, "'%s': Sending time %02d:%02d:%02d", this->get_name().c_str(), this->hour_, this->minute_,
|
30
30
|
this->second_);
|
31
31
|
this->state_callback_.call();
|
@@ -94,6 +94,13 @@ COMPILER_OPTIMIZATIONS = {
|
|
94
94
|
"SIZE": "CONFIG_COMPILER_OPTIMIZATION_SIZE",
|
95
95
|
}
|
96
96
|
|
97
|
+
ARDUINO_ALLOWED_VARIANTS = [
|
98
|
+
VARIANT_ESP32,
|
99
|
+
VARIANT_ESP32C3,
|
100
|
+
VARIANT_ESP32S2,
|
101
|
+
VARIANT_ESP32S3,
|
102
|
+
]
|
103
|
+
|
97
104
|
|
98
105
|
def get_cpu_frequencies(*frequencies):
|
99
106
|
return [str(x) + "MHZ" for x in frequencies]
|
@@ -143,12 +150,17 @@ def set_core_data(config):
|
|
143
150
|
CORE.data[KEY_ESP32][KEY_COMPONENTS] = {}
|
144
151
|
elif conf[CONF_TYPE] == FRAMEWORK_ARDUINO:
|
145
152
|
CORE.data[KEY_CORE][KEY_TARGET_FRAMEWORK] = "arduino"
|
153
|
+
if variant not in ARDUINO_ALLOWED_VARIANTS:
|
154
|
+
raise cv.Invalid(
|
155
|
+
f"ESPHome does not support using the Arduino framework for the {variant}. Please use the ESP-IDF framework instead.",
|
156
|
+
path=[CONF_FRAMEWORK, CONF_TYPE],
|
157
|
+
)
|
146
158
|
CORE.data[KEY_CORE][KEY_FRAMEWORK_VERSION] = cv.Version.parse(
|
147
159
|
config[CONF_FRAMEWORK][CONF_VERSION]
|
148
160
|
)
|
149
161
|
|
150
162
|
CORE.data[KEY_ESP32][KEY_BOARD] = config[CONF_BOARD]
|
151
|
-
CORE.data[KEY_ESP32][KEY_VARIANT] =
|
163
|
+
CORE.data[KEY_ESP32][KEY_VARIANT] = variant
|
152
164
|
CORE.data[KEY_ESP32][KEY_EXTRA_BUILD_FILES] = {}
|
153
165
|
|
154
166
|
return config
|
@@ -618,6 +630,21 @@ ESP_IDF_FRAMEWORK_SCHEMA = cv.All(
|
|
618
630
|
)
|
619
631
|
|
620
632
|
|
633
|
+
def _set_default_framework(config):
|
634
|
+
if CONF_FRAMEWORK not in config:
|
635
|
+
config = config.copy()
|
636
|
+
|
637
|
+
variant = config[CONF_VARIANT]
|
638
|
+
if variant in ARDUINO_ALLOWED_VARIANTS:
|
639
|
+
config[CONF_FRAMEWORK] = ARDUINO_FRAMEWORK_SCHEMA({})
|
640
|
+
config[CONF_FRAMEWORK][CONF_TYPE] = FRAMEWORK_ARDUINO
|
641
|
+
else:
|
642
|
+
config[CONF_FRAMEWORK] = ESP_IDF_FRAMEWORK_SCHEMA({})
|
643
|
+
config[CONF_FRAMEWORK][CONF_TYPE] = FRAMEWORK_ESP_IDF
|
644
|
+
|
645
|
+
return config
|
646
|
+
|
647
|
+
|
621
648
|
FRAMEWORK_ESP_IDF = "esp-idf"
|
622
649
|
FRAMEWORK_ARDUINO = "arduino"
|
623
650
|
FRAMEWORK_SCHEMA = cv.typed_schema(
|
@@ -627,7 +654,6 @@ FRAMEWORK_SCHEMA = cv.typed_schema(
|
|
627
654
|
},
|
628
655
|
lower=True,
|
629
656
|
space="-",
|
630
|
-
default_type=FRAMEWORK_ARDUINO,
|
631
657
|
)
|
632
658
|
|
633
659
|
|
@@ -654,10 +680,11 @@ CONFIG_SCHEMA = cv.All(
|
|
654
680
|
),
|
655
681
|
cv.Optional(CONF_PARTITIONS): cv.file_,
|
656
682
|
cv.Optional(CONF_VARIANT): cv.one_of(*VARIANTS, upper=True),
|
657
|
-
cv.Optional(CONF_FRAMEWORK
|
683
|
+
cv.Optional(CONF_FRAMEWORK): FRAMEWORK_SCHEMA,
|
658
684
|
}
|
659
685
|
),
|
660
686
|
_detect_variant,
|
687
|
+
_set_default_framework,
|
661
688
|
set_core_data,
|
662
689
|
)
|
663
690
|
|
@@ -304,20 +304,52 @@ void ESP32BLE::loop() {
|
|
304
304
|
BLEEvent *ble_event = this->ble_events_.pop();
|
305
305
|
while (ble_event != nullptr) {
|
306
306
|
switch (ble_event->type_) {
|
307
|
-
case BLEEvent::GATTS:
|
308
|
-
|
309
|
-
|
307
|
+
case BLEEvent::GATTS: {
|
308
|
+
esp_gatts_cb_event_t event = ble_event->event_.gatts.gatts_event;
|
309
|
+
esp_gatt_if_t gatts_if = ble_event->event_.gatts.gatts_if;
|
310
|
+
esp_ble_gatts_cb_param_t *param = ble_event->event_.gatts.gatts_param;
|
311
|
+
ESP_LOGV(TAG, "gatts_event [esp_gatt_if: %d] - %d", gatts_if, event);
|
312
|
+
for (auto *gatts_handler : this->gatts_event_handlers_) {
|
313
|
+
gatts_handler->gatts_event_handler(event, gatts_if, param);
|
314
|
+
}
|
310
315
|
break;
|
311
|
-
|
312
|
-
|
313
|
-
|
316
|
+
}
|
317
|
+
case BLEEvent::GATTC: {
|
318
|
+
esp_gattc_cb_event_t event = ble_event->event_.gattc.gattc_event;
|
319
|
+
esp_gatt_if_t gattc_if = ble_event->event_.gattc.gattc_if;
|
320
|
+
esp_ble_gattc_cb_param_t *param = ble_event->event_.gattc.gattc_param;
|
321
|
+
ESP_LOGV(TAG, "gattc_event [esp_gatt_if: %d] - %d", gattc_if, event);
|
322
|
+
for (auto *gattc_handler : this->gattc_event_handlers_) {
|
323
|
+
gattc_handler->gattc_event_handler(event, gattc_if, param);
|
324
|
+
}
|
314
325
|
break;
|
315
|
-
|
316
|
-
|
326
|
+
}
|
327
|
+
case BLEEvent::GAP: {
|
328
|
+
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
|
+
}
|
346
|
+
}
|
317
347
|
break;
|
348
|
+
}
|
318
349
|
default:
|
319
350
|
break;
|
320
351
|
}
|
352
|
+
// Destructor will clean up external allocations for GATTC/GATTS
|
321
353
|
ble_event->~BLEEvent();
|
322
354
|
EVENT_ALLOCATOR.deallocate(ble_event, 1);
|
323
355
|
ble_event = this->ble_events_.pop();
|
@@ -325,61 +357,74 @@ void ESP32BLE::loop() {
|
|
325
357
|
if (this->advertising_ != nullptr) {
|
326
358
|
this->advertising_->loop();
|
327
359
|
}
|
360
|
+
|
361
|
+
// Log dropped events periodically
|
362
|
+
size_t dropped = this->ble_events_.get_and_reset_dropped_count();
|
363
|
+
if (dropped > 0) {
|
364
|
+
ESP_LOGW(TAG, "Dropped %zu BLE events due to buffer overflow", dropped);
|
365
|
+
}
|
328
366
|
}
|
329
367
|
|
330
|
-
void
|
368
|
+
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
|
372
|
+
global_ble->ble_events_.increment_dropped_count();
|
373
|
+
return;
|
374
|
+
}
|
375
|
+
|
331
376
|
BLEEvent *new_event = EVENT_ALLOCATOR.allocate(1);
|
332
377
|
if (new_event == nullptr) {
|
333
378
|
// Memory too fragmented to allocate new event. Can only drop it until memory comes back
|
379
|
+
global_ble->ble_events_.increment_dropped_count();
|
334
380
|
return;
|
335
381
|
}
|
336
|
-
new (new_event) BLEEvent(
|
337
|
-
|
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
|
+
}
|
338
392
|
} // NOLINT(clang-analyzer-unix.Malloc)
|
339
393
|
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
394
|
+
// Explicit template instantiations for the friend function
|
395
|
+
template void enqueue_ble_event(esp_gap_ble_cb_event_t, esp_ble_gap_cb_param_t *);
|
396
|
+
template void enqueue_ble_event(esp_gatts_cb_event_t, esp_gatt_if_t, esp_ble_gatts_cb_param_t *);
|
397
|
+
template void enqueue_ble_event(esp_gattc_cb_event_t, esp_gatt_if_t, esp_ble_gattc_cb_param_t *);
|
398
|
+
|
399
|
+
void ESP32BLE::gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) {
|
400
|
+
switch (event) {
|
401
|
+
// Only queue the 4 GAP events we actually handle
|
402
|
+
case ESP_GAP_BLE_SCAN_RESULT_EVT:
|
403
|
+
case ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT:
|
404
|
+
case ESP_GAP_BLE_SCAN_START_COMPLETE_EVT:
|
405
|
+
case ESP_GAP_BLE_SCAN_STOP_COMPLETE_EVT:
|
406
|
+
enqueue_ble_event(event, param);
|
407
|
+
return;
|
408
|
+
|
409
|
+
// Ignore these GAP events as they are not relevant for our use case
|
410
|
+
case ESP_GAP_BLE_UPDATE_CONN_PARAMS_EVT:
|
411
|
+
case ESP_GAP_BLE_SET_PKT_LENGTH_COMPLETE_EVT:
|
412
|
+
return;
|
413
|
+
|
414
|
+
default:
|
415
|
+
break;
|
344
416
|
}
|
417
|
+
ESP_LOGW(TAG, "Ignoring unexpected GAP event type: %d", event);
|
345
418
|
}
|
346
419
|
|
347
420
|
void ESP32BLE::gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if,
|
348
421
|
esp_ble_gatts_cb_param_t *param) {
|
349
|
-
|
350
|
-
if (new_event == nullptr) {
|
351
|
-
// Memory too fragmented to allocate new event. Can only drop it until memory comes back
|
352
|
-
return;
|
353
|
-
}
|
354
|
-
new (new_event) BLEEvent(event, gatts_if, param);
|
355
|
-
global_ble->ble_events_.push(new_event);
|
356
|
-
} // NOLINT(clang-analyzer-unix.Malloc)
|
357
|
-
|
358
|
-
void ESP32BLE::real_gatts_event_handler_(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if,
|
359
|
-
esp_ble_gatts_cb_param_t *param) {
|
360
|
-
ESP_LOGV(TAG, "(BLE) gatts_event [esp_gatt_if: %d] - %d", gatts_if, event);
|
361
|
-
for (auto *gatts_handler : this->gatts_event_handlers_) {
|
362
|
-
gatts_handler->gatts_event_handler(event, gatts_if, param);
|
363
|
-
}
|
422
|
+
enqueue_ble_event(event, gatts_if, param);
|
364
423
|
}
|
365
424
|
|
366
425
|
void ESP32BLE::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
|
367
426
|
esp_ble_gattc_cb_param_t *param) {
|
368
|
-
|
369
|
-
if (new_event == nullptr) {
|
370
|
-
// Memory too fragmented to allocate new event. Can only drop it until memory comes back
|
371
|
-
return;
|
372
|
-
}
|
373
|
-
new (new_event) BLEEvent(event, gattc_if, param);
|
374
|
-
global_ble->ble_events_.push(new_event);
|
375
|
-
} // NOLINT(clang-analyzer-unix.Malloc)
|
376
|
-
|
377
|
-
void ESP32BLE::real_gattc_event_handler_(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
|
378
|
-
esp_ble_gattc_cb_param_t *param) {
|
379
|
-
ESP_LOGV(TAG, "(BLE) gattc_event [esp_gatt_if: %d] - %d", gattc_if, event);
|
380
|
-
for (auto *gattc_handler : this->gattc_event_handlers_) {
|
381
|
-
gattc_handler->gattc_event_handler(event, gattc_if, param);
|
382
|
-
}
|
427
|
+
enqueue_ble_event(event, gattc_if, param);
|
383
428
|
}
|
384
429
|
|
385
430
|
float ESP32BLE::get_setup_priority() const { return setup_priority::BLUETOOTH; }
|
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
#include "ble_advertising.h"
|
4
4
|
#include "ble_uuid.h"
|
5
|
+
#include "ble_scan_result.h"
|
5
6
|
|
6
7
|
#include <functional>
|
7
8
|
|
@@ -22,6 +23,16 @@
|
|
22
23
|
namespace esphome {
|
23
24
|
namespace esp32_ble {
|
24
25
|
|
26
|
+
// Maximum number of BLE scan results to buffer
|
27
|
+
#ifdef USE_PSRAM
|
28
|
+
static constexpr uint8_t SCAN_RESULT_BUFFER_SIZE = 32;
|
29
|
+
#else
|
30
|
+
static constexpr uint8_t SCAN_RESULT_BUFFER_SIZE = 20;
|
31
|
+
#endif
|
32
|
+
|
33
|
+
// Maximum size of the BLE event queue - must be power of 2 for lock-free queue
|
34
|
+
static constexpr size_t MAX_BLE_QUEUE_SIZE = 64;
|
35
|
+
|
25
36
|
uint64_t ble_addr_to_uint64(const esp_bd_addr_t address);
|
26
37
|
|
27
38
|
// NOLINTNEXTLINE(modernize-use-using)
|
@@ -57,6 +68,11 @@ class GAPEventHandler {
|
|
57
68
|
virtual void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) = 0;
|
58
69
|
};
|
59
70
|
|
71
|
+
class GAPScanEventHandler {
|
72
|
+
public:
|
73
|
+
virtual void gap_scan_event_handler(const BLEScanResult &scan_result) = 0;
|
74
|
+
};
|
75
|
+
|
60
76
|
class GATTcEventHandler {
|
61
77
|
public:
|
62
78
|
virtual void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
|
@@ -101,6 +117,9 @@ class ESP32BLE : public Component {
|
|
101
117
|
void advertising_register_raw_advertisement_callback(std::function<void(bool)> &&callback);
|
102
118
|
|
103
119
|
void register_gap_event_handler(GAPEventHandler *handler) { this->gap_event_handlers_.push_back(handler); }
|
120
|
+
void register_gap_scan_event_handler(GAPScanEventHandler *handler) {
|
121
|
+
this->gap_scan_event_handlers_.push_back(handler);
|
122
|
+
}
|
104
123
|
void register_gattc_event_handler(GATTcEventHandler *handler) { this->gattc_event_handlers_.push_back(handler); }
|
105
124
|
void register_gatts_event_handler(GATTsEventHandler *handler) { this->gatts_event_handlers_.push_back(handler); }
|
106
125
|
void register_ble_status_event_handler(BLEStatusEventHandler *handler) {
|
@@ -113,22 +132,22 @@ class ESP32BLE : public Component {
|
|
113
132
|
static void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param);
|
114
133
|
static void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param);
|
115
134
|
|
116
|
-
void real_gatts_event_handler_(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param);
|
117
|
-
void real_gattc_event_handler_(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param);
|
118
|
-
void real_gap_event_handler_(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param);
|
119
|
-
|
120
135
|
bool ble_setup_();
|
121
136
|
bool ble_dismantle_();
|
122
137
|
bool ble_pre_setup_();
|
123
138
|
void advertising_init_();
|
124
139
|
|
140
|
+
private:
|
141
|
+
template<typename... Args> friend void enqueue_ble_event(Args... args);
|
142
|
+
|
125
143
|
std::vector<GAPEventHandler *> gap_event_handlers_;
|
144
|
+
std::vector<GAPScanEventHandler *> gap_scan_event_handlers_;
|
126
145
|
std::vector<GATTcEventHandler *> gattc_event_handlers_;
|
127
146
|
std::vector<GATTsEventHandler *> gatts_event_handlers_;
|
128
147
|
std::vector<BLEStatusEventHandler *> ble_status_event_handlers_;
|
129
148
|
BLEComponentState state_{BLE_COMPONENT_STATE_OFF};
|
130
149
|
|
131
|
-
|
150
|
+
LockFreeQueue<BLEEvent, MAX_BLE_QUEUE_SIZE> ble_events_;
|
132
151
|
BLEAdvertising *advertising_{};
|
133
152
|
esp_ble_io_cap_t io_cap_{ESP_IO_CAP_NONE};
|
134
153
|
uint32_t advertising_cycle_time_{};
|