Compare commits
22 Commits
runtime_wi
...
loop_runti
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
45e08ed584 | ||
|
|
a7449dce92 | ||
|
|
8067caf16f | ||
|
|
5fbb066ee7 | ||
|
|
c9680a1ccb | ||
|
|
03399e6dd6 | ||
|
|
7f838ece00 | ||
|
|
3f87010c0e | ||
|
|
032949bc77 | ||
|
|
a960d9966d | ||
|
|
02c390c6c3 | ||
|
|
eebefdf026 | ||
|
|
cb748bbb02 | ||
|
|
c35db19995 | ||
|
|
71b493bd8b | ||
|
|
f67e02c653 | ||
|
|
9db52b17f2 | ||
|
|
d728382542 | ||
|
|
d95bbfc6c4 | ||
|
|
6f8ee65919 | ||
|
|
c5654b4cb2 | ||
|
|
410b6353fe |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -143,5 +143,3 @@ sdkconfig.*
|
||||
/components
|
||||
/managed_components
|
||||
|
||||
|
||||
**/.claude/settings.local.json
|
||||
|
||||
@@ -171,7 +171,7 @@ AudioDecoderState AudioDecoder::decode(bool stop_gracefully) {
|
||||
|
||||
bytes_available_before_processing = this->input_transfer_buffer_->available();
|
||||
|
||||
if ((this->potentially_failed_count_ > 10) && (bytes_read == 0)) {
|
||||
if ((this->potentially_failed_count_ > 0) && (bytes_read == 0)) {
|
||||
// Failed to decode in last attempt and there is no new data
|
||||
|
||||
if ((this->input_transfer_buffer_->free() == 0) && first_loop_iteration) {
|
||||
|
||||
@@ -51,35 +51,54 @@ bool BluetoothProxy::parse_device(const esp32_ble_tracker::ESPBTDevice &device)
|
||||
return true;
|
||||
}
|
||||
|
||||
// Static buffer to store advertisements between batches
|
||||
static constexpr size_t MAX_BATCH_SIZE = 8;
|
||||
static std::vector<api::BluetoothLERawAdvertisement> batch_buffer;
|
||||
|
||||
bool BluetoothProxy::parse_devices(esp_ble_gap_cb_param_t::ble_scan_result_evt_param *advertisements, size_t count) {
|
||||
if (!api::global_api_server->is_connected() || this->api_connection_ == nullptr || !this->raw_advertisements_)
|
||||
return false;
|
||||
|
||||
api::BluetoothLERawAdvertisementsResponse resp;
|
||||
// Pre-allocate the advertisements vector to avoid reallocations
|
||||
resp.advertisements.reserve(count);
|
||||
// Reserve additional capacity if needed
|
||||
size_t new_size = batch_buffer.size() + count;
|
||||
if (batch_buffer.capacity() < new_size) {
|
||||
batch_buffer.reserve(new_size);
|
||||
}
|
||||
|
||||
// Add new advertisements to the batch buffer
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
auto &result = advertisements[i];
|
||||
api::BluetoothLERawAdvertisement adv;
|
||||
uint8_t length = result.adv_data_len + result.scan_rsp_len;
|
||||
|
||||
batch_buffer.emplace_back();
|
||||
auto &adv = batch_buffer.back();
|
||||
adv.address = esp32_ble::ble_addr_to_uint64(result.bda);
|
||||
adv.rssi = result.rssi;
|
||||
adv.address_type = result.ble_addr_type;
|
||||
adv.data.assign(&result.ble_adv[0], &result.ble_adv[length]);
|
||||
|
||||
uint8_t length = result.adv_data_len + result.scan_rsp_len;
|
||||
adv.data.reserve(length);
|
||||
// Use a bulk insert instead of individual push_backs
|
||||
adv.data.insert(adv.data.end(), &result.ble_adv[0], &result.ble_adv[length]);
|
||||
|
||||
resp.advertisements.push_back(std::move(adv));
|
||||
|
||||
ESP_LOGV(TAG, "Proxying raw packet from %02X:%02X:%02X:%02X:%02X:%02X, length %d. RSSI: %d dB", result.bda[0],
|
||||
ESP_LOGV(TAG, "Queuing raw packet from %02X:%02X:%02X:%02X:%02X:%02X, length %d. RSSI: %d dB", result.bda[0],
|
||||
result.bda[1], result.bda[2], result.bda[3], result.bda[4], result.bda[5], length, result.rssi);
|
||||
}
|
||||
ESP_LOGV(TAG, "Proxying %d packets", count);
|
||||
this->api_connection_->send_bluetooth_le_raw_advertisements_response(resp);
|
||||
|
||||
// Only send if we've accumulated a good batch size to maximize batching efficiency
|
||||
// https://github.com/esphome/backlog/issues/21
|
||||
if (batch_buffer.size() >= MAX_BATCH_SIZE) {
|
||||
this->flush_pending_advertisements();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void BluetoothProxy::flush_pending_advertisements() {
|
||||
if (batch_buffer.empty() || !api::global_api_server->is_connected() || this->api_connection_ == nullptr)
|
||||
return;
|
||||
|
||||
api::BluetoothLERawAdvertisementsResponse resp;
|
||||
resp.advertisements.swap(batch_buffer);
|
||||
this->api_connection_->send_bluetooth_le_raw_advertisements_response(resp);
|
||||
}
|
||||
|
||||
void BluetoothProxy::send_api_packet_(const esp32_ble_tracker::ESPBTDevice &device) {
|
||||
api::BluetoothLEAdvertisementResponse resp;
|
||||
resp.address = device.address_uint64();
|
||||
@@ -91,28 +110,28 @@ void BluetoothProxy::send_api_packet_(const esp32_ble_tracker::ESPBTDevice &devi
|
||||
// Pre-allocate vectors based on known sizes
|
||||
auto service_uuids = device.get_service_uuids();
|
||||
resp.service_uuids.reserve(service_uuids.size());
|
||||
for (auto uuid : service_uuids) {
|
||||
resp.service_uuids.push_back(uuid.to_string());
|
||||
for (auto &uuid : service_uuids) {
|
||||
resp.service_uuids.emplace_back(uuid.to_string());
|
||||
}
|
||||
|
||||
// Pre-allocate service data vector
|
||||
auto service_datas = device.get_service_datas();
|
||||
resp.service_data.reserve(service_datas.size());
|
||||
for (auto &data : service_datas) {
|
||||
api::BluetoothServiceData service_data;
|
||||
resp.service_data.emplace_back();
|
||||
auto &service_data = resp.service_data.back();
|
||||
service_data.uuid = data.uuid.to_string();
|
||||
service_data.data.assign(data.data.begin(), data.data.end());
|
||||
resp.service_data.push_back(std::move(service_data));
|
||||
}
|
||||
|
||||
// Pre-allocate manufacturer data vector
|
||||
auto manufacturer_datas = device.get_manufacturer_datas();
|
||||
resp.manufacturer_data.reserve(manufacturer_datas.size());
|
||||
for (auto &data : manufacturer_datas) {
|
||||
api::BluetoothServiceData manufacturer_data;
|
||||
resp.manufacturer_data.emplace_back();
|
||||
auto &manufacturer_data = resp.manufacturer_data.back();
|
||||
manufacturer_data.uuid = data.uuid.to_string();
|
||||
manufacturer_data.data.assign(data.data.begin(), data.data.end());
|
||||
resp.manufacturer_data.push_back(std::move(manufacturer_data));
|
||||
}
|
||||
|
||||
this->api_connection_->send_bluetooth_le_advertisement(resp);
|
||||
@@ -148,6 +167,18 @@ void BluetoothProxy::loop() {
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Flush any pending BLE advertisements that have been accumulated but not yet sent
|
||||
if (this->raw_advertisements_) {
|
||||
static uint32_t last_flush_time = 0;
|
||||
uint32_t now = millis();
|
||||
|
||||
// Flush accumulated advertisements every 100ms
|
||||
if (now - last_flush_time >= 100) {
|
||||
this->flush_pending_advertisements();
|
||||
last_flush_time = now;
|
||||
}
|
||||
}
|
||||
for (auto *connection : this->connections_) {
|
||||
if (connection->send_service_ == connection->service_count_) {
|
||||
connection->send_service_ = DONE_SENDING_SERVICES;
|
||||
|
||||
@@ -56,6 +56,7 @@ class BluetoothProxy : public esp32_ble_tracker::ESPBTDeviceListener, public Com
|
||||
void dump_config() override;
|
||||
void setup() override;
|
||||
void loop() override;
|
||||
void flush_pending_advertisements();
|
||||
esp32_ble_tracker::AdvertisementParserType get_advertisement_parser_type() override;
|
||||
|
||||
void register_connection(BluetoothConnection *connection) {
|
||||
|
||||
@@ -2,6 +2,7 @@ import esphome.codegen as cg
|
||||
from esphome.components import switch
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_TYPE, ENTITY_CATEGORY_CONFIG
|
||||
from esphome.cpp_generator import MockObjClass
|
||||
|
||||
from .. import CONF_DFROBOT_SEN0395_ID, DfrobotSen0395Component
|
||||
|
||||
@@ -26,32 +27,30 @@ Sen0395StartAfterBootSwitch = dfrobot_sen0395_ns.class_(
|
||||
"Sen0395StartAfterBootSwitch", DfrobotSen0395Switch
|
||||
)
|
||||
|
||||
_SWITCH_SCHEMA = (
|
||||
switch.switch_schema(
|
||||
entity_category=ENTITY_CATEGORY_CONFIG,
|
||||
|
||||
def _switch_schema(class_: MockObjClass) -> cv.Schema:
|
||||
return (
|
||||
switch.switch_schema(
|
||||
class_,
|
||||
entity_category=ENTITY_CATEGORY_CONFIG,
|
||||
)
|
||||
.extend(
|
||||
{
|
||||
cv.GenerateID(CONF_DFROBOT_SEN0395_ID): cv.use_id(
|
||||
DfrobotSen0395Component
|
||||
),
|
||||
}
|
||||
)
|
||||
.extend(cv.COMPONENT_SCHEMA)
|
||||
)
|
||||
.extend(
|
||||
{
|
||||
cv.GenerateID(CONF_DFROBOT_SEN0395_ID): cv.use_id(DfrobotSen0395Component),
|
||||
}
|
||||
)
|
||||
.extend(cv.COMPONENT_SCHEMA)
|
||||
)
|
||||
|
||||
|
||||
CONFIG_SCHEMA = cv.typed_schema(
|
||||
{
|
||||
"sensor_active": _SWITCH_SCHEMA.extend(
|
||||
{cv.GenerateID(): cv.declare_id(Sen0395PowerSwitch)}
|
||||
),
|
||||
"turn_on_led": _SWITCH_SCHEMA.extend(
|
||||
{cv.GenerateID(): cv.declare_id(Sen0395LedSwitch)}
|
||||
),
|
||||
"presence_via_uart": _SWITCH_SCHEMA.extend(
|
||||
{cv.GenerateID(): cv.declare_id(Sen0395UartPresenceSwitch)}
|
||||
),
|
||||
"start_after_boot": _SWITCH_SCHEMA.extend(
|
||||
{cv.GenerateID(): cv.declare_id(Sen0395StartAfterBootSwitch)}
|
||||
),
|
||||
"sensor_active": _switch_schema(Sen0395PowerSwitch),
|
||||
"turn_on_led": _switch_schema(Sen0395LedSwitch),
|
||||
"presence_via_uart": _switch_schema(Sen0395UartPresenceSwitch),
|
||||
"start_after_boot": _switch_schema(Sen0395StartAfterBootSwitch),
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from dataclasses import dataclass
|
||||
import logging
|
||||
from typing import Any
|
||||
from typing import Any, Callable
|
||||
|
||||
from esphome import pins
|
||||
import esphome.codegen as cg
|
||||
@@ -64,8 +64,7 @@ def _lookup_pin(value):
|
||||
def _translate_pin(value):
|
||||
if isinstance(value, dict) or value is None:
|
||||
raise cv.Invalid(
|
||||
"This variable only supports pin numbers, not full pin schemas "
|
||||
"(with inverted and mode)."
|
||||
"This variable only supports pin numbers, not full pin schemas (with inverted and mode)."
|
||||
)
|
||||
if isinstance(value, int) and not isinstance(value, bool):
|
||||
return value
|
||||
@@ -82,30 +81,22 @@ def _translate_pin(value):
|
||||
|
||||
@dataclass
|
||||
class ESP32ValidationFunctions:
|
||||
pin_validation: Any
|
||||
usage_validation: Any
|
||||
pin_validation: Callable[[Any], Any]
|
||||
usage_validation: Callable[[Any], Any]
|
||||
|
||||
|
||||
_esp32_validations = {
|
||||
VARIANT_ESP32: ESP32ValidationFunctions(
|
||||
pin_validation=esp32_validate_gpio_pin, usage_validation=esp32_validate_supports
|
||||
),
|
||||
VARIANT_ESP32S2: ESP32ValidationFunctions(
|
||||
pin_validation=esp32_s2_validate_gpio_pin,
|
||||
usage_validation=esp32_s2_validate_supports,
|
||||
VARIANT_ESP32C2: ESP32ValidationFunctions(
|
||||
pin_validation=esp32_c2_validate_gpio_pin,
|
||||
usage_validation=esp32_c2_validate_supports,
|
||||
),
|
||||
VARIANT_ESP32C3: ESP32ValidationFunctions(
|
||||
pin_validation=esp32_c3_validate_gpio_pin,
|
||||
usage_validation=esp32_c3_validate_supports,
|
||||
),
|
||||
VARIANT_ESP32S3: ESP32ValidationFunctions(
|
||||
pin_validation=esp32_s3_validate_gpio_pin,
|
||||
usage_validation=esp32_s3_validate_supports,
|
||||
),
|
||||
VARIANT_ESP32C2: ESP32ValidationFunctions(
|
||||
pin_validation=esp32_c2_validate_gpio_pin,
|
||||
usage_validation=esp32_c2_validate_supports,
|
||||
),
|
||||
VARIANT_ESP32C6: ESP32ValidationFunctions(
|
||||
pin_validation=esp32_c6_validate_gpio_pin,
|
||||
usage_validation=esp32_c6_validate_supports,
|
||||
@@ -114,6 +105,14 @@ _esp32_validations = {
|
||||
pin_validation=esp32_h2_validate_gpio_pin,
|
||||
usage_validation=esp32_h2_validate_supports,
|
||||
),
|
||||
VARIANT_ESP32S2: ESP32ValidationFunctions(
|
||||
pin_validation=esp32_s2_validate_gpio_pin,
|
||||
usage_validation=esp32_s2_validate_supports,
|
||||
),
|
||||
VARIANT_ESP32S3: ESP32ValidationFunctions(
|
||||
pin_validation=esp32_s3_validate_gpio_pin,
|
||||
usage_validation=esp32_s3_validate_supports,
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -31,8 +31,7 @@ def esp32_validate_gpio_pin(value):
|
||||
)
|
||||
if 9 <= value <= 10:
|
||||
_LOGGER.warning(
|
||||
"Pin %s (9-10) might already be used by the "
|
||||
"flash interface in QUAD IO flash mode.",
|
||||
"Pin %s (9-10) might already be used by the flash interface in QUAD IO flash mode.",
|
||||
value,
|
||||
)
|
||||
if value in (24, 28, 29, 30, 31):
|
||||
|
||||
@@ -22,7 +22,7 @@ def esp32_c2_validate_supports(value):
|
||||
is_input = mode[CONF_INPUT]
|
||||
|
||||
if num < 0 or num > 20:
|
||||
raise cv.Invalid(f"Invalid pin number: {value} (must be 0-20)")
|
||||
raise cv.Invalid(f"Invalid pin number: {num} (must be 0-20)")
|
||||
|
||||
if is_input:
|
||||
# All ESP32 pins support input mode
|
||||
|
||||
@@ -35,7 +35,7 @@ def esp32_c3_validate_supports(value):
|
||||
is_input = mode[CONF_INPUT]
|
||||
|
||||
if num < 0 or num > 21:
|
||||
raise cv.Invalid(f"Invalid pin number: {value} (must be 0-21)")
|
||||
raise cv.Invalid(f"Invalid pin number: {num} (must be 0-21)")
|
||||
|
||||
if is_input:
|
||||
# All ESP32 pins support input mode
|
||||
|
||||
@@ -36,7 +36,7 @@ def esp32_c6_validate_supports(value):
|
||||
is_input = mode[CONF_INPUT]
|
||||
|
||||
if num < 0 or num > 23:
|
||||
raise cv.Invalid(f"Invalid pin number: {value} (must be 0-23)")
|
||||
raise cv.Invalid(f"Invalid pin number: {num} (must be 0-23)")
|
||||
if is_input:
|
||||
# All ESP32 pins support input mode
|
||||
pass
|
||||
|
||||
@@ -45,7 +45,7 @@ def esp32_h2_validate_supports(value):
|
||||
is_input = mode[CONF_INPUT]
|
||||
|
||||
if num < 0 or num > 27:
|
||||
raise cv.Invalid(f"Invalid pin number: {value} (must be 0-27)")
|
||||
raise cv.Invalid(f"Invalid pin number: {num} (must be 0-27)")
|
||||
if is_input:
|
||||
# All ESP32 pins support input mode
|
||||
pass
|
||||
|
||||
@@ -17,10 +17,6 @@
|
||||
#include <nvs_flash.h>
|
||||
#include <cinttypes>
|
||||
|
||||
#ifdef USE_BLUETOOTH_PROXY
|
||||
#include "esphome/components/bluetooth_proxy/bluetooth_proxy.h"
|
||||
#endif
|
||||
|
||||
#ifdef USE_OTA
|
||||
#include "esphome/components/ota/ota_backend.h"
|
||||
#endif
|
||||
@@ -79,90 +75,7 @@ void ESP32BLETracker::setup() {
|
||||
#endif
|
||||
}
|
||||
|
||||
void ESP32BLETracker::log_timing_stats_() {
|
||||
// Only log if we have data to report
|
||||
if (this->timing_stats_.loop_processing_count == 0)
|
||||
return;
|
||||
|
||||
ESP_LOGI(TAG, "BLE Tracker Timing Statistics (last %dms):", this->timing_stats_.log_interval_ms);
|
||||
|
||||
if (this->timing_stats_.loop_processing_count > 0) {
|
||||
float avg_loop_time = this->timing_stats_.loop_processing_time / (float) this->timing_stats_.loop_processing_count;
|
||||
ESP_LOGI(TAG, " BLE Scan Processing: count=%d, avg=%.2fms, total=%dms", this->timing_stats_.loop_processing_count,
|
||||
avg_loop_time, this->timing_stats_.loop_processing_time);
|
||||
|
||||
// Log scan results processed
|
||||
ESP_LOGI(TAG, " BLE Scan Results Processed: count=%d", this->timing_stats_.scan_result_count);
|
||||
}
|
||||
|
||||
// Log raw advertisement processing statistics
|
||||
if (this->timing_stats_.raw_adv_count > 0) {
|
||||
float avg_raw_time = this->timing_stats_.raw_adv_time / (float) this->timing_stats_.raw_adv_count;
|
||||
ESP_LOGI(TAG, " Raw Advertisement Processing: count=%d, avg=%.2fms, total=%dms", this->timing_stats_.raw_adv_count,
|
||||
avg_raw_time, this->timing_stats_.raw_adv_time);
|
||||
}
|
||||
|
||||
// Log parsed advertisement processing statistics
|
||||
if (this->timing_stats_.parse_adv_count > 0) {
|
||||
float avg_parse_time = this->timing_stats_.parse_adv_time / (float) this->timing_stats_.parse_adv_count;
|
||||
ESP_LOGI(TAG, " Parsed Advertisement Processing: count=%d, avg=%.2fms, total=%dms",
|
||||
this->timing_stats_.parse_adv_count, avg_parse_time, this->timing_stats_.parse_adv_time);
|
||||
}
|
||||
|
||||
// Log individual device parsing statistics
|
||||
if (this->timing_stats_.parse_device_count > 0) {
|
||||
float avg_device_time = this->timing_stats_.parse_device_time / (float) this->timing_stats_.parse_device_count;
|
||||
ESP_LOGI(TAG, " Device Parsing: count=%d, avg=%.2fms, total=%dms", this->timing_stats_.parse_device_count,
|
||||
avg_device_time, this->timing_stats_.parse_device_time);
|
||||
}
|
||||
|
||||
// Log component-specific timings
|
||||
if (this->timing_stats_.bluetooth_proxy_count > 0) {
|
||||
float avg_proxy_time = this->timing_stats_.bluetooth_proxy_time / (float) this->timing_stats_.bluetooth_proxy_count;
|
||||
ESP_LOGI(TAG, " Bluetooth Proxy: count=%d, avg=%.2fms, total=%dms", this->timing_stats_.bluetooth_proxy_count,
|
||||
avg_proxy_time, this->timing_stats_.bluetooth_proxy_time);
|
||||
}
|
||||
|
||||
if (this->timing_stats_.client_listeners_count > 0) {
|
||||
float avg_client_time =
|
||||
this->timing_stats_.client_listeners_time / (float) this->timing_stats_.client_listeners_count;
|
||||
ESP_LOGI(TAG, " Client Listeners: count=%d, avg=%.2fms, total=%dms", this->timing_stats_.client_listeners_count,
|
||||
avg_client_time, this->timing_stats_.client_listeners_time);
|
||||
}
|
||||
|
||||
// Log gap scan result processing statistics
|
||||
if (this->timing_stats_.gap_scan_result_count > 0) {
|
||||
float avg_gap_time = this->timing_stats_.gap_scan_result_time / (float) this->timing_stats_.gap_scan_result_count;
|
||||
ESP_LOGI(TAG, " GAP Scan Result Processing: count=%d, avg=%.2fms, total=%dms",
|
||||
this->timing_stats_.gap_scan_result_count, avg_gap_time, this->timing_stats_.gap_scan_result_time);
|
||||
}
|
||||
|
||||
// Reset timers for next period
|
||||
this->timing_stats_.loop_processing_time = 0;
|
||||
this->timing_stats_.loop_processing_count = 0;
|
||||
this->timing_stats_.bluetooth_proxy_time = 0;
|
||||
this->timing_stats_.bluetooth_proxy_count = 0;
|
||||
this->timing_stats_.client_listeners_time = 0;
|
||||
this->timing_stats_.client_listeners_count = 0;
|
||||
this->timing_stats_.scan_result_count = 0;
|
||||
this->timing_stats_.raw_adv_time = 0;
|
||||
this->timing_stats_.raw_adv_count = 0;
|
||||
this->timing_stats_.parse_adv_time = 0;
|
||||
this->timing_stats_.parse_adv_count = 0;
|
||||
this->timing_stats_.parse_device_time = 0;
|
||||
this->timing_stats_.parse_device_count = 0;
|
||||
this->timing_stats_.gap_scan_result_time = 0;
|
||||
this->timing_stats_.gap_scan_result_count = 0;
|
||||
}
|
||||
|
||||
void ESP32BLETracker::loop() {
|
||||
// Check if we need to log stats first
|
||||
uint32_t now = millis();
|
||||
if (now - this->timing_stats_.last_log_time >= this->timing_stats_.log_interval_ms) {
|
||||
this->log_timing_stats_();
|
||||
this->timing_stats_.last_log_time = now;
|
||||
}
|
||||
|
||||
if (!this->parent_->is_active()) {
|
||||
this->ble_was_disabled_ = true;
|
||||
return;
|
||||
@@ -209,76 +122,26 @@ void ESP32BLETracker::loop() {
|
||||
|
||||
if (this->scanner_state_ == ScannerState::RUNNING &&
|
||||
this->scan_result_index_ && // if it looks like we have a scan result we will take the lock
|
||||
xSemaphoreTake(this->scan_result_lock_, 5L / portTICK_PERIOD_MS)) {
|
||||
// Start timing the BLE scan processing
|
||||
uint32_t processing_start = millis();
|
||||
|
||||
xSemaphoreTake(this->scan_result_lock_, 0)) {
|
||||
uint32_t index = this->scan_result_index_;
|
||||
if (index >= ESP32BLETracker::SCAN_RESULT_BUFFER_SIZE) {
|
||||
ESP_LOGW(TAG, "Too many BLE events to process. Some devices may not show up.");
|
||||
}
|
||||
|
||||
if (this->raw_advertisements_) {
|
||||
this->timing_stats_.scan_result_count += this->scan_result_index_;
|
||||
|
||||
// Track overall raw advertisement processing time
|
||||
uint32_t raw_adv_start = millis();
|
||||
|
||||
// Time each listener type separately
|
||||
for (auto *listener : this->listeners_) {
|
||||
// Check if this is the bluetooth_proxy component
|
||||
bool is_bluetooth_proxy = false;
|
||||
|
||||
#ifdef USE_BLUETOOTH_PROXY
|
||||
// Since RTTI is disabled (typeid can't be used), check if the address is the global bluetooth proxy
|
||||
// This is a safe pointer comparison since we're not calling any methods on it
|
||||
if (esphome::bluetooth_proxy::global_bluetooth_proxy != nullptr) {
|
||||
is_bluetooth_proxy =
|
||||
(listener == static_cast<ESPBTDeviceListener *>(esphome::bluetooth_proxy::global_bluetooth_proxy));
|
||||
}
|
||||
#endif
|
||||
|
||||
uint32_t listener_start = millis();
|
||||
listener->parse_devices(this->scan_result_buffer_, this->scan_result_index_);
|
||||
uint32_t listener_time = millis() - listener_start;
|
||||
|
||||
// Track times for specific components
|
||||
if (is_bluetooth_proxy) {
|
||||
this->timing_stats_.bluetooth_proxy_time += listener_time;
|
||||
this->timing_stats_.bluetooth_proxy_count++;
|
||||
}
|
||||
}
|
||||
|
||||
// Time the client listeners
|
||||
uint32_t clients_start = millis();
|
||||
for (auto *client : this->clients_) {
|
||||
client->parse_devices(this->scan_result_buffer_, this->scan_result_index_);
|
||||
}
|
||||
uint32_t clients_time = millis() - clients_start;
|
||||
this->timing_stats_.client_listeners_time += clients_time;
|
||||
this->timing_stats_.client_listeners_count++;
|
||||
|
||||
// Calculate total raw advertisement processing time
|
||||
uint32_t raw_adv_time = millis() - raw_adv_start;
|
||||
this->timing_stats_.raw_adv_time += raw_adv_time;
|
||||
this->timing_stats_.raw_adv_count++;
|
||||
}
|
||||
|
||||
if (this->parse_advertisements_) {
|
||||
// Track overall time for parsed advertisements
|
||||
uint32_t parse_adv_start = millis();
|
||||
|
||||
for (size_t i = 0; i < index; i++) {
|
||||
// Time individual device parsing
|
||||
uint32_t device_start = millis();
|
||||
|
||||
ESPBTDevice device;
|
||||
device.parse_scan_rst(this->scan_result_buffer_[i]);
|
||||
|
||||
uint32_t device_parse_time = millis() - device_start;
|
||||
this->timing_stats_.parse_device_time += device_parse_time;
|
||||
this->timing_stats_.parse_device_count++;
|
||||
|
||||
bool found = false;
|
||||
for (auto *listener : this->listeners_) {
|
||||
if (listener->parse_device(device))
|
||||
@@ -298,19 +161,9 @@ void ESP32BLETracker::loop() {
|
||||
this->print_bt_device_info(device);
|
||||
}
|
||||
}
|
||||
|
||||
// Record total time for parsed advertisements processing
|
||||
uint32_t parse_adv_time = millis() - parse_adv_start;
|
||||
this->timing_stats_.parse_adv_time += parse_adv_time;
|
||||
this->timing_stats_.parse_adv_count++;
|
||||
}
|
||||
this->scan_result_index_ = 0;
|
||||
xSemaphoreGive(this->scan_result_lock_);
|
||||
|
||||
// Record timing for the BLE scan processing
|
||||
uint32_t processing_time = millis() - processing_start;
|
||||
this->timing_stats_.loop_processing_time += processing_time;
|
||||
this->timing_stats_.loop_processing_count++;
|
||||
}
|
||||
if (this->scanner_state_ == ScannerState::STOPPED) {
|
||||
this->end_of_scan_(); // Change state to IDLE
|
||||
@@ -592,11 +445,9 @@ void ESP32BLETracker::gap_scan_stop_complete_(const esp_ble_gap_cb_param_t::ble_
|
||||
}
|
||||
|
||||
void ESP32BLETracker::gap_scan_result_(const esp_ble_gap_cb_param_t::ble_scan_result_evt_param ¶m) {
|
||||
uint32_t start_time = millis();
|
||||
|
||||
ESP_LOGV(TAG, "gap_scan_result - event %d", param.search_evt);
|
||||
if (param.search_evt == ESP_GAP_SEARCH_INQ_RES_EVT) {
|
||||
if (xSemaphoreTake(this->scan_result_lock_, 0L)) {
|
||||
if (xSemaphoreTake(this->scan_result_lock_, 0)) {
|
||||
if (this->scan_result_index_ < ESP32BLETracker::SCAN_RESULT_BUFFER_SIZE) {
|
||||
this->scan_result_buffer_[this->scan_result_index_++] = param;
|
||||
}
|
||||
@@ -619,18 +470,6 @@ void ESP32BLETracker::gap_scan_result_(const esp_ble_gap_cb_param_t::ble_scan_re
|
||||
}
|
||||
this->set_scanner_state_(ScannerState::STOPPED);
|
||||
}
|
||||
|
||||
// Record timing statistics
|
||||
uint32_t duration = millis() - start_time;
|
||||
this->timing_stats_.gap_scan_result_time += duration;
|
||||
this->timing_stats_.gap_scan_result_count++;
|
||||
|
||||
// Periodically log statistics
|
||||
uint32_t now = millis();
|
||||
if (now - this->timing_stats_.last_log_time >= this->timing_stats_.log_interval_ms) {
|
||||
this->log_timing_stats_();
|
||||
this->timing_stats_.last_log_time = now;
|
||||
}
|
||||
}
|
||||
|
||||
void ESP32BLETracker::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
|
||||
|
||||
@@ -247,9 +247,6 @@ class ESP32BLETracker : public Component,
|
||||
}
|
||||
ScannerState get_scanner_state() const { return this->scanner_state_; }
|
||||
|
||||
// Logs timing statistics for BLE operations
|
||||
void log_timing_stats_();
|
||||
|
||||
protected:
|
||||
void stop_scan_();
|
||||
/// Start a single scan by setting up the parameters and doing some esp-idf calls.
|
||||
@@ -293,7 +290,7 @@ class ESP32BLETracker : public Component,
|
||||
#ifdef USE_PSRAM
|
||||
const static u_int8_t SCAN_RESULT_BUFFER_SIZE = 32;
|
||||
#else
|
||||
const static u_int8_t SCAN_RESULT_BUFFER_SIZE = 16;
|
||||
const static u_int8_t SCAN_RESULT_BUFFER_SIZE = 20;
|
||||
#endif // USE_PSRAM
|
||||
esp_ble_gap_cb_param_t::ble_scan_result_evt_param *scan_result_buffer_;
|
||||
esp_bt_status_t scan_start_failed_{ESP_BT_STATUS_SUCCESS};
|
||||
@@ -302,39 +299,6 @@ class ESP32BLETracker : public Component,
|
||||
int discovered_{0};
|
||||
int searching_{0};
|
||||
int disconnecting_{0};
|
||||
|
||||
// Timing statistics
|
||||
struct {
|
||||
// Loop processing timing
|
||||
uint32_t loop_processing_time{0};
|
||||
uint32_t loop_processing_count{0};
|
||||
|
||||
// Detailed component-level timings
|
||||
uint32_t bluetooth_proxy_time{0};
|
||||
uint32_t bluetooth_proxy_count{0};
|
||||
uint32_t client_listeners_time{0};
|
||||
uint32_t client_listeners_count{0};
|
||||
uint32_t scan_result_count{0};
|
||||
|
||||
// Raw advertisement processing stats
|
||||
uint32_t raw_adv_time{0};
|
||||
uint32_t raw_adv_count{0};
|
||||
|
||||
// Parsed advertisement processing stats
|
||||
uint32_t parse_adv_time{0};
|
||||
uint32_t parse_adv_count{0};
|
||||
uint32_t parse_device_time{0};
|
||||
uint32_t parse_device_count{0};
|
||||
|
||||
// Gap scan result stats
|
||||
uint32_t gap_scan_result_time{0};
|
||||
uint32_t gap_scan_result_count{0};
|
||||
|
||||
// Logging control
|
||||
uint32_t last_log_time{0};
|
||||
uint32_t log_interval_ms{30000}; // 30 seconds
|
||||
} timing_stats_;
|
||||
|
||||
#ifdef USE_ESP32_BLE_SOFTWARE_COEXISTENCE
|
||||
bool coex_prefer_ble_{false};
|
||||
#endif
|
||||
|
||||
@@ -72,6 +72,9 @@ _SWITCH_SCHEMA = (
|
||||
{
|
||||
cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTSwitchComponent),
|
||||
cv.Optional(CONF_INVERTED): cv.boolean,
|
||||
cv.Optional(CONF_RESTORE_MODE, default="ALWAYS_OFF"): cv.enum(
|
||||
RESTORE_MODES, upper=True, space="_"
|
||||
),
|
||||
cv.Optional(CONF_ON_TURN_ON): automation.validate_automation(
|
||||
{
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(SwitchTurnOnTrigger),
|
||||
@@ -89,54 +92,41 @@ _SWITCH_SCHEMA = (
|
||||
|
||||
|
||||
def switch_schema(
|
||||
class_: MockObjClass = cv.UNDEFINED,
|
||||
class_: MockObjClass,
|
||||
*,
|
||||
entity_category: str = cv.UNDEFINED,
|
||||
device_class: str = cv.UNDEFINED,
|
||||
icon: str = cv.UNDEFINED,
|
||||
block_inverted: bool = False,
|
||||
default_restore_mode: str = "ALWAYS_OFF",
|
||||
default_restore_mode: str = cv.UNDEFINED,
|
||||
device_class: str = cv.UNDEFINED,
|
||||
entity_category: str = cv.UNDEFINED,
|
||||
icon: str = cv.UNDEFINED,
|
||||
):
|
||||
schema = _SWITCH_SCHEMA.extend(
|
||||
{
|
||||
cv.Optional(CONF_RESTORE_MODE, default=default_restore_mode): cv.enum(
|
||||
RESTORE_MODES, upper=True, space="_"
|
||||
),
|
||||
}
|
||||
)
|
||||
if class_ is not cv.UNDEFINED:
|
||||
schema = schema.extend({cv.GenerateID(): cv.declare_id(class_)})
|
||||
if entity_category is not cv.UNDEFINED:
|
||||
schema = schema.extend(
|
||||
{
|
||||
cv.Optional(
|
||||
CONF_ENTITY_CATEGORY, default=entity_category
|
||||
): cv.entity_category
|
||||
}
|
||||
)
|
||||
if device_class is not cv.UNDEFINED:
|
||||
schema = schema.extend(
|
||||
{
|
||||
cv.Optional(
|
||||
CONF_DEVICE_CLASS, default=device_class
|
||||
): validate_device_class
|
||||
}
|
||||
)
|
||||
if icon is not cv.UNDEFINED:
|
||||
schema = schema.extend({cv.Optional(CONF_ICON, default=icon): cv.icon})
|
||||
schema = {cv.GenerateID(): cv.declare_id(class_)}
|
||||
|
||||
for key, default, validator in [
|
||||
(CONF_DEVICE_CLASS, device_class, validate_device_class),
|
||||
(CONF_ENTITY_CATEGORY, entity_category, cv.entity_category),
|
||||
(CONF_ICON, icon, cv.icon),
|
||||
(
|
||||
CONF_RESTORE_MODE,
|
||||
default_restore_mode,
|
||||
cv.enum(RESTORE_MODES, upper=True, space="_")
|
||||
if default_restore_mode is not cv.UNDEFINED
|
||||
else cv.UNDEFINED,
|
||||
),
|
||||
]:
|
||||
if default is not cv.UNDEFINED:
|
||||
schema[cv.Optional(key, default=default)] = validator
|
||||
|
||||
if block_inverted:
|
||||
schema = schema.extend(
|
||||
{
|
||||
cv.Optional(CONF_INVERTED): cv.invalid(
|
||||
"Inverted is not supported for this platform!"
|
||||
)
|
||||
}
|
||||
schema[cv.Optional(CONF_INVERTED)] = cv.invalid(
|
||||
"Inverted is not supported for this platform!"
|
||||
)
|
||||
return schema
|
||||
|
||||
return _SWITCH_SCHEMA.extend(schema)
|
||||
|
||||
|
||||
# Remove before 2025.11.0
|
||||
SWITCH_SCHEMA = switch_schema()
|
||||
SWITCH_SCHEMA = switch_schema(Switch)
|
||||
SWITCH_SCHEMA.add_extra(cv.deprecated_schema_constant("switch"))
|
||||
|
||||
|
||||
|
||||
@@ -156,32 +156,24 @@ _TEXT_SENSOR_SCHEMA = (
|
||||
def text_sensor_schema(
|
||||
class_: MockObjClass = cv.UNDEFINED,
|
||||
*,
|
||||
icon: str = cv.UNDEFINED,
|
||||
entity_category: str = cv.UNDEFINED,
|
||||
device_class: str = cv.UNDEFINED,
|
||||
entity_category: str = cv.UNDEFINED,
|
||||
icon: str = cv.UNDEFINED,
|
||||
) -> cv.Schema:
|
||||
schema = _TEXT_SENSOR_SCHEMA
|
||||
schema = {}
|
||||
|
||||
if class_ is not cv.UNDEFINED:
|
||||
schema = schema.extend({cv.GenerateID(): cv.declare_id(class_)})
|
||||
if icon is not cv.UNDEFINED:
|
||||
schema = schema.extend({cv.Optional(CONF_ICON, default=icon): cv.icon})
|
||||
if device_class is not cv.UNDEFINED:
|
||||
schema = schema.extend(
|
||||
{
|
||||
cv.Optional(
|
||||
CONF_DEVICE_CLASS, default=device_class
|
||||
): validate_device_class
|
||||
}
|
||||
)
|
||||
if entity_category is not cv.UNDEFINED:
|
||||
schema = schema.extend(
|
||||
{
|
||||
cv.Optional(
|
||||
CONF_ENTITY_CATEGORY, default=entity_category
|
||||
): cv.entity_category
|
||||
}
|
||||
)
|
||||
return schema
|
||||
schema[cv.GenerateID()] = cv.declare_id(class_)
|
||||
|
||||
for key, default, validator in [
|
||||
(CONF_ICON, icon, cv.icon),
|
||||
(CONF_DEVICE_CLASS, device_class, validate_device_class),
|
||||
(CONF_ENTITY_CATEGORY, entity_category, cv.entity_category),
|
||||
]:
|
||||
if default is not cv.UNDEFINED:
|
||||
schema[cv.Optional(key, default=default)] = validator
|
||||
|
||||
return _TEXT_SENSOR_SCHEMA.extend(schema)
|
||||
|
||||
|
||||
# Remove before 2025.11.0
|
||||
|
||||
@@ -26,3 +26,17 @@ dfrobot_sen0395:
|
||||
binary_sensor:
|
||||
- platform: dfrobot_sen0395
|
||||
id: mmwave_detected
|
||||
|
||||
switch:
|
||||
- platform: dfrobot_sen0395
|
||||
type: sensor_active
|
||||
id: mmwave_sensor_active
|
||||
- platform: dfrobot_sen0395
|
||||
type: turn_on_led
|
||||
id: mmwave_turn_on_led
|
||||
- platform: dfrobot_sen0395
|
||||
type: presence_via_uart
|
||||
id: mmwave_presence_via_uart
|
||||
- platform: dfrobot_sen0395
|
||||
type: start_after_boot
|
||||
id: mmwave_start_after_boot
|
||||
|
||||
Reference in New Issue
Block a user