1
0
mirror of https://github.com/esphome/esphome.git synced 2025-10-08 21:03:49 +01:00

[esp32_ble_server] Replace EventEmitter with direct callbacks to reduce memory usage (#10946)

This commit is contained in:
J. Nick Koston
2025-10-05 15:20:40 -05:00
committed by GitHub
parent 722c5a94f2
commit 39d5cbc74a
13 changed files with 107 additions and 234 deletions

View File

@@ -160,7 +160,6 @@ esphome/components/esp_ldo/* @clydebarrow
esphome/components/espnow/* @jesserockz esphome/components/espnow/* @jesserockz
esphome/components/ethernet_info/* @gtjadsonsantos esphome/components/ethernet_info/* @gtjadsonsantos
esphome/components/event/* @nohat esphome/components/event/* @nohat
esphome/components/event_emitter/* @Rapsssito
esphome/components/exposure_notifications/* @OttoWinter esphome/components/exposure_notifications/* @OttoWinter
esphome/components/ezo/* @ssieb esphome/components/ezo/* @ssieb
esphome/components/ezo_pmp/* @carlos-sarmiento esphome/components/ezo_pmp/* @carlos-sarmiento

View File

@@ -26,7 +26,7 @@ from esphome.const import (
from esphome.core import CORE from esphome.core import CORE
from esphome.schema_extractors import SCHEMA_EXTRACT from esphome.schema_extractors import SCHEMA_EXTRACT
AUTO_LOAD = ["esp32_ble", "bytebuffer", "event_emitter"] AUTO_LOAD = ["esp32_ble", "bytebuffer"]
CODEOWNERS = ["@jesserockz", "@clydebarrow", "@Rapsssito"] CODEOWNERS = ["@jesserockz", "@clydebarrow", "@Rapsssito"]
DEPENDENCIES = ["esp32"] DEPENDENCIES = ["esp32"]
DOMAIN = "esp32_ble_server" DOMAIN = "esp32_ble_server"

View File

@@ -73,7 +73,7 @@ void BLECharacteristic::notify() {
void BLECharacteristic::add_descriptor(BLEDescriptor *descriptor) { void BLECharacteristic::add_descriptor(BLEDescriptor *descriptor) {
// If the descriptor is the CCCD descriptor, listen to its write event to know if the client wants to be notified // If the descriptor is the CCCD descriptor, listen to its write event to know if the client wants to be notified
if (descriptor->get_uuid() == ESPBTUUID::from_uint16(ESP_GATT_UUID_CHAR_CLIENT_CONFIG)) { if (descriptor->get_uuid() == ESPBTUUID::from_uint16(ESP_GATT_UUID_CHAR_CLIENT_CONFIG)) {
descriptor->on(BLEDescriptorEvt::VectorEvt::ON_WRITE, [this](const std::vector<uint8_t> &value, uint16_t conn_id) { descriptor->on_write([this](std::span<const uint8_t> value, uint16_t conn_id) {
if (value.size() != 2) if (value.size() != 2)
return; return;
uint16_t cccd = encode_uint16(value[1], value[0]); uint16_t cccd = encode_uint16(value[1], value[0]);
@@ -208,8 +208,9 @@ void BLECharacteristic::gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt
if (!param->read.need_rsp) if (!param->read.need_rsp)
break; // For some reason you can request a read but not want a response break; // For some reason you can request a read but not want a response
this->EventEmitter<BLECharacteristicEvt::EmptyEvt, uint16_t>::emit_(BLECharacteristicEvt::EmptyEvt::ON_READ, if (this->on_read_callback_) {
param->read.conn_id); (*this->on_read_callback_)(param->read.conn_id);
}
uint16_t max_offset = 22; uint16_t max_offset = 22;
@@ -277,8 +278,9 @@ void BLECharacteristic::gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt
} }
if (!param->write.is_prep) { if (!param->write.is_prep) {
this->EventEmitter<BLECharacteristicEvt::VectorEvt, std::vector<uint8_t>, uint16_t>::emit_( if (this->on_write_callback_) {
BLECharacteristicEvt::VectorEvt::ON_WRITE, this->value_, param->write.conn_id); (*this->on_write_callback_)(this->value_, param->write.conn_id);
}
} }
break; break;
@@ -289,8 +291,9 @@ void BLECharacteristic::gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt
break; break;
this->write_event_ = false; this->write_event_ = false;
if (param->exec_write.exec_write_flag == ESP_GATT_PREP_WRITE_EXEC) { if (param->exec_write.exec_write_flag == ESP_GATT_PREP_WRITE_EXEC) {
this->EventEmitter<BLECharacteristicEvt::VectorEvt, std::vector<uint8_t>, uint16_t>::emit_( if (this->on_write_callback_) {
BLECharacteristicEvt::VectorEvt::ON_WRITE, this->value_, param->exec_write.conn_id); (*this->on_write_callback_)(this->value_, param->exec_write.conn_id);
}
} }
esp_err_t err = esp_err_t err =
esp_ble_gatts_send_response(gatts_if, param->write.conn_id, param->write.trans_id, ESP_GATT_OK, nullptr); esp_ble_gatts_send_response(gatts_if, param->write.conn_id, param->write.trans_id, ESP_GATT_OK, nullptr);

View File

@@ -2,10 +2,12 @@
#include "ble_descriptor.h" #include "ble_descriptor.h"
#include "esphome/components/esp32_ble/ble_uuid.h" #include "esphome/components/esp32_ble/ble_uuid.h"
#include "esphome/components/event_emitter/event_emitter.h"
#include "esphome/components/bytebuffer/bytebuffer.h" #include "esphome/components/bytebuffer/bytebuffer.h"
#include <vector> #include <vector>
#include <span>
#include <functional>
#include <memory>
#ifdef USE_ESP32 #ifdef USE_ESP32
@@ -22,22 +24,10 @@ namespace esp32_ble_server {
using namespace esp32_ble; using namespace esp32_ble;
using namespace bytebuffer; using namespace bytebuffer;
using namespace event_emitter;
class BLEService; class BLEService;
namespace BLECharacteristicEvt { class BLECharacteristic {
enum VectorEvt {
ON_WRITE,
};
enum EmptyEvt {
ON_READ,
};
} // namespace BLECharacteristicEvt
class BLECharacteristic : public EventEmitter<BLECharacteristicEvt::VectorEvt, std::vector<uint8_t>, uint16_t>,
public EventEmitter<BLECharacteristicEvt::EmptyEvt, uint16_t> {
public: public:
BLECharacteristic(ESPBTUUID uuid, uint32_t properties); BLECharacteristic(ESPBTUUID uuid, uint32_t properties);
~BLECharacteristic(); ~BLECharacteristic();
@@ -76,6 +66,15 @@ class BLECharacteristic : public EventEmitter<BLECharacteristicEvt::VectorEvt, s
bool is_created(); bool is_created();
bool is_failed(); bool is_failed();
// Direct callback registration - only allocates when callback is set
void on_write(std::function<void(std::span<const uint8_t>, uint16_t)> &&callback) {
this->on_write_callback_ =
std::make_unique<std::function<void(std::span<const uint8_t>, uint16_t)>>(std::move(callback));
}
void on_read(std::function<void(uint16_t)> &&callback) {
this->on_read_callback_ = std::make_unique<std::function<void(uint16_t)>>(std::move(callback));
}
protected: protected:
bool write_event_{false}; bool write_event_{false};
BLEService *service_{}; BLEService *service_{};
@@ -98,6 +97,9 @@ class BLECharacteristic : public EventEmitter<BLECharacteristicEvt::VectorEvt, s
void remove_client_from_notify_list_(uint16_t conn_id); void remove_client_from_notify_list_(uint16_t conn_id);
ClientNotificationEntry *find_client_in_notify_list_(uint16_t conn_id); ClientNotificationEntry *find_client_in_notify_list_(uint16_t conn_id);
std::unique_ptr<std::function<void(std::span<const uint8_t>, uint16_t)>> on_write_callback_;
std::unique_ptr<std::function<void(uint16_t)>> on_read_callback_;
esp_gatt_perm_t permissions_ = ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE; esp_gatt_perm_t permissions_ = ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE;
enum State : uint8_t { enum State : uint8_t {

View File

@@ -74,9 +74,10 @@ void BLEDescriptor::gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_
break; break;
this->value_.attr_len = param->write.len; this->value_.attr_len = param->write.len;
memcpy(this->value_.attr_value, param->write.value, param->write.len); memcpy(this->value_.attr_value, param->write.value, param->write.len);
this->emit_(BLEDescriptorEvt::VectorEvt::ON_WRITE, if (this->on_write_callback_) {
std::vector<uint8_t>(param->write.value, param->write.value + param->write.len), (*this->on_write_callback_)(std::span<const uint8_t>(param->write.value, param->write.len),
param->write.conn_id); param->write.conn_id);
}
break; break;
} }
default: default:

View File

@@ -1,30 +1,26 @@
#pragma once #pragma once
#include "esphome/components/esp32_ble/ble_uuid.h" #include "esphome/components/esp32_ble/ble_uuid.h"
#include "esphome/components/event_emitter/event_emitter.h"
#include "esphome/components/bytebuffer/bytebuffer.h" #include "esphome/components/bytebuffer/bytebuffer.h"
#ifdef USE_ESP32 #ifdef USE_ESP32
#include <esp_gatt_defs.h> #include <esp_gatt_defs.h>
#include <esp_gatts_api.h> #include <esp_gatts_api.h>
#include <span>
#include <functional>
#include <memory>
namespace esphome { namespace esphome {
namespace esp32_ble_server { namespace esp32_ble_server {
using namespace esp32_ble; using namespace esp32_ble;
using namespace bytebuffer; using namespace bytebuffer;
using namespace event_emitter;
class BLECharacteristic; class BLECharacteristic;
namespace BLEDescriptorEvt { // Base class for BLE descriptors
enum VectorEvt { class BLEDescriptor {
ON_WRITE,
};
} // namespace BLEDescriptorEvt
class BLEDescriptor : public EventEmitter<BLEDescriptorEvt::VectorEvt, std::vector<uint8_t>, uint16_t> {
public: public:
BLEDescriptor(ESPBTUUID uuid, uint16_t max_len = 100, bool read = true, bool write = true); BLEDescriptor(ESPBTUUID uuid, uint16_t max_len = 100, bool read = true, bool write = true);
virtual ~BLEDescriptor(); virtual ~BLEDescriptor();
@@ -39,6 +35,12 @@ class BLEDescriptor : public EventEmitter<BLEDescriptorEvt::VectorEvt, std::vect
bool is_created() { return this->state_ == CREATED; } bool is_created() { return this->state_ == CREATED; }
bool is_failed() { return this->state_ == FAILED; } bool is_failed() { return this->state_ == FAILED; }
// Direct callback registration - only allocates when callback is set
void on_write(std::function<void(std::span<const uint8_t>, uint16_t)> &&callback) {
this->on_write_callback_ =
std::make_unique<std::function<void(std::span<const uint8_t>, uint16_t)>>(std::move(callback));
}
protected: protected:
BLECharacteristic *characteristic_{nullptr}; BLECharacteristic *characteristic_{nullptr};
ESPBTUUID uuid_; ESPBTUUID uuid_;
@@ -46,6 +48,8 @@ class BLEDescriptor : public EventEmitter<BLEDescriptorEvt::VectorEvt, std::vect
esp_attr_value_t value_{}; esp_attr_value_t value_{};
std::unique_ptr<std::function<void(std::span<const uint8_t>, uint16_t)>> on_write_callback_;
esp_gatt_perm_t permissions_{}; esp_gatt_perm_t permissions_{};
enum State : uint8_t { enum State : uint8_t {

View File

@@ -147,20 +147,28 @@ BLEService *BLEServer::get_service(ESPBTUUID uuid, uint8_t inst_id) {
return nullptr; return nullptr;
} }
void BLEServer::dispatch_callbacks_(CallbackType type, uint16_t conn_id) {
for (auto &entry : this->callbacks_) {
if (entry.type == type) {
entry.callback(conn_id);
}
}
}
void BLEServer::gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, void BLEServer::gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if,
esp_ble_gatts_cb_param_t *param) { esp_ble_gatts_cb_param_t *param) {
switch (event) { switch (event) {
case ESP_GATTS_CONNECT_EVT: { case ESP_GATTS_CONNECT_EVT: {
ESP_LOGD(TAG, "BLE Client connected"); ESP_LOGD(TAG, "BLE Client connected");
this->add_client_(param->connect.conn_id); this->add_client_(param->connect.conn_id);
this->emit_(BLEServerEvt::EmptyEvt::ON_CONNECT, param->connect.conn_id); this->dispatch_callbacks_(CallbackType::ON_CONNECT, param->connect.conn_id);
break; break;
} }
case ESP_GATTS_DISCONNECT_EVT: { case ESP_GATTS_DISCONNECT_EVT: {
ESP_LOGD(TAG, "BLE Client disconnected"); ESP_LOGD(TAG, "BLE Client disconnected");
this->remove_client_(param->disconnect.conn_id); this->remove_client_(param->disconnect.conn_id);
this->parent_->advertising_start(); this->parent_->advertising_start();
this->emit_(BLEServerEvt::EmptyEvt::ON_DISCONNECT, param->disconnect.conn_id); this->dispatch_callbacks_(CallbackType::ON_DISCONNECT, param->disconnect.conn_id);
break; break;
} }
case ESP_GATTS_REG_EVT: { case ESP_GATTS_REG_EVT: {

View File

@@ -13,6 +13,7 @@
#include <vector> #include <vector>
#include <unordered_map> #include <unordered_map>
#include <unordered_set> #include <unordered_set>
#include <functional>
#ifdef USE_ESP32 #ifdef USE_ESP32
@@ -24,18 +25,7 @@ namespace esp32_ble_server {
using namespace esp32_ble; using namespace esp32_ble;
using namespace bytebuffer; using namespace bytebuffer;
namespace BLEServerEvt { class BLEServer : public Component, public GATTsEventHandler, public BLEStatusEventHandler, public Parented<ESP32BLE> {
enum EmptyEvt {
ON_CONNECT,
ON_DISCONNECT,
};
} // namespace BLEServerEvt
class BLEServer : public Component,
public GATTsEventHandler,
public BLEStatusEventHandler,
public Parented<ESP32BLE>,
public EventEmitter<BLEServerEvt::EmptyEvt, uint16_t> {
public: public:
void setup() override; void setup() override;
void loop() override; void loop() override;
@@ -65,7 +55,25 @@ class BLEServer : public Component,
void ble_before_disabled_event_handler() override; void ble_before_disabled_event_handler() override;
// Direct callback registration - supports multiple callbacks
void on_connect(std::function<void(uint16_t)> &&callback) {
this->callbacks_.push_back({CallbackType::ON_CONNECT, std::move(callback)});
}
void on_disconnect(std::function<void(uint16_t)> &&callback) {
this->callbacks_.push_back({CallbackType::ON_DISCONNECT, std::move(callback)});
}
protected: protected:
enum class CallbackType : uint8_t {
ON_CONNECT,
ON_DISCONNECT,
};
struct CallbackEntry {
CallbackType type;
std::function<void(uint16_t)> callback;
};
struct ServiceEntry { struct ServiceEntry {
ESPBTUUID uuid; ESPBTUUID uuid;
uint8_t inst_id; uint8_t inst_id;
@@ -76,6 +84,9 @@ class BLEServer : public Component,
void add_client_(uint16_t conn_id) { this->clients_.insert(conn_id); } void add_client_(uint16_t conn_id) { this->clients_.insert(conn_id); }
void remove_client_(uint16_t conn_id) { this->clients_.erase(conn_id); } void remove_client_(uint16_t conn_id) { this->clients_.erase(conn_id); }
void dispatch_callbacks_(CallbackType type, uint16_t conn_id);
std::vector<CallbackEntry> callbacks_;
std::vector<uint8_t> manufacturer_data_{}; std::vector<uint8_t> manufacturer_data_{};
esp_gatt_if_t gatts_if_{0}; esp_gatt_if_t gatts_if_{0};

View File

@@ -14,9 +14,10 @@ Trigger<std::vector<uint8_t>, uint16_t> *BLETriggers::create_characteristic_on_w
BLECharacteristic *characteristic) { BLECharacteristic *characteristic) {
Trigger<std::vector<uint8_t>, uint16_t> *on_write_trigger = // NOLINT(cppcoreguidelines-owning-memory) Trigger<std::vector<uint8_t>, uint16_t> *on_write_trigger = // NOLINT(cppcoreguidelines-owning-memory)
new Trigger<std::vector<uint8_t>, uint16_t>(); new Trigger<std::vector<uint8_t>, uint16_t>();
characteristic->EventEmitter<BLECharacteristicEvt::VectorEvt, std::vector<uint8_t>, uint16_t>::on( characteristic->on_write([on_write_trigger](std::span<const uint8_t> data, uint16_t id) {
BLECharacteristicEvt::VectorEvt::ON_WRITE, // Convert span to vector for trigger
[on_write_trigger](const std::vector<uint8_t> &data, uint16_t id) { on_write_trigger->trigger(data, id); }); on_write_trigger->trigger(std::vector<uint8_t>(data.begin(), data.end()), id);
});
return on_write_trigger; return on_write_trigger;
} }
#endif #endif
@@ -25,9 +26,10 @@ Trigger<std::vector<uint8_t>, uint16_t> *BLETriggers::create_characteristic_on_w
Trigger<std::vector<uint8_t>, uint16_t> *BLETriggers::create_descriptor_on_write_trigger(BLEDescriptor *descriptor) { Trigger<std::vector<uint8_t>, uint16_t> *BLETriggers::create_descriptor_on_write_trigger(BLEDescriptor *descriptor) {
Trigger<std::vector<uint8_t>, uint16_t> *on_write_trigger = // NOLINT(cppcoreguidelines-owning-memory) Trigger<std::vector<uint8_t>, uint16_t> *on_write_trigger = // NOLINT(cppcoreguidelines-owning-memory)
new Trigger<std::vector<uint8_t>, uint16_t>(); new Trigger<std::vector<uint8_t>, uint16_t>();
descriptor->on( descriptor->on_write([on_write_trigger](std::span<const uint8_t> data, uint16_t id) {
BLEDescriptorEvt::VectorEvt::ON_WRITE, // Convert span to vector for trigger
[on_write_trigger](const std::vector<uint8_t> &data, uint16_t id) { on_write_trigger->trigger(data, id); }); on_write_trigger->trigger(std::vector<uint8_t>(data.begin(), data.end()), id);
});
return on_write_trigger; return on_write_trigger;
} }
#endif #endif
@@ -35,8 +37,7 @@ Trigger<std::vector<uint8_t>, uint16_t> *BLETriggers::create_descriptor_on_write
#ifdef USE_ESP32_BLE_SERVER_ON_CONNECT #ifdef USE_ESP32_BLE_SERVER_ON_CONNECT
Trigger<uint16_t> *BLETriggers::create_server_on_connect_trigger(BLEServer *server) { Trigger<uint16_t> *BLETriggers::create_server_on_connect_trigger(BLEServer *server) {
Trigger<uint16_t> *on_connect_trigger = new Trigger<uint16_t>(); // NOLINT(cppcoreguidelines-owning-memory) Trigger<uint16_t> *on_connect_trigger = new Trigger<uint16_t>(); // NOLINT(cppcoreguidelines-owning-memory)
server->on(BLEServerEvt::EmptyEvt::ON_CONNECT, server->on_connect([on_connect_trigger](uint16_t conn_id) { on_connect_trigger->trigger(conn_id); });
[on_connect_trigger](uint16_t conn_id) { on_connect_trigger->trigger(conn_id); });
return on_connect_trigger; return on_connect_trigger;
} }
#endif #endif
@@ -44,38 +45,22 @@ Trigger<uint16_t> *BLETriggers::create_server_on_connect_trigger(BLEServer *serv
#ifdef USE_ESP32_BLE_SERVER_ON_DISCONNECT #ifdef USE_ESP32_BLE_SERVER_ON_DISCONNECT
Trigger<uint16_t> *BLETriggers::create_server_on_disconnect_trigger(BLEServer *server) { Trigger<uint16_t> *BLETriggers::create_server_on_disconnect_trigger(BLEServer *server) {
Trigger<uint16_t> *on_disconnect_trigger = new Trigger<uint16_t>(); // NOLINT(cppcoreguidelines-owning-memory) Trigger<uint16_t> *on_disconnect_trigger = new Trigger<uint16_t>(); // NOLINT(cppcoreguidelines-owning-memory)
server->on(BLEServerEvt::EmptyEvt::ON_DISCONNECT, server->on_disconnect([on_disconnect_trigger](uint16_t conn_id) { on_disconnect_trigger->trigger(conn_id); });
[on_disconnect_trigger](uint16_t conn_id) { on_disconnect_trigger->trigger(conn_id); });
return on_disconnect_trigger; return on_disconnect_trigger;
} }
#endif #endif
#ifdef USE_ESP32_BLE_SERVER_SET_VALUE_ACTION #ifdef USE_ESP32_BLE_SERVER_SET_VALUE_ACTION
void BLECharacteristicSetValueActionManager::set_listener(BLECharacteristic *characteristic, void BLECharacteristicSetValueActionManager::set_listener(BLECharacteristic *characteristic,
EventEmitterListenerID listener_id,
const std::function<void()> &pre_notify_listener) { const std::function<void()> &pre_notify_listener) {
// Find and remove existing listener for this characteristic // Find and remove existing listener for this characteristic
auto *existing = this->find_listener_(characteristic); auto *existing = this->find_listener_(characteristic);
if (existing != nullptr) { if (existing != nullptr) {
// Remove the previous listener
characteristic->EventEmitter<BLECharacteristicEvt::EmptyEvt, uint16_t>::off(BLECharacteristicEvt::EmptyEvt::ON_READ,
existing->listener_id);
// Remove the pre-notify listener
this->off(BLECharacteristicSetValueActionEvt::PRE_NOTIFY, existing->pre_notify_listener_id);
// Remove from vector // Remove from vector
this->remove_listener_(characteristic); this->remove_listener_(characteristic);
} }
// Create a new listener for the pre-notify event
EventEmitterListenerID pre_notify_listener_id =
this->on(BLECharacteristicSetValueActionEvt::PRE_NOTIFY,
[pre_notify_listener, characteristic](const BLECharacteristic *evt_characteristic) {
// Only call the pre-notify listener if the characteristic is the one we are interested in
if (characteristic == evt_characteristic) {
pre_notify_listener();
}
});
// Save the entry to the vector // Save the entry to the vector
this->listeners_.push_back({characteristic, listener_id, pre_notify_listener_id}); this->listeners_.push_back({characteristic, pre_notify_listener});
} }
BLECharacteristicSetValueActionManager::ListenerEntry *BLECharacteristicSetValueActionManager::find_listener_( BLECharacteristicSetValueActionManager::ListenerEntry *BLECharacteristicSetValueActionManager::find_listener_(

View File

@@ -4,7 +4,6 @@
#include "ble_characteristic.h" #include "ble_characteristic.h"
#include "ble_descriptor.h" #include "ble_descriptor.h"
#include "esphome/components/event_emitter/event_emitter.h"
#include "esphome/core/automation.h" #include "esphome/core/automation.h"
#include <vector> #include <vector>
@@ -18,10 +17,6 @@ namespace esp32_ble_server {
namespace esp32_ble_server_automations { namespace esp32_ble_server_automations {
using namespace esp32_ble; using namespace esp32_ble;
using namespace event_emitter;
// Invalid listener ID constant - 0 is used as sentinel value in EventEmitter
static constexpr EventEmitterListenerID INVALID_LISTENER_ID = 0;
class BLETriggers { class BLETriggers {
public: public:
@@ -41,38 +36,29 @@ class BLETriggers {
}; };
#ifdef USE_ESP32_BLE_SERVER_SET_VALUE_ACTION #ifdef USE_ESP32_BLE_SERVER_SET_VALUE_ACTION
enum BLECharacteristicSetValueActionEvt {
PRE_NOTIFY,
};
// Class to make sure only one BLECharacteristicSetValueAction is active at a time for each characteristic // Class to make sure only one BLECharacteristicSetValueAction is active at a time for each characteristic
class BLECharacteristicSetValueActionManager class BLECharacteristicSetValueActionManager {
: public EventEmitter<BLECharacteristicSetValueActionEvt, BLECharacteristic *> {
public: public:
// Singleton pattern // Singleton pattern
static BLECharacteristicSetValueActionManager *get_instance() { static BLECharacteristicSetValueActionManager *get_instance() {
static BLECharacteristicSetValueActionManager instance; static BLECharacteristicSetValueActionManager instance;
return &instance; return &instance;
} }
void set_listener(BLECharacteristic *characteristic, EventEmitterListenerID listener_id, void set_listener(BLECharacteristic *characteristic, const std::function<void()> &pre_notify_listener);
const std::function<void()> &pre_notify_listener); bool has_listener(BLECharacteristic *characteristic) { return this->find_listener_(characteristic) != nullptr; }
EventEmitterListenerID get_listener(BLECharacteristic *characteristic) { void emit_pre_notify(BLECharacteristic *characteristic) {
for (const auto &entry : this->listeners_) { for (const auto &entry : this->listeners_) {
if (entry.characteristic == characteristic) { if (entry.characteristic == characteristic) {
return entry.listener_id; entry.pre_notify_listener();
break;
} }
} }
return INVALID_LISTENER_ID;
}
void emit_pre_notify(BLECharacteristic *characteristic) {
this->emit_(BLECharacteristicSetValueActionEvt::PRE_NOTIFY, characteristic);
} }
private: private:
struct ListenerEntry { struct ListenerEntry {
BLECharacteristic *characteristic; BLECharacteristic *characteristic;
EventEmitterListenerID listener_id; std::function<void()> pre_notify_listener;
EventEmitterListenerID pre_notify_listener_id;
}; };
std::vector<ListenerEntry> listeners_; std::vector<ListenerEntry> listeners_;
@@ -87,24 +73,22 @@ template<typename... Ts> class BLECharacteristicSetValueAction : public Action<T
void set_buffer(ByteBuffer buffer) { this->set_buffer(buffer.get_data()); } void set_buffer(ByteBuffer buffer) { this->set_buffer(buffer.get_data()); }
void play(Ts... x) override { void play(Ts... x) override {
// If the listener is already set, do nothing // If the listener is already set, do nothing
if (BLECharacteristicSetValueActionManager::get_instance()->get_listener(this->parent_) == this->listener_id_) if (BLECharacteristicSetValueActionManager::get_instance()->has_listener(this->parent_))
return; return;
// Set initial value // Set initial value
this->parent_->set_value(this->buffer_.value(x...)); this->parent_->set_value(this->buffer_.value(x...));
// Set the listener for read events // Set the listener for read events
this->listener_id_ = this->parent_->EventEmitter<BLECharacteristicEvt::EmptyEvt, uint16_t>::on( this->parent_->on_read([this, x...](uint16_t id) {
BLECharacteristicEvt::EmptyEvt::ON_READ, [this, x...](uint16_t id) { // Set the value of the characteristic every time it is read
// Set the value of the characteristic every time it is read this->parent_->set_value(this->buffer_.value(x...));
this->parent_->set_value(this->buffer_.value(x...)); });
});
// Set the listener in the global manager so only one BLECharacteristicSetValueAction is set for each characteristic // Set the listener in the global manager so only one BLECharacteristicSetValueAction is set for each characteristic
BLECharacteristicSetValueActionManager::get_instance()->set_listener( BLECharacteristicSetValueActionManager::get_instance()->set_listener(
this->parent_, this->listener_id_, [this, x...]() { this->parent_->set_value(this->buffer_.value(x...)); }); this->parent_, [this, x...]() { this->parent_->set_value(this->buffer_.value(x...)); });
} }
protected: protected:
BLECharacteristic *parent_; BLECharacteristic *parent_;
EventEmitterListenerID listener_id_;
}; };
#endif // USE_ESP32_BLE_SERVER_SET_VALUE_ACTION #endif // USE_ESP32_BLE_SERVER_SET_VALUE_ACTION

View File

@@ -38,8 +38,7 @@ void ESP32ImprovComponent::setup() {
}); });
} }
#endif #endif
global_ble_server->on(BLEServerEvt::EmptyEvt::ON_DISCONNECT, global_ble_server->on_disconnect([this](uint16_t conn_id) { this->set_error_(improv::ERROR_NONE); });
[this](uint16_t conn_id) { this->set_error_(improv::ERROR_NONE); });
// Start with loop disabled - will be enabled by start() when needed // Start with loop disabled - will be enabled by start() when needed
this->disable_loop(); this->disable_loop();
@@ -57,12 +56,11 @@ void ESP32ImprovComponent::setup_characteristics() {
this->error_->add_descriptor(error_descriptor); this->error_->add_descriptor(error_descriptor);
this->rpc_ = this->service_->create_characteristic(improv::RPC_COMMAND_UUID, BLECharacteristic::PROPERTY_WRITE); this->rpc_ = this->service_->create_characteristic(improv::RPC_COMMAND_UUID, BLECharacteristic::PROPERTY_WRITE);
this->rpc_->EventEmitter<BLECharacteristicEvt::VectorEvt, std::vector<uint8_t>, uint16_t>::on( this->rpc_->on_write([this](std::span<const uint8_t> data, uint16_t id) {
BLECharacteristicEvt::VectorEvt::ON_WRITE, [this](const std::vector<uint8_t> &data, uint16_t id) { if (!data.empty()) {
if (!data.empty()) { this->incoming_data_.insert(this->incoming_data_.end(), data.begin(), data.end());
this->incoming_data_.insert(this->incoming_data_.end(), data.begin(), data.end()); }
} });
});
BLEDescriptor *rpc_descriptor = new BLE2902(); BLEDescriptor *rpc_descriptor = new BLE2902();
this->rpc_->add_descriptor(rpc_descriptor); this->rpc_->add_descriptor(rpc_descriptor);

View File

@@ -1,5 +0,0 @@
CODEOWNERS = ["@Rapsssito"]
# Allows event_emitter to be configured in yaml, to allow use of the C++ api.
CONFIG_SCHEMA = {}

View File

@@ -1,117 +0,0 @@
#pragma once
#include <vector>
#include <functional>
#include <limits>
#include "esphome/core/log.h"
namespace esphome {
namespace event_emitter {
using EventEmitterListenerID = uint32_t;
static constexpr EventEmitterListenerID INVALID_LISTENER_ID = 0;
// EventEmitter class that can emit events with a specific name (it is highly recommended to use an enum class for this)
// and a list of arguments. Supports multiple listeners for each event.
template<typename EvtType, typename... Args> class EventEmitter {
public:
EventEmitterListenerID on(EvtType event, std::function<void(Args...)> listener) {
EventEmitterListenerID listener_id = this->get_next_id_();
// Find or create event entry
EventEntry *entry = this->find_or_create_event_(event);
entry->listeners.push_back({listener_id, listener});
return listener_id;
}
void off(EvtType event, EventEmitterListenerID id) {
EventEntry *entry = this->find_event_(event);
if (entry == nullptr)
return;
// Remove listener with given id
for (auto it = entry->listeners.begin(); it != entry->listeners.end(); ++it) {
if (it->id == id) {
// Swap with last and pop for efficient removal
*it = entry->listeners.back();
entry->listeners.pop_back();
// Remove event entry if no more listeners
if (entry->listeners.empty()) {
this->remove_event_(event);
}
return;
}
}
}
protected:
void emit_(EvtType event, Args... args) {
EventEntry *entry = this->find_event_(event);
if (entry == nullptr)
return;
// Call all listeners for this event
for (const auto &listener : entry->listeners) {
listener.callback(args...);
}
}
private:
struct Listener {
EventEmitterListenerID id;
std::function<void(Args...)> callback;
};
struct EventEntry {
EvtType event;
std::vector<Listener> listeners;
};
EventEmitterListenerID get_next_id_() {
// Simple incrementing ID, wrapping around at max
EventEmitterListenerID next_id = (this->current_id_ + 1);
if (next_id == INVALID_LISTENER_ID) {
next_id = 1;
}
this->current_id_ = next_id;
return this->current_id_;
}
EventEntry *find_event_(EvtType event) {
for (auto &entry : this->events_) {
if (entry.event == event) {
return &entry;
}
}
return nullptr;
}
EventEntry *find_or_create_event_(EvtType event) {
EventEntry *entry = this->find_event_(event);
if (entry != nullptr)
return entry;
// Create new event entry
this->events_.push_back({event, {}});
return &this->events_.back();
}
void remove_event_(EvtType event) {
for (auto it = this->events_.begin(); it != this->events_.end(); ++it) {
if (it->event == event) {
// Swap with last and pop
*it = this->events_.back();
this->events_.pop_back();
return;
}
}
}
std::vector<EventEntry> events_;
EventEmitterListenerID current_id_ = 0;
};
} // namespace event_emitter
} // namespace esphome