mirror of
https://github.com/esphome/esphome.git
synced 2024-11-27 17:27:59 +01:00
Add connect, disconnect and fix notify
This commit is contained in:
parent
b422fda8bf
commit
c3b29fbfab
12 changed files with 143 additions and 84 deletions
|
@ -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)
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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: {
|
||||||
|
|
|
@ -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_;
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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...));
|
||||||
});
|
});
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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_();
|
||||||
|
|
|
@ -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});'
|
||||||
|
|
Loading…
Reference in a new issue