Add connect, disconnect and fix notify

This commit is contained in:
Rapsssito 2024-10-19 20:00:19 +02:00
parent b422fda8bf
commit c3b29fbfab
12 changed files with 143 additions and 84 deletions

View file

@ -11,6 +11,8 @@ from esphome.const import (
CONF_SERVICES, CONF_SERVICES,
CONF_VALUE, CONF_VALUE,
CONF_NOTIFY, CONF_NOTIFY,
CONF_ON_CONNECT,
CONF_ON_DISCONNECT,
) )
from esphome.core import CORE from esphome.core import CORE
@ -142,7 +144,7 @@ def create_description_cud(char_config):
def create_notify_cccd(char_config): def create_notify_cccd(char_config):
if not char_config[CONF_NOTIFY]: if not char_config[CONF_NOTIFY] and not char_config[CONF_INDICATE]:
return char_config return char_config
# If the CCCD descriptor is already present, return the config # If the CCCD descriptor is already present, return the config
for desc in char_config[CONF_DESCRIPTORS]: for desc in char_config[CONF_DESCRIPTORS]:
@ -206,9 +208,7 @@ DESCRIPTOR_SCHEMA = cv.Schema(
cv.Required(CONF_UUID): cv.Any(bt_uuid, cv.hex_uint32_t), cv.Required(CONF_UUID): cv.Any(bt_uuid, cv.hex_uint32_t),
cv.Optional(CONF_READ, default=True): cv.boolean, cv.Optional(CONF_READ, default=True): cv.boolean,
cv.Optional(CONF_WRITE, default=True): cv.boolean, cv.Optional(CONF_WRITE, default=True): cv.boolean,
cv.Optional(CONF_ON_WRITE): automation.validate_automation( cv.Optional(CONF_ON_WRITE): automation.validate_automation(single=True),
{cv.GenerateID(): cv.declare_id(BLEDescriptor)}, single=True
),
cv.Required(CONF_VALUE): VALUE_SCHEMA, cv.Required(CONF_VALUE): VALUE_SCHEMA,
}, },
extra_schemas=[validate_descritor_value_length, validate_desc_on_write], extra_schemas=[validate_descritor_value_length, validate_desc_on_write],
@ -226,9 +226,7 @@ SERVICE_CHARACTERISTIC_SCHEMA = (
cv.Optional(CONF_DESCRIPTORS, default=[]): cv.ensure_list( cv.Optional(CONF_DESCRIPTORS, default=[]): cv.ensure_list(
DESCRIPTOR_SCHEMA DESCRIPTOR_SCHEMA
), ),
cv.Optional(CONF_ON_WRITE): automation.validate_automation( cv.Optional(CONF_ON_WRITE): automation.validate_automation(single=True),
{cv.GenerateID(): cv.declare_id(BLECharacteristic)}, single=True
),
cv.Optional(CONF_DESCRIPTION): cv.string, cv.Optional(CONF_DESCRIPTION): cv.string,
cv.GenerateID(CONF_CUD_ID_): cv.declare_id(BLEDescriptor), cv.GenerateID(CONF_CUD_ID_): cv.declare_id(BLEDescriptor),
cv.GenerateID(CONF_CUD_VALUE_BUFFER_): cv.declare_id(ByteBuffer), cv.GenerateID(CONF_CUD_VALUE_BUFFER_): cv.declare_id(ByteBuffer),
@ -264,6 +262,8 @@ CONFIG_SCHEMA = cv.Schema(
cv.Optional(CONF_MANUFACTURER_DATA): cv.Schema([cv.uint8_t]), cv.Optional(CONF_MANUFACTURER_DATA): cv.Schema([cv.uint8_t]),
cv.Optional(CONF_MODEL): cv.string, cv.Optional(CONF_MODEL): cv.string,
cv.Optional(CONF_SERVICES, default=[]): cv.ensure_list(SERVICE_SCHEMA), cv.Optional(CONF_SERVICES, default=[]): cv.ensure_list(SERVICE_SCHEMA),
cv.Optional(CONF_ON_CONNECT): automation.validate_automation(single=True),
cv.Optional(CONF_ON_DISCONNECT): automation.validate_automation(single=True),
} }
).extend(cv.COMPONENT_SCHEMA) ).extend(cv.COMPONENT_SCHEMA)
@ -380,7 +380,7 @@ async def to_code_descriptor(descriptor_conf, char_var):
on_write_conf = descriptor_conf[CONF_ON_WRITE] on_write_conf = descriptor_conf[CONF_ON_WRITE]
await automation.build_automation( await automation.build_automation(
BLETriggers_ns.create_descriptor_on_write_trigger(desc_var), BLETriggers_ns.create_descriptor_on_write_trigger(desc_var),
[(cg.std_vector.template(cg.uint8), "x")], [(cg.std_vector.template(cg.uint8), "x"), (cg.uint16, "id")],
on_write_conf, on_write_conf,
) )
@ -397,7 +397,7 @@ async def to_code_characteristic(service_var, char_conf):
on_write_conf = char_conf[CONF_ON_WRITE] on_write_conf = char_conf[CONF_ON_WRITE]
await automation.build_automation( await automation.build_automation(
BLETriggers_ns.create_characteristic_on_write_trigger(char_var), BLETriggers_ns.create_characteristic_on_write_trigger(char_var),
[(cg.std_vector.template(cg.uint8), "x")], [(cg.std_vector.template(cg.uint8), "x"), (cg.uint16, "id")],
on_write_conf, on_write_conf,
) )
if CONF_VALUE in char_conf: if CONF_VALUE in char_conf:
@ -447,6 +447,18 @@ async def to_code(config):
for char_conf in service_config[CONF_CHARACTERISTICS]: for char_conf in service_config[CONF_CHARACTERISTICS]:
await to_code_characteristic(service_var, char_conf) await to_code_characteristic(service_var, char_conf)
cg.add(var.enqueue_start_service(service_var)) cg.add(var.enqueue_start_service(service_var))
if CONF_ON_CONNECT in config:
await automation.build_automation(
BLETriggers_ns.create_server_on_connect_trigger(var),
[(cg.uint16, "id")],
config[CONF_ON_CONNECT],
)
if CONF_ON_DISCONNECT in config:
await automation.build_automation(
BLETriggers_ns.create_server_on_disconnect_trigger(var),
[(cg.uint16, "id")],
config[CONF_ON_DISCONNECT],
)
cg.add_define("USE_ESP32_BLE_SERVER") cg.add_define("USE_ESP32_BLE_SERVER")
if CORE.using_esp_idf: if CORE.using_esp_idf:
add_idf_sdkconfig_option("CONFIG_BT_ENABLED", True) add_idf_sdkconfig_option("CONFIG_BT_ENABLED", True)

