mirror of
https://github.com/esphome/esphome.git
synced 2025-10-04 02:52:22 +01:00
wip
This commit is contained in:
@@ -1,4 +1,5 @@
|
|||||||
import encodings
|
import encodings
|
||||||
|
from typing import TypeAlias
|
||||||
|
|
||||||
from esphome import automation
|
from esphome import automation
|
||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
@@ -31,6 +32,92 @@ CODEOWNERS = ["@jesserockz", "@clydebarrow", "@Rapsssito"]
|
|||||||
DEPENDENCIES = ["esp32"]
|
DEPENDENCIES = ["esp32"]
|
||||||
DOMAIN = "esp32_ble_server"
|
DOMAIN = "esp32_ble_server"
|
||||||
|
|
||||||
|
# Type aliases
|
||||||
|
_ListenerAllocation: TypeAlias = tuple[str, int | str, int] # (component, uuid, count)
|
||||||
|
_ServerListenerAllocation: TypeAlias = tuple[str, int] # (component, count)
|
||||||
|
|
||||||
|
# Event listener allocation tracking - used by components to reserve slots
|
||||||
|
_LISTENER_ALLOCATIONS: dict[
|
||||||
|
str, list[_ListenerAllocation | _ServerListenerAllocation]
|
||||||
|
] = {
|
||||||
|
"characteristic_write": [],
|
||||||
|
"characteristic_read": [],
|
||||||
|
"descriptor_write": [],
|
||||||
|
"server_connect": [],
|
||||||
|
"server_disconnect": [],
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def allocate_characteristic_event_listener(
|
||||||
|
uuid: int | str, event_type: str, component: str, count: int = 1
|
||||||
|
) -> None:
|
||||||
|
"""
|
||||||
|
Allocate event listener slots for a characteristic.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
uuid: The characteristic UUID (int or string)
|
||||||
|
event_type: "WRITE" or "READ"
|
||||||
|
component: Name of the component requesting allocation
|
||||||
|
count: Number of listeners needed (default 1)
|
||||||
|
"""
|
||||||
|
if event_type not in ("WRITE", "READ"):
|
||||||
|
raise ValueError(f"Unknown event_type: {event_type}")
|
||||||
|
|
||||||
|
key = f"characteristic_{event_type.lower()}"
|
||||||
|
_LISTENER_ALLOCATIONS[key].append((component, uuid, count))
|
||||||
|
|
||||||
|
|
||||||
|
def allocate_descriptor_event_listener(
|
||||||
|
uuid: int | str, event_type: str, component: str, count: int = 1
|
||||||
|
) -> None:
|
||||||
|
"""Allocate event listener slots for a descriptor."""
|
||||||
|
if event_type != "WRITE":
|
||||||
|
raise ValueError(f"Unknown event_type: {event_type}")
|
||||||
|
|
||||||
|
_LISTENER_ALLOCATIONS["descriptor_write"].append((component, uuid, count))
|
||||||
|
|
||||||
|
|
||||||
|
def allocate_server_event_listener(
|
||||||
|
event_type: str, component: str, count: int = 1
|
||||||
|
) -> None:
|
||||||
|
"""Allocate event listener slots for server events."""
|
||||||
|
if event_type not in ("CONNECT", "DISCONNECT"):
|
||||||
|
raise ValueError(f"Unknown event_type: {event_type}")
|
||||||
|
|
||||||
|
key = f"server_{event_type.lower()}"
|
||||||
|
_LISTENER_ALLOCATIONS[key].append((component, count))
|
||||||
|
|
||||||
|
|
||||||
|
def _sum_allocations_for_uuid(allocation_key: str, uuid: int | str) -> int:
|
||||||
|
"""Helper to sum allocations for a specific UUID."""
|
||||||
|
return sum(
|
||||||
|
count
|
||||||
|
for comp, alloc_uuid, count in _LISTENER_ALLOCATIONS[allocation_key]
|
||||||
|
if alloc_uuid == uuid
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _get_allocations_for_uuid(uuid: int | str, event_type: str) -> int:
|
||||||
|
"""Get total allocated listeners for a specific UUID and event type."""
|
||||||
|
if event_type not in ("WRITE", "READ"):
|
||||||
|
return 0
|
||||||
|
key = f"characteristic_{event_type.lower()}"
|
||||||
|
return _sum_allocations_for_uuid(key, uuid)
|
||||||
|
|
||||||
|
|
||||||
|
def _get_descriptor_allocations_for_uuid(uuid: int | str) -> int:
|
||||||
|
"""Get total allocated listeners for a descriptor UUID."""
|
||||||
|
return _sum_allocations_for_uuid("descriptor_write", uuid)
|
||||||
|
|
||||||
|
|
||||||
|
def sanitize_uuid_for_identifier(uuid: int | str) -> str:
|
||||||
|
"""Convert UUID to valid C++ identifier."""
|
||||||
|
if isinstance(uuid, int):
|
||||||
|
return f"0x{uuid:04X}"
|
||||||
|
# For string UUIDs, replace dashes and colons with underscores
|
||||||
|
return str(uuid).replace("-", "_").replace(":", "_").lower()
|
||||||
|
|
||||||
|
|
||||||
CONF_ADVERTISE = "advertise"
|
CONF_ADVERTISE = "advertise"
|
||||||
CONF_APPEARANCE = "appearance"
|
CONF_APPEARANCE = "appearance"
|
||||||
CONF_BROADCAST = "broadcast"
|
CONF_BROADCAST = "broadcast"
|
||||||
|
@@ -208,8 +208,7 @@ 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,
|
this->emit_on_read_(param->read.conn_id);
|
||||||
param->read.conn_id);
|
|
||||||
|
|
||||||
uint16_t max_offset = 22;
|
uint16_t max_offset = 22;
|
||||||
|
|
||||||
@@ -277,8 +276,7 @@ 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_(
|
this->emit_on_write_(this->value_, param->write.conn_id);
|
||||||
BLECharacteristicEvt::VectorEvt::ON_WRITE, this->value_, param->write.conn_id);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@@ -289,8 +287,7 @@ 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_(
|
this->emit_on_write_(this->value_, param->exec_write.conn_id);
|
||||||
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/components/bytebuffer/bytebuffer.h"
|
#include "esphome/components/bytebuffer/bytebuffer.h"
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <span>
|
||||||
|
|
||||||
#ifdef USE_ESP32
|
#ifdef USE_ESP32
|
||||||
|
|
||||||
@@ -36,8 +37,9 @@ enum EmptyEvt {
|
|||||||
};
|
};
|
||||||
} // namespace BLECharacteristicEvt
|
} // namespace BLECharacteristicEvt
|
||||||
|
|
||||||
class BLECharacteristic : public EventEmitter<BLECharacteristicEvt::VectorEvt, std::vector<uint8_t>, uint16_t>,
|
// Base class for BLE characteristics
|
||||||
public EventEmitter<BLECharacteristicEvt::EmptyEvt, uint16_t> {
|
// Specialized classes with EventEmitter support are generated per-UUID in the build process
|
||||||
|
class BLECharacteristic {
|
||||||
public:
|
public:
|
||||||
BLECharacteristic(ESPBTUUID uuid, uint32_t properties);
|
BLECharacteristic(ESPBTUUID uuid, uint32_t properties);
|
||||||
~BLECharacteristic();
|
~BLECharacteristic();
|
||||||
@@ -76,7 +78,19 @@ class BLECharacteristic : public EventEmitter<BLECharacteristicEvt::VectorEvt, s
|
|||||||
bool is_created();
|
bool is_created();
|
||||||
bool is_failed();
|
bool is_failed();
|
||||||
|
|
||||||
|
// Event listener registration - overridden by generated specialized classes
|
||||||
|
virtual EventEmitterListenerID on_write(std::function<void(std::span<const uint8_t>, uint16_t)> &&listener) {
|
||||||
|
return INVALID_LISTENER_ID;
|
||||||
|
}
|
||||||
|
virtual EventEmitterListenerID on_read(std::function<void(uint16_t)> &&listener) { return INVALID_LISTENER_ID; }
|
||||||
|
virtual void off_write(EventEmitterListenerID id) {}
|
||||||
|
virtual void off_read(EventEmitterListenerID id) {}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
// Virtual methods for emitting events - overridden by generated specialized classes
|
||||||
|
virtual void emit_on_write_(std::span<const uint8_t> value, uint16_t conn_id) {}
|
||||||
|
virtual void emit_on_read_(uint16_t conn_id) {}
|
||||||
|
|
||||||
bool write_event_{false};
|
bool write_event_{false};
|
||||||
BLEService *service_{};
|
BLEService *service_{};
|
||||||
ESPBTUUID uuid_;
|
ESPBTUUID uuid_;
|
||||||
|
@@ -74,9 +74,7 @@ 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,
|
this->emit_on_write_(std::span<const uint8_t>(param->write.value, param->write.len), param->write.conn_id);
|
||||||
std::vector<uint8_t>(param->write.value, param->write.value + param->write.len),
|
|
||||||
param->write.conn_id);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
@@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
#include <esp_gatt_defs.h>
|
#include <esp_gatt_defs.h>
|
||||||
#include <esp_gatts_api.h>
|
#include <esp_gatts_api.h>
|
||||||
|
#include <span>
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace esp32_ble_server {
|
namespace esp32_ble_server {
|
||||||
@@ -24,7 +25,9 @@ enum VectorEvt {
|
|||||||
};
|
};
|
||||||
} // namespace BLEDescriptorEvt
|
} // namespace BLEDescriptorEvt
|
||||||
|
|
||||||
class BLEDescriptor : public EventEmitter<BLEDescriptorEvt::VectorEvt, std::vector<uint8_t>, uint16_t> {
|
// Base class for BLE descriptors
|
||||||
|
// Specialized classes with EventEmitter support are generated per-UUID in the build process
|
||||||
|
class BLEDescriptor {
|
||||||
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,7 +42,16 @@ 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; }
|
||||||
|
|
||||||
|
// Event listener registration - overridden by generated specialized classes if needed
|
||||||
|
virtual EventEmitterListenerID on_write(std::function<void(std::span<const uint8_t>, uint16_t)> &&listener) {
|
||||||
|
return INVALID_LISTENER_ID;
|
||||||
|
}
|
||||||
|
virtual void off_write(EventEmitterListenerID id) {}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
// Virtual method for emitting events - overridden by generated specialized classes
|
||||||
|
virtual void emit_on_write_(std::span<const uint8_t> value, uint16_t conn_id) {}
|
||||||
|
|
||||||
BLECharacteristic *characteristic_{nullptr};
|
BLECharacteristic *characteristic_{nullptr};
|
||||||
ESPBTUUID uuid_;
|
ESPBTUUID uuid_;
|
||||||
uint16_t handle_{0xFFFF};
|
uint16_t handle_{0xFFFF};
|
||||||
|
@@ -153,14 +153,14 @@ void BLEServer::gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t ga
|
|||||||
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->emit_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->emit_on_disconnect_(param->disconnect.conn_id);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ESP_GATTS_REG_EVT: {
|
case ESP_GATTS_REG_EVT: {
|
||||||
|
@@ -31,11 +31,9 @@ enum EmptyEvt {
|
|||||||
};
|
};
|
||||||
} // namespace BLEServerEvt
|
} // namespace BLEServerEvt
|
||||||
|
|
||||||
class BLEServer : public Component,
|
// Base class for BLE server
|
||||||
public GATTsEventHandler,
|
// Note: Only one BLEServer instance exists per build, so we can use fixed defines
|
||||||
public BLEStatusEventHandler,
|
class BLEServer : public Component, public GATTsEventHandler, public BLEStatusEventHandler, public Parented<ESP32BLE> {
|
||||||
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 +63,16 @@ class BLEServer : public Component,
|
|||||||
|
|
||||||
void ble_before_disabled_event_handler() override;
|
void ble_before_disabled_event_handler() override;
|
||||||
|
|
||||||
|
// Event listener registration - overridden by generated specialized classes if needed
|
||||||
|
virtual EventEmitterListenerID on_connect(std::function<void(uint16_t)> &&listener) { return INVALID_LISTENER_ID; }
|
||||||
|
virtual EventEmitterListenerID on_disconnect(std::function<void(uint16_t)> &&listener) { return INVALID_LISTENER_ID; }
|
||||||
|
virtual void off_connect(EventEmitterListenerID id) {}
|
||||||
|
virtual void off_disconnect(EventEmitterListenerID id) {}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
// Virtual methods for emitting events
|
||||||
|
virtual void emit_on_connect_(uint16_t conn_id) {}
|
||||||
|
virtual void emit_on_disconnect_(uint16_t conn_id) {}
|
||||||
struct ServiceEntry {
|
struct ServiceEntry {
|
||||||
ESPBTUUID uuid;
|
ESPBTUUID uuid;
|
||||||
uint8_t inst_id;
|
uint8_t inst_id;
|
||||||
|
@@ -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,8 +45,7 @@ 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
|
||||||
@@ -58,8 +58,7 @@ void BLECharacteristicSetValueActionManager::set_listener(BLECharacteristic *cha
|
|||||||
auto *existing = this->find_listener_(characteristic);
|
auto *existing = this->find_listener_(characteristic);
|
||||||
if (existing != nullptr) {
|
if (existing != nullptr) {
|
||||||
// Remove the previous listener
|
// Remove the previous listener
|
||||||
characteristic->EventEmitter<BLECharacteristicEvt::EmptyEvt, uint16_t>::off(BLECharacteristicEvt::EmptyEvt::ON_READ,
|
characteristic->off_read(existing->listener_id);
|
||||||
existing->listener_id);
|
|
||||||
// Remove the pre-notify listener
|
// Remove the pre-notify listener
|
||||||
this->off(BLECharacteristicSetValueActionEvt::PRE_NOTIFY, existing->pre_notify_listener_id);
|
this->off(BLECharacteristicSetValueActionEvt::PRE_NOTIFY, existing->pre_notify_listener_id);
|
||||||
// Remove from vector
|
// Remove from vector
|
||||||
|
@@ -46,8 +46,12 @@ enum BLECharacteristicSetValueActionEvt {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// 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
|
||||||
|
#ifndef BLE_SET_VALUE_ACTION_MAX_LISTENERS
|
||||||
|
#define BLE_SET_VALUE_ACTION_MAX_LISTENERS 1
|
||||||
|
#endif
|
||||||
|
|
||||||
class BLECharacteristicSetValueActionManager
|
class BLECharacteristicSetValueActionManager
|
||||||
: public EventEmitter<BLECharacteristicSetValueActionEvt, BLECharacteristic *> {
|
: public EventEmitter<BLECharacteristicSetValueActionEvt, BLE_SET_VALUE_ACTION_MAX_LISTENERS, BLECharacteristic *> {
|
||||||
public:
|
public:
|
||||||
// Singleton pattern
|
// Singleton pattern
|
||||||
static BLECharacteristicSetValueActionManager *get_instance() {
|
static BLECharacteristicSetValueActionManager *get_instance() {
|
||||||
@@ -92,11 +96,10 @@ 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, uint16_t>::on(
|
this->listener_id_ = 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->listener_id_, [this, x...]() { this->parent_->set_value(this->buffer_.value(x...)); });
|
||||||
|
@@ -41,6 +41,10 @@ BLECharacteristic *BLEService::create_characteristic(ESPBTUUID uuid, esp_gatt_ch
|
|||||||
return characteristic;
|
return characteristic;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void BLEService::add_characteristic(BLECharacteristic *characteristic) {
|
||||||
|
this->characteristics_.push_back(characteristic);
|
||||||
|
}
|
||||||
|
|
||||||
void BLEService::do_create(BLEServer *server) {
|
void BLEService::do_create(BLEServer *server) {
|
||||||
this->server_ = server;
|
this->server_ = server;
|
||||||
|
|
||||||
|
@@ -31,6 +31,9 @@ class BLEService {
|
|||||||
BLECharacteristic *create_characteristic(uint16_t uuid, esp_gatt_char_prop_t properties);
|
BLECharacteristic *create_characteristic(uint16_t uuid, esp_gatt_char_prop_t properties);
|
||||||
BLECharacteristic *create_characteristic(ESPBTUUID uuid, esp_gatt_char_prop_t properties);
|
BLECharacteristic *create_characteristic(ESPBTUUID uuid, esp_gatt_char_prop_t properties);
|
||||||
|
|
||||||
|
// Add pre-constructed characteristic (used by generated code)
|
||||||
|
void add_characteristic(BLECharacteristic *characteristic);
|
||||||
|
|
||||||
ESPBTUUID get_uuid() { return this->uuid_; }
|
ESPBTUUID get_uuid() { return this->uuid_; }
|
||||||
uint8_t get_inst_id() { return this->inst_id_; }
|
uint8_t get_inst_id() { return this->inst_id_; }
|
||||||
BLECharacteristic *get_last_created_characteristic() { return this->last_created_characteristic_; }
|
BLECharacteristic *get_last_created_characteristic() { return this->last_created_characteristic_; }
|
||||||
|
@@ -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);
|
||||||
|
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <vector>
|
#include <array>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <limits>
|
#include <limits>
|
||||||
|
|
||||||
@@ -13,34 +13,31 @@ 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)
|
// 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.
|
// and a list of arguments. Supports multiple listeners for each event.
|
||||||
template<typename EvtType, typename... Args> class EventEmitter {
|
// MaxListeners is the compile-time maximum number of listeners per event type.
|
||||||
|
template<typename EvtType, size_t MaxListeners, typename... Args> class EventEmitter {
|
||||||
public:
|
public:
|
||||||
EventEmitterListenerID on(EvtType event, std::function<void(Args...)> listener) {
|
EventEmitterListenerID on(EvtType event, std::function<void(Args...)> listener) {
|
||||||
EventEmitterListenerID listener_id = this->get_next_id_();
|
// Find a free slot in the listeners array
|
||||||
|
for (auto &entry : this->listeners_) {
|
||||||
// Find or create event entry
|
if (entry.id == INVALID_LISTENER_ID) {
|
||||||
EventEntry *entry = this->find_or_create_event_(event);
|
// Found empty slot
|
||||||
entry->listeners.push_back({listener_id, listener});
|
EventEmitterListenerID listener_id = this->get_next_id_();
|
||||||
|
entry.id = listener_id;
|
||||||
return listener_id;
|
entry.event = event;
|
||||||
|
entry.callback = std::move(listener);
|
||||||
|
return listener_id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// No free slots - array is full
|
||||||
|
return INVALID_LISTENER_ID;
|
||||||
}
|
}
|
||||||
|
|
||||||
void off(EvtType event, EventEmitterListenerID id) {
|
void off(EvtType event, EventEmitterListenerID id) {
|
||||||
EventEntry *entry = this->find_event_(event);
|
// Find and remove listener with given id
|
||||||
if (entry == nullptr)
|
for (auto &entry : this->listeners_) {
|
||||||
return;
|
if (entry.id == id && entry.event == event) {
|
||||||
|
entry.id = INVALID_LISTENER_ID;
|
||||||
// Remove listener with given id
|
entry.callback = nullptr;
|
||||||
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;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -48,25 +45,19 @@ template<typename EvtType, typename... Args> class EventEmitter {
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
void emit_(EvtType event, Args... args) {
|
void emit_(EvtType event, Args... args) {
|
||||||
EventEntry *entry = this->find_event_(event);
|
|
||||||
if (entry == nullptr)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Call all listeners for this event
|
// Call all listeners for this event
|
||||||
for (const auto &listener : entry->listeners) {
|
for (const auto &entry : this->listeners_) {
|
||||||
listener.callback(args...);
|
if (entry.id != INVALID_LISTENER_ID && entry.event == event) {
|
||||||
|
entry.callback(args...);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct Listener {
|
struct ListenerEntry {
|
||||||
EventEmitterListenerID id;
|
|
||||||
std::function<void(Args...)> callback;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct EventEntry {
|
|
||||||
EvtType event;
|
EvtType event;
|
||||||
std::vector<Listener> listeners;
|
EventEmitterListenerID id{INVALID_LISTENER_ID};
|
||||||
|
std::function<void(Args...)> callback{nullptr};
|
||||||
};
|
};
|
||||||
|
|
||||||
EventEmitterListenerID get_next_id_() {
|
EventEmitterListenerID get_next_id_() {
|
||||||
@@ -79,38 +70,8 @@ template<typename EvtType, typename... Args> class EventEmitter {
|
|||||||
return this->current_id_;
|
return this->current_id_;
|
||||||
}
|
}
|
||||||
|
|
||||||
EventEntry *find_event_(EvtType event) {
|
std::array<ListenerEntry, MaxListeners> listeners_{};
|
||||||
for (auto &entry : this->events_) {
|
EventEmitterListenerID current_id_{0};
|
||||||
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 event_emitter
|
||||||
|
Reference in New Issue
Block a user