View file

@ -38,19 +38,25 @@ void BLECharacteristic::set_value(ByteBuffer buffer) {
xSemaphoreGive(this->set_value_lock_); xSemaphoreGive(this->set_value_lock_);
} }
void BLECharacteristic::notify(bool require_ack) { void BLECharacteristic::notify() {
if (require_ack) {
ESP_LOGW(TAG, "require_ack=true is not yet supported (i.e. INDICATE is not yet supported)");
// TODO: Handle when require_ack=true
}
if (this->service_ == nullptr || this->service_->get_server() == nullptr || if (this->service_ == nullptr || this->service_->get_server() == nullptr ||
this->service_->get_server()->get_connected_client_count() == 0) this->service_->get_server()->get_connected_client_count() == 0)
return; return;
for (auto &client : this->service_->get_server()->get_clients()) { for (auto &client : this->service_->get_server()->get_clients()) {
size_t length = this->value_.size(); size_t length = this->value_.size();
esp_err_t err = esp_ble_gatts_send_indicate(this->service_->get_server()->get_gatts_if(), client.first, // If the client is not in the list of clients to notify, skip it
this->handle_, length, this->value_.data(), false); if (this->clients_to_notify_.count(client) == 0)
continue;
// If the client is in the list of clients to notify, check if it requires an ack (i.e. INDICATE)
bool require_ack = this->clients_to_notify_[client];
// TODO: Remove this block when INDICATE acknowledgment is supported
if (require_ack) {
ESP_LOGW(TAG, "INDICATE acknowledgment is not yet supported (i.e. it works as a NOTIFY)");
require_ack = false;
}
esp_err_t err = esp_ble_gatts_send_indicate(this->service_->get_server()->get_gatts_if(), client,
this->handle_, length, this->value_.data(), require_ack);
if (err != ESP_OK) { if (err != ESP_OK) {
ESP_LOGE(TAG, "esp_ble_gatts_send_indicate failed %d", err); ESP_LOGE(TAG, "esp_ble_gatts_send_indicate failed %d", err);
return; return;
@ -58,7 +64,27 @@ void BLECharacteristic::notify(bool require_ack) {
} }
} }
void BLECharacteristic::add_descriptor(BLEDescriptor *descriptor) { this->descriptors_.push_back(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 (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) {
if (value.size() != 2)
return;
uint16_t cccd = (value[1] << 8) | value[0];
bool notify = (cccd & 1) != 0;
bool indicate = (cccd & 2) != 0;
if (notify || indicate) {
this->clients_to_notify_[conn_id] = indicate;
} else {
this->clients_to_notify_.erase(conn_id);
}
}
);
}
this->descriptors_.push_back(descriptor);
}
void BLECharacteristic::remove_descriptor(BLEDescriptor *descriptor) { void BLECharacteristic::remove_descriptor(BLEDescriptor *descriptor) {
this->descriptors_.erase(std::remove(this->descriptors_.begin(), this->descriptors_.end(), descriptor), this->descriptors_.erase(std::remove(this->descriptors_.begin(), this->descriptors_.end(), descriptor),
@ -178,7 +204,8 @@ 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>::emit_(BLECharacteristicEvt::EmptyEvt::ON_READ); this->EventEmitter<BLECharacteristicEvt::EmptyEvt, uint16_t>::emit_(
BLECharacteristicEvt::EmptyEvt::ON_READ, param->read.conn_id);
uint16_t max_offset = 22; uint16_t max_offset = 22;
@ -246,8 +273,8 @@ 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>>::emit_( this->EventEmitter<BLECharacteristicEvt::VectorEvt, std::vector<uint8_t>, uint16_t>::emit_(
BLECharacteristicEvt::VectorEvt::ON_WRITE, this->value_); BLECharacteristicEvt::VectorEvt::ON_WRITE, this->value_, param->write.conn_id);
} }
break; break;
@ -258,8 +285,8 @@ 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>>::emit_( this->EventEmitter<BLECharacteristicEvt::VectorEvt, std::vector<uint8_t>, uint16_t>::emit_(
BLECharacteristicEvt::VectorEvt::ON_WRITE, this->value_); BLECharacteristicEvt::VectorEvt::ON_WRITE, 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

@ -6,6 +6,7 @@
#include "esphome/core/bytebuffer.h" #include "esphome/core/bytebuffer.h"
#include <vector> #include <vector>
#include <unordered_map>
#ifdef USE_ESP32 #ifdef USE_ESP32
@ -34,8 +35,8 @@ enum EmptyEvt {
}; };
} // namespace BLECharacteristicEvt } // namespace BLECharacteristicEvt
class BLECharacteristic : public EventEmitter<BLECharacteristicEvt::VectorEvt, std::vector<uint8_t>>, class BLECharacteristic : public EventEmitter<BLECharacteristicEvt::VectorEvt, std::vector<uint8_t>, uint16_t>,
public EventEmitter<BLECharacteristicEvt::EmptyEvt> { public EventEmitter<BLECharacteristicEvt::EmptyEvt, uint16_t> {
public: public:
BLECharacteristic(ESPBTUUID uuid, uint32_t properties); BLECharacteristic(ESPBTUUID uuid, uint32_t properties);
~BLECharacteristic(); ~BLECharacteristic();
@ -49,10 +50,10 @@ class BLECharacteristic : public EventEmitter<BLECharacteristicEvt::VectorEvt, s
void set_write_property(bool value); void set_write_property(bool value);
void set_write_no_response_property(bool value); void set_write_no_response_property(bool value);
void indicate() { this->notify(true); } void notify();
void notify(bool require_ack = false);
void do_create(BLEService *service); void do_create(BLEService *service);
void do_delete() { this->clients_to_notify_.clear(); }
void gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param); void gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param);
void add_descriptor(BLEDescriptor *descriptor); void add_descriptor(BLEDescriptor *descriptor);
@ -84,6 +85,7 @@ class BLECharacteristic : public EventEmitter<BLECharacteristicEvt::VectorEvt, s
SemaphoreHandle_t set_value_lock_; SemaphoreHandle_t set_value_lock_;
std::vector<BLEDescriptor *> descriptors_; std::vector<BLEDescriptor *> descriptors_;
std::unordered_map<uint16_t, bool> clients_to_notify_;
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;

View file

@ -72,9 +72,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->EventEmitter<BLEDescriptorEvt::VectorEvt, std::vector<uint8_t>>::emit_( this->emit_(
BLEDescriptorEvt::VectorEvt::ON_WRITE, BLEDescriptorEvt::VectorEvt::ON_WRITE,
std::vector<uint8_t>(param->write.value, param->write.value + param->write.len) std::vector<uint8_t>(param->write.value, param->write.value + param->write.len),
param->write.conn_id
); );
break; break;
} }

View file

@ -20,13 +20,14 @@ namespace BLEDescriptorEvt {
enum VectorEvt { enum VectorEvt {
ON_WRITE, ON_WRITE,
}; };
} // namespace BLECharacteristicEvt } // namespace BLEDescriptorEvt
class BLEDescriptor : public EventEmitter<BLEDescriptorEvt::VectorEvt, std::vector<uint8_t>> { 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();
void do_create(BLECharacteristic *characteristic); void do_create(BLECharacteristic *characteristic);
ESPBTUUID get_uuid() const { return this->uuid_; }
void set_value(ByteBuffer buffer); void set_value(ByteBuffer buffer);

View file

@ -182,21 +182,15 @@ void BLEServer::gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t ga
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, (void *) this); this->add_client_(param->connect.conn_id);
this->connected_clients_++; this->emit_(BLEServerEvt::EmptyEvt::ON_CONNECT, param->connect.conn_id);
for (auto &pair : this->services_) {
pair.second->emit_client_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");
if (this->remove_client_(param->disconnect.conn_id)) this->remove_client_(param->disconnect.conn_id);
this->connected_clients_--;
this->parent_->advertising_start(); this->parent_->advertising_start();
for (auto &pair : this->services_) { this->emit_(BLEServerEvt::EmptyEvt::ON_DISCONNECT, param->disconnect.conn_id);
pair.second->emit_client_disconnect(param->disconnect.conn_id);
}
break; break;
} }
case ESP_GATTS_REG_EVT: { case ESP_GATTS_REG_EVT: {

View file

@ -15,6 +15,7 @@
#include <memory> #include <memory>
#include <vector> #include <vector>
#include <unordered_map> #include <unordered_map>
#include <unordered_set>
#ifdef USE_ESP32 #ifdef USE_ESP32
@ -26,7 +27,15 @@ namespace esp32_ble_server {
using namespace esp32_ble; using namespace esp32_ble;
class BLEServer : public Component, public GATTsEventHandler, public BLEStatusEventHandler, public Parented<ESP32BLE> { namespace BLEServerEvt {
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;
@ -50,8 +59,8 @@ class BLEServer : public Component, public GATTsEventHandler, public BLEStatusEv
void enqueue_start_service(BLEService *service) { this->services_to_start_.push_back(service); } void enqueue_start_service(BLEService *service) { this->services_to_start_.push_back(service); }
esp_gatt_if_t get_gatts_if() { return this->gatts_if_; } esp_gatt_if_t get_gatts_if() { return this->gatts_if_; }
uint32_t get_connected_client_count() { return this->connected_clients_; } uint32_t get_connected_client_count() { return this->clients_.size(); }
const std::unordered_map<uint16_t, void *> &get_clients() { return this->clients_; } const std::unordered_set<uint16_t> &get_clients() { return this->clients_; }
void gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, void gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if,
esp_ble_gatts_cb_param_t *param) override; esp_ble_gatts_cb_param_t *param) override;
@ -63,8 +72,8 @@ class BLEServer : public Component, public GATTsEventHandler, public BLEStatusEv
bool create_device_characteristics_(); bool create_device_characteristics_();
void restart_advertising_(); void restart_advertising_();
void add_client_(uint16_t conn_id, void *client) { this->clients_.emplace(conn_id, client); } void add_client_(uint16_t conn_id) { this->clients_.insert(conn_id); }
bool remove_client_(uint16_t conn_id) { return this->clients_.erase(conn_id) > 0; } void remove_client_(uint16_t conn_id) { this->clients_.erase(conn_id); }
std::string manufacturer_; std::string manufacturer_;
optional<std::string> model_; optional<std::string> model_;
@ -72,8 +81,7 @@ class BLEServer : public Component, public GATTsEventHandler, public BLEStatusEv
esp_gatt_if_t gatts_if_{0}; esp_gatt_if_t gatts_if_{0};
bool registered_{false}; bool registered_{false};
uint32_t connected_clients_{0}; std::unordered_set<uint16_t> clients_;
std::unordered_map<uint16_t, void *> clients_;
std::unordered_map<std::string, BLEService *> services_; std::unordered_map<std::string, BLEService *> services_;
std::vector<BLEService *> services_to_start_; std::vector<BLEService *> services_to_start_;
BLEService *device_information_service_; BLEService *device_information_service_;

View file

@ -9,24 +9,36 @@ namespace esp32_ble_server_automations {
using namespace esp32_ble; using namespace esp32_ble;
Trigger<std::vector<uint8_t>> *BLETriggers::create_characteristic_on_write_trigger(BLECharacteristic *characteristic) { Trigger<std::vector<uint8_t>, uint16_t> *BLETriggers::create_characteristic_on_write_trigger(BLECharacteristic *characteristic) {
Trigger<std::vector<uint8_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>>(); new Trigger<std::vector<uint8_t>, uint16_t>();
characteristic->EventEmitter<BLECharacteristicEvt::VectorEvt, std::vector<uint8_t>>::on( characteristic->EventEmitter<BLECharacteristicEvt::VectorEvt, std::vector<uint8_t>, uint16_t>::on(
BLECharacteristicEvt::VectorEvt::ON_WRITE, BLECharacteristicEvt::VectorEvt::ON_WRITE,
[on_write_trigger](const std::vector<uint8_t> &data) { on_write_trigger->trigger(data); }); [on_write_trigger](const std::vector<uint8_t> &data, uint16_t id) { on_write_trigger->trigger(data, id); });
return on_write_trigger; return on_write_trigger;
} }
Trigger<std::vector<uint8_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>> *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>>(); new Trigger<std::vector<uint8_t>, uint16_t>();
descriptor->EventEmitter<BLEDescriptorEvt::VectorEvt, std::vector<uint8_t>>::on( descriptor->on(
BLEDescriptorEvt::VectorEvt::ON_WRITE, BLEDescriptorEvt::VectorEvt::ON_WRITE,
[on_write_trigger](const std::vector<uint8_t> &data) { on_write_trigger->trigger(data); }); [on_write_trigger](const std::vector<uint8_t> &data, uint16_t id) { on_write_trigger->trigger(data, id); });
return on_write_trigger; return on_write_trigger;
} }
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)
server->on(BLEServerEvt::EmptyEvt::ON_CONNECT, [on_connect_trigger](uint16_t conn_id) { on_connect_trigger->trigger(conn_id); });
return on_connect_trigger;
}
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)
server->on(BLEServerEvt::EmptyEvt::ON_DISCONNECT, [on_disconnect_trigger](uint16_t conn_id) { on_disconnect_trigger->trigger(conn_id); });
return on_disconnect_trigger;
}
void BLECharacteristicSetValueActionManager::set_listener(BLECharacteristic *characteristic, void BLECharacteristicSetValueActionManager::set_listener(BLECharacteristic *characteristic,
EventEmitterListenerID listener_id, EventEmitterListenerID listener_id,
const std::function<void()> &pre_notify_listener) { const std::function<void()> &pre_notify_listener) {
@ -37,7 +49,7 @@ void BLECharacteristicSetValueActionManager::set_listener(BLECharacteristic *cha
EventEmitterListenerID old_listener_id = listener_pairs.first; EventEmitterListenerID old_listener_id = listener_pairs.first;
EventEmitterListenerID old_pre_notify_listener_id = listener_pairs.second; EventEmitterListenerID old_pre_notify_listener_id = listener_pairs.second;
// Remove the previous listener // Remove the previous listener
characteristic->EventEmitter<BLECharacteristicEvt::EmptyEvt>::off(BLECharacteristicEvt::EmptyEvt::ON_READ, characteristic->EventEmitter<BLECharacteristicEvt::EmptyEvt, uint16_t>::off(BLECharacteristicEvt::EmptyEvt::ON_READ,
old_listener_id); old_listener_id);
// Remove the pre-notify listener // Remove the pre-notify listener
this->off(BLECharacteristicSetValueActionEvt::PRE_NOTIFY, old_pre_notify_listener_id); this->off(BLECharacteristicSetValueActionEvt::PRE_NOTIFY, old_pre_notify_listener_id);

View file

@ -1,6 +1,8 @@
#pragma once #pragma once
#include "ble_server.h"
#include "ble_characteristic.h" #include "ble_characteristic.h"
#include "ble_descriptor.h"
#include "esphome/core/event_emitter.h" #include "esphome/core/event_emitter.h"
#include "esphome/core/automation.h" #include "esphome/core/automation.h"
@ -21,8 +23,10 @@ using namespace esp32_ble;
class BLETriggers { class BLETriggers {
public: public:
static Trigger<std::vector<uint8_t>> *create_characteristic_on_write_trigger(BLECharacteristic *characteristic); static Trigger<std::vector<uint8_t>, uint16_t> *create_characteristic_on_write_trigger(BLECharacteristic *characteristic);
static Trigger<std::vector<uint8_t>> *create_descriptor_on_write_trigger(BLEDescriptor *descriptor); static Trigger<std::vector<uint8_t>, uint16_t> *create_descriptor_on_write_trigger(BLEDescriptor *descriptor);
static Trigger<uint16_t> *create_server_on_connect_trigger(BLEServer *server);
static Trigger<uint16_t> *create_server_on_disconnect_trigger(BLEServer *server);
}; };
enum BLECharacteristicSetValueActionEvt { enum BLECharacteristicSetValueActionEvt {
@ -62,8 +66,8 @@ template<typename... Ts> class BLECharacteristicSetValueAction : public Action<T
// 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>::on( this->listener_id_ = this->parent_->EventEmitter<BLECharacteristicEvt::EmptyEvt, uint16_t>::on(
BLECharacteristicEvt::EmptyEvt::ON_READ, [this, x...]() { 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...));
}); });

View file

@ -58,22 +58,15 @@ void BLEService::do_create(BLEServer *server) {
this->state_ = CREATING; this->state_ = CREATING;
} }
void BLEService::emit_client_connect(const uint16_t conn_id) {
if (this->on_client_connect_ && this->is_running())
this->on_client_connect_(conn_id);
}
void BLEService::emit_client_disconnect(const uint16_t conn_id) {
if (this->on_client_disconnect_ && this->is_running())
this->on_client_disconnect_(conn_id);
}
void BLEService::do_delete() { void BLEService::do_delete() {
if (this->state_ == DELETING || this->state_ == DELETED) if (this->state_ == DELETING || this->state_ == DELETED)
return; return;
this->state_ = DELETING; this->state_ = DELETING;
this->created_characteristic_count_ = 0; this->created_characteristic_count_ = 0;
this->last_created_characteristic_ = nullptr; this->last_created_characteristic_ = nullptr;
// Call all characteristics to delete
for (auto *characteristic : this->characteristics_)
characteristic->do_delete();
this->stop_(); this->stop_();
esp_err_t err = esp_ble_gatts_delete_service(this->handle_); esp_err_t err = esp_ble_gatts_delete_service(this->handle_);
if (err != ESP_OK) { if (err != ESP_OK) {

View file

@ -50,10 +50,6 @@ class BLEService {
bool is_running() { return this->state_ == RUNNING; } bool is_running() { return this->state_ == RUNNING; }
bool is_starting() { return this->state_ == STARTING; } bool is_starting() { return this->state_ == STARTING; }
bool is_deleted() { return this->state_ == DELETED; } bool is_deleted() { return this->state_ == DELETED; }
void on_client_connect(const std::function<void(const uint16_t)> &&func) { this->on_client_connect_ = func; }
void on_client_disconnect(const std::function<void(const uint16_t)> &&func) { this->on_client_disconnect_ = func; }
void emit_client_connect(uint16_t conn_id);
void emit_client_disconnect(uint16_t conn_id);
protected: protected:
std::vector<BLECharacteristic *> characteristics_; std::vector<BLECharacteristic *> characteristics_;
@ -66,8 +62,6 @@ class BLEService {
uint8_t inst_id_; uint8_t inst_id_;
bool advertise_{false}; bool advertise_{false};
bool should_start_{false}; bool should_start_{false};
std::function<void(const uint16_t)> on_client_connect_;
std::function<void(const uint16_t)> on_client_disconnect_;
bool do_create_characteristics_(); bool do_create_characteristics_();
void stop_(); void stop_();

View file

@ -3,15 +3,22 @@ esp32_ble_server:
manufacturer_data: [0x72, 0x4, 0x00, 0x23] manufacturer_data: [0x72, 0x4, 0x00, 0x23]
manufacturer: ESPHome manufacturer: ESPHome
model: Test model: Test
on_connect:
- lambda: |-
ESP_LOGD("BLE", "Connection from %d", id);
on_disconnect:
- lambda: |-
ESP_LOGD("BLE", "Disconnection from %d", id);
services: services:
- uuid: 2a24b789-7aab-4535-af3e-ee76a35cc42d - uuid: 2a24b789-7aab-4535-af3e-ee76a35cc12d
advertise: false advertise: false
characteristics: characteristics:
- id: test_notify_characteristic - id: test_notify_characteristic
description: "Notify characteristic"
uuid: cad48e28-7fbe-41cf-bae9-d77a6c233423 uuid: cad48e28-7fbe-41cf-bae9-d77a6c233423
read: true read: true
notify: true notify: true
value: [0, 1, 2] value: [9, 9, 9]
descriptors: descriptors:
- uuid: cad48e28-7fbe-41cf-bae9-d77a6c111111 - uuid: cad48e28-7fbe-41cf-bae9-d77a6c111111
value: 123.1 value: 123.1
@ -20,18 +27,22 @@ esp32_ble_server:
advertise: false advertise: false
characteristics: characteristics:
- id: test_change_characteristic - id: test_change_characteristic
uuid: 2a24b789-7a1b-4535-af3e-ee76a35cc11d uuid: 2a24b789-7a1b-4535-af3e-ee76a35cc11c
read: true read: true
value: "Initial" value: "Initial"
description: "Change characteristic"
descriptors: descriptors:
- uuid: cad48e28-7fbe-41cf-bae9-d77a6c111111 - uuid: 0x2312
value: 0x12 value: 0x12
- uuid: 2a24b789-7a1b-4535-af3e-ee76a35cc12d on_write:
- lambda: |-
ESP_LOGD("BLE", "Descriptor received: %s from %d", std::string(x.begin(), x.end()).c_str(), id);
- uuid: 2a24b789-7a1b-4535-af3e-ee76a35cc99a
write: true write: true
on_write: on_write:
then: then:
- lambda: |- - lambda: |-
ESP_LOGD("BLE", "Received: %s", std::string(x.begin(), x.end()).c_str()); ESP_LOGD("BLE", "Characteristic received: %s from %d", std::string(x.begin(), x.end()).c_str(), id);
- ble_server.characteristic.set_value: - ble_server.characteristic.set_value:
id: test_change_characteristic id: test_change_characteristic
value: !lambda 'return ByteBuffer::wrap({0x00, 0x01, 0x02});' value: !lambda 'return ByteBuffer::wrap({0x00, 0x01, 0x02});'