mirror of
https://github.com/esphome/esphome.git
synced 2024-11-24 16:08:10 +01:00
Compute characteristic value on request
This commit is contained in:
parent
55c3b66ed8
commit
4e4cdc92a1
14 changed files with 411 additions and 208 deletions
|
@ -7,7 +7,6 @@ from esphome.const import (
|
||||||
CONF_UUID,
|
CONF_UUID,
|
||||||
CONF_SERVICES,
|
CONF_SERVICES,
|
||||||
CONF_VALUE,
|
CONF_VALUE,
|
||||||
CONF_MAX_LENGTH,
|
|
||||||
)
|
)
|
||||||
from esphome.components import esp32_ble
|
from esphome.components import esp32_ble
|
||||||
from esphome.core import CORE
|
from esphome.core import CORE
|
||||||
|
@ -24,8 +23,14 @@ CONF_ADVERTISE = "advertise"
|
||||||
CONF_NUM_HANDLES = "num_handles"
|
CONF_NUM_HANDLES = "num_handles"
|
||||||
CONF_ON_WRITE = "on_write"
|
CONF_ON_WRITE = "on_write"
|
||||||
CONF_CHARACTERISTICS = "characteristics"
|
CONF_CHARACTERISTICS = "characteristics"
|
||||||
CONF_PROPERTIES = "properties"
|
CONF_READ = "read"
|
||||||
|
CONF_WRITE = "write"
|
||||||
|
CONF_NOTIFY = "notify"
|
||||||
|
CONF_BROADCAST = "broadcast"
|
||||||
|
CONF_INDICATE = "indicate"
|
||||||
|
CONF_WRITE_NO_RESPONSE = "write_no_response"
|
||||||
CONF_DESCRIPTORS = "descriptors"
|
CONF_DESCRIPTORS = "descriptors"
|
||||||
|
CONF_VALUE_ACTION_ID = "value_action_id_"
|
||||||
|
|
||||||
esp32_ble_server_ns = cg.esphome_ns.namespace("esp32_ble_server")
|
esp32_ble_server_ns = cg.esphome_ns.namespace("esp32_ble_server")
|
||||||
ESPBTUUID_ns = cg.esphome_ns.namespace("esp32_ble").namespace("ESPBTUUID")
|
ESPBTUUID_ns = cg.esphome_ns.namespace("esp32_ble").namespace("ESPBTUUID")
|
||||||
|
@ -36,15 +41,20 @@ BLEServer = esp32_ble_server_ns.class_(
|
||||||
esp32_ble.GATTsEventHandler,
|
esp32_ble.GATTsEventHandler,
|
||||||
cg.Parented.template(esp32_ble.ESP32BLE),
|
cg.Parented.template(esp32_ble.ESP32BLE),
|
||||||
)
|
)
|
||||||
BLEServerAutomationInterface = esp32_ble_server_ns.namespace(
|
esp32_ble_server_automations_ns = esp32_ble_server_ns.namespace(
|
||||||
"BLEServerAutomationInterface"
|
"esp32_ble_server_automations"
|
||||||
)
|
)
|
||||||
|
BLETriggers_ns = esp32_ble_server_automations_ns.namespace("BLETriggers")
|
||||||
BLEDescriptor = esp32_ble_server_ns.class_("BLEDescriptor")
|
BLEDescriptor = esp32_ble_server_ns.class_("BLEDescriptor")
|
||||||
BLECharacteristic = esp32_ble_server_ns.class_("BLECharacteristic")
|
BLECharacteristic = esp32_ble_server_ns.class_("BLECharacteristic")
|
||||||
BLEService = esp32_ble_server_ns.class_("BLEService")
|
BLEService = esp32_ble_server_ns.class_("BLEService")
|
||||||
BLECharacteristicSetValueAction = BLEServerAutomationInterface.class_(
|
BLECharacteristicSetValueAction = esp32_ble_server_automations_ns.class_(
|
||||||
"BLECharacteristicSetValueAction", automation.Action
|
"BLECharacteristicSetValueAction", automation.Action
|
||||||
)
|
)
|
||||||
|
BLECharacteristicNotifyAction = esp32_ble_server_automations_ns.class_(
|
||||||
|
"BLECharacteristicNotifyAction", automation.Action
|
||||||
|
)
|
||||||
|
_ble_server_config = None
|
||||||
|
|
||||||
|
|
||||||
def validate_uuid(value):
|
def validate_uuid(value):
|
||||||
|
@ -53,40 +63,35 @@ def validate_uuid(value):
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
|
||||||
PROPERTIES_SCHEMA = cv.All(
|
|
||||||
cv.ensure_list(
|
|
||||||
cv.one_of(
|
|
||||||
"READ",
|
|
||||||
"WRITE",
|
|
||||||
"NOTIFY",
|
|
||||||
"BROADCAST",
|
|
||||||
"INDICATE",
|
|
||||||
"WRITE_NR",
|
|
||||||
upper=True,
|
|
||||||
)
|
|
||||||
),
|
|
||||||
cv.Length(min=1),
|
|
||||||
)
|
|
||||||
|
|
||||||
UUID_SCHEMA = cv.Any(cv.All(cv.string, validate_uuid), cv.hex_uint32_t)
|
UUID_SCHEMA = cv.Any(cv.All(cv.string, validate_uuid), cv.hex_uint32_t)
|
||||||
|
|
||||||
CHARACTERISTIC_VALUE_SCHEMA = cv.Any(
|
DESCRIPTOR_VALUE_SCHEMA = cv.Any(
|
||||||
cv.All(cv.ensure_list(cv.hex_uint8_t), cv.Length(min=1)),
|
cv.boolean,
|
||||||
cv.string,
|
cv.float_,
|
||||||
cv.hex_uint8_t,
|
cv.hex_uint8_t,
|
||||||
cv.hex_uint16_t,
|
cv.hex_uint16_t,
|
||||||
cv.hex_uint32_t,
|
cv.hex_uint32_t,
|
||||||
cv.int_,
|
cv.int_,
|
||||||
cv.float_,
|
cv.All(cv.ensure_list(cv.hex_uint8_t), cv.Length(min=1)),
|
||||||
cv.boolean,
|
cv.string,
|
||||||
)
|
)
|
||||||
|
|
||||||
SERVICE_CHARACTERISTIC_DESCRIPTOR_SCHEMA = cv.Schema(
|
CHARACTERISTIC_VALUE_SCHEMA = cv.Any(
|
||||||
|
cv.boolean,
|
||||||
|
cv.float_,
|
||||||
|
cv.hex_uint8_t,
|
||||||
|
cv.hex_uint16_t,
|
||||||
|
cv.hex_uint32_t,
|
||||||
|
cv.int_,
|
||||||
|
cv.templatable(cv.All(cv.ensure_list(cv.hex_uint8_t), cv.Length(min=1))),
|
||||||
|
cv.string,
|
||||||
|
)
|
||||||
|
|
||||||
|
DESCRIPTOR_SCHEMA = cv.Schema(
|
||||||
{
|
{
|
||||||
cv.GenerateID(): cv.declare_id(BLEDescriptor),
|
cv.GenerateID(): cv.declare_id(BLEDescriptor),
|
||||||
cv.Required(CONF_UUID): UUID_SCHEMA,
|
cv.Required(CONF_UUID): UUID_SCHEMA,
|
||||||
cv.Optional(CONF_MAX_LENGTH, default=0): cv.int_,
|
cv.Required(CONF_VALUE): DESCRIPTOR_VALUE_SCHEMA,
|
||||||
cv.Required(CONF_VALUE): CHARACTERISTIC_VALUE_SCHEMA,
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -94,10 +99,16 @@ SERVICE_CHARACTERISTIC_SCHEMA = cv.Schema(
|
||||||
{
|
{
|
||||||
cv.GenerateID(): cv.declare_id(BLECharacteristic),
|
cv.GenerateID(): cv.declare_id(BLECharacteristic),
|
||||||
cv.Required(CONF_UUID): UUID_SCHEMA,
|
cv.Required(CONF_UUID): UUID_SCHEMA,
|
||||||
cv.Required(CONF_PROPERTIES): PROPERTIES_SCHEMA,
|
cv.Optional(CONF_READ, default=False): cv.boolean,
|
||||||
|
cv.Optional(CONF_WRITE, default=False): cv.boolean,
|
||||||
|
cv.Optional(CONF_NOTIFY, default=False): cv.boolean,
|
||||||
|
cv.Optional(CONF_BROADCAST, default=False): cv.boolean,
|
||||||
|
cv.Optional(CONF_INDICATE, default=False): cv.boolean,
|
||||||
|
cv.Optional(CONF_WRITE_NO_RESPONSE, default=False): cv.boolean,
|
||||||
cv.Optional(CONF_VALUE): CHARACTERISTIC_VALUE_SCHEMA,
|
cv.Optional(CONF_VALUE): CHARACTERISTIC_VALUE_SCHEMA,
|
||||||
|
cv.GenerateID(CONF_VALUE_ACTION_ID): cv.declare_id(BLECharacteristicSetValueAction),
|
||||||
cv.Optional(CONF_DESCRIPTORS, default=[]): cv.ensure_list(
|
cv.Optional(CONF_DESCRIPTORS, default=[]): cv.ensure_list(
|
||||||
SERVICE_CHARACTERISTIC_DESCRIPTOR_SCHEMA
|
DESCRIPTOR_SCHEMA
|
||||||
),
|
),
|
||||||
cv.Optional(CONF_ON_WRITE): automation.validate_automation(
|
cv.Optional(CONF_ON_WRITE): automation.validate_automation(
|
||||||
{cv.GenerateID(): cv.declare_id(BLECharacteristic)}, single=True
|
{cv.GenerateID(): cv.declare_id(BLECharacteristic)}, single=True
|
||||||
|
@ -129,21 +140,20 @@ CONFIG_SCHEMA = cv.Schema(
|
||||||
).extend(cv.COMPONENT_SCHEMA)
|
).extend(cv.COMPONENT_SCHEMA)
|
||||||
|
|
||||||
|
|
||||||
def parse_properties(properties):
|
def parse_properties(char_conf):
|
||||||
result = 0
|
result = 0
|
||||||
for prop in properties:
|
if char_conf[CONF_READ]:
|
||||||
if prop == "READ":
|
result = result | BLECharacteristic_ns.PROPERTY_READ
|
||||||
result = result | BLECharacteristic_ns.PROPERTY_READ
|
if char_conf[CONF_WRITE]:
|
||||||
elif prop == "WRITE":
|
result = result | BLECharacteristic_ns.PROPERTY_WRITE
|
||||||
result = result | BLECharacteristic_ns.PROPERTY_WRITE
|
if char_conf[CONF_NOTIFY]:
|
||||||
elif prop == "NOTIFY":
|
result = result | BLECharacteristic_ns.PROPERTY_NOTIFY
|
||||||
result = result | BLECharacteristic_ns.PROPERTY_NOTIFY
|
if char_conf[CONF_BROADCAST]:
|
||||||
elif prop == "BROADCAST":
|
result = result | BLECharacteristic_ns.PROPERTY_BROADCAST
|
||||||
result = result | BLECharacteristic_ns.PROPERTY_BROADCAST
|
if char_conf[CONF_INDICATE]:
|
||||||
elif prop == "INDICATE":
|
result = result | BLECharacteristic_ns.PROPERTY_INDICATE
|
||||||
result = result | BLECharacteristic_ns.PROPERTY_INDICATE
|
if char_conf[CONF_WRITE_NO_RESPONSE]:
|
||||||
elif prop == "WRITE_NR":
|
result = result | BLECharacteristic_ns.PROPERTY_WRITE_NR
|
||||||
result = result | BLECharacteristic_ns.PROPERTY_WRITE_NR
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
@ -155,10 +165,47 @@ def parse_uuid(uuid):
|
||||||
return ESPBTUUID_ns.from_uint32(uuid)
|
return ESPBTUUID_ns.from_uint32(uuid)
|
||||||
|
|
||||||
|
|
||||||
def parse_value(value):
|
def parse_descriptor_value(value):
|
||||||
if isinstance(value, list):
|
# Compute the maximum length of the descriptor value
|
||||||
return cg.std_vector.template(cg.uint8)(value)
|
# Also parse the value for byte arrays
|
||||||
return value
|
try:
|
||||||
|
cv.boolean(value)
|
||||||
|
return value, 1
|
||||||
|
except cv.Invalid:
|
||||||
|
pass
|
||||||
|
try:
|
||||||
|
cv.float_(value)
|
||||||
|
return value, 8
|
||||||
|
except cv.Invalid:
|
||||||
|
pass
|
||||||
|
try:
|
||||||
|
cv.hex_uint8_t(value)
|
||||||
|
return value, 1
|
||||||
|
except cv.Invalid:
|
||||||
|
pass
|
||||||
|
try:
|
||||||
|
cv.hex_uint16_t(value)
|
||||||
|
return value, 2
|
||||||
|
except cv.Invalid:
|
||||||
|
pass
|
||||||
|
try:
|
||||||
|
cv.hex_uint32_t(value)
|
||||||
|
return value, 4
|
||||||
|
except cv.Invalid:
|
||||||
|
pass
|
||||||
|
try:
|
||||||
|
cv.int_(value)
|
||||||
|
return value, 4
|
||||||
|
except cv.Invalid:
|
||||||
|
pass
|
||||||
|
try:
|
||||||
|
cv.string(value)
|
||||||
|
# Count the bytes in the string
|
||||||
|
value_len = len(value.encode("utf-8"))
|
||||||
|
return value, value_len
|
||||||
|
except cv.Invalid:
|
||||||
|
pass
|
||||||
|
return cg.std_vector.template(cg.uint8)(value), len(value) # Assume it's a list of bytes
|
||||||
|
|
||||||
|
|
||||||
def calculate_num_handles(service_config):
|
def calculate_num_handles(service_config):
|
||||||
|
@ -172,6 +219,8 @@ def calculate_num_handles(service_config):
|
||||||
|
|
||||||
async def to_code(config):
|
async def to_code(config):
|
||||||
var = cg.new_Pvariable(config[CONF_ID])
|
var = cg.new_Pvariable(config[CONF_ID])
|
||||||
|
global _ble_server_config
|
||||||
|
_ble_server_config = config
|
||||||
|
|
||||||
await cg.register_component(var, config)
|
await cg.register_component(var, config)
|
||||||
|
|
||||||
|
@ -202,51 +251,106 @@ async def to_code(config):
|
||||||
char_conf[CONF_ID],
|
char_conf[CONF_ID],
|
||||||
service_var.create_characteristic(
|
service_var.create_characteristic(
|
||||||
parse_uuid(char_conf[CONF_UUID]),
|
parse_uuid(char_conf[CONF_UUID]),
|
||||||
parse_properties(char_conf[CONF_PROPERTIES]),
|
parse_properties(char_conf),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
if CONF_ON_WRITE in char_conf:
|
if CONF_ON_WRITE in char_conf:
|
||||||
on_write_conf = char_conf[CONF_ON_WRITE]
|
on_write_conf = char_conf[CONF_ON_WRITE]
|
||||||
if "WRITE" not in char_conf[CONF_PROPERTIES]:
|
if not char_conf[CONF_WRITE] and not char_conf[CONF_WRITE_NO_RESPONSE]:
|
||||||
raise cv.Invalid("on_write requires the WRITE property")
|
raise cv.Invalid(f"on_write requires the {CONF_WRITE} or {CONF_WRITE_NO_RESPONSE} property to be set")
|
||||||
await automation.build_automation(
|
await automation.build_automation(
|
||||||
BLEServerAutomationInterface.create_on_write_trigger(char_var),
|
BLETriggers_ns.create_on_write_trigger(char_var),
|
||||||
[(cg.std_string, "x")],
|
[(cg.std_vector.template(cg.uint8), "x")],
|
||||||
on_write_conf,
|
on_write_conf,
|
||||||
)
|
)
|
||||||
if CONF_VALUE in char_conf:
|
if CONF_VALUE in char_conf:
|
||||||
cg.add(char_var.set_value(parse_value(char_conf[CONF_VALUE])))
|
action_conf = {
|
||||||
|
CONF_ID: char_conf[CONF_ID],
|
||||||
|
CONF_VALUE: char_conf[CONF_VALUE],
|
||||||
|
}
|
||||||
|
value_action = await ble_server_characteristic_set_value(action_conf, char_conf[CONF_VALUE_ACTION_ID], cg.TemplateArguments(None), {})
|
||||||
|
cg.add(value_action.play())
|
||||||
for descriptor_conf in char_conf[CONF_DESCRIPTORS]:
|
for descriptor_conf in char_conf[CONF_DESCRIPTORS]:
|
||||||
max_length = descriptor_conf[CONF_MAX_LENGTH]
|
descriptor_value, max_length = parse_descriptor_value(descriptor_conf[CONF_VALUE])
|
||||||
# If max_length is 0, calculate the optimal length based on the value
|
|
||||||
if max_length == 0:
|
|
||||||
max_length = len(parse_value(descriptor_conf[CONF_VALUE]))
|
|
||||||
desc_var = cg.new_Pvariable(
|
desc_var = cg.new_Pvariable(
|
||||||
descriptor_conf[CONF_ID],
|
descriptor_conf[CONF_ID],
|
||||||
parse_uuid(descriptor_conf[CONF_UUID]),
|
parse_uuid(descriptor_conf[CONF_UUID]),
|
||||||
max_length,
|
max_length,
|
||||||
)
|
)
|
||||||
if CONF_VALUE in descriptor_conf:
|
if CONF_VALUE in descriptor_conf:
|
||||||
cg.add(desc_var.set_value(parse_value(descriptor_conf[CONF_VALUE])))
|
cg.add(desc_var.set_value(descriptor_value))
|
||||||
cg.add(var.enqueue_start_service(service_var))
|
cg.add(var.enqueue_start_service(service_var))
|
||||||
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)
|
||||||
|
|
||||||
|
|
||||||
|
async def parse_characteristic_value(value, args):
|
||||||
|
if isinstance(value, cv.Lambda):
|
||||||
|
return await cg.templatable(value, args, cg.std_vector.template(cg.uint8), cg.std_vector.template(cg.uint8))
|
||||||
|
if isinstance(value, list):
|
||||||
|
return cg.std_vector.template(cg.uint8)(value)
|
||||||
|
# Transform the value into a vector of bytes
|
||||||
|
exp_value = cg.RawExpression(f'to_vector({value})')
|
||||||
|
try:
|
||||||
|
bool_value = cv.boolean(value)
|
||||||
|
return cg.RawExpression(f'to_vector({"true" if bool_value else "false"})')
|
||||||
|
except cv.Invalid:
|
||||||
|
pass
|
||||||
|
try:
|
||||||
|
int_ = cv.uint64_t(value)
|
||||||
|
return cg.RawExpression(f'to_vector({int_})')
|
||||||
|
except cv.Invalid:
|
||||||
|
pass
|
||||||
|
try:
|
||||||
|
float_ = cv.float_(value)
|
||||||
|
return cg.RawExpression(f'to_vector({float_})')
|
||||||
|
except cv.Invalid:
|
||||||
|
pass
|
||||||
|
try:
|
||||||
|
string_ = cv.string(value)
|
||||||
|
return cg.RawExpression(f'to_vector("{string_}")')
|
||||||
|
except cv.Invalid:
|
||||||
|
pass
|
||||||
|
raise cv.Invalid(f"Invalid value {value}")
|
||||||
|
|
||||||
|
|
||||||
@automation.register_action(
|
@automation.register_action(
|
||||||
"ble_server.characteristic_set_value",
|
"ble_server.characteristic.set_value",
|
||||||
BLECharacteristicSetValueAction,
|
BLECharacteristicSetValueAction,
|
||||||
cv.Schema(
|
cv.Schema(
|
||||||
{
|
{
|
||||||
cv.Required(CONF_ID): cv.use_id(BLECharacteristic),
|
cv.Required(CONF_ID): cv.use_id(BLECharacteristic),
|
||||||
cv.Required(CONF_VALUE): cv.templatable(cv.string),
|
cv.Required(CONF_VALUE): CHARACTERISTIC_VALUE_SCHEMA,
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
async def ble_server_characteristic_set_value(config, action_id, template_arg, args):
|
async def ble_server_characteristic_set_value(config, action_id, template_arg, args):
|
||||||
paren = await cg.get_variable(config[CONF_ID])
|
paren = await cg.get_variable(config[CONF_ID])
|
||||||
var = cg.new_Pvariable(action_id, template_arg, paren)
|
var = cg.new_Pvariable(action_id, template_arg, paren)
|
||||||
template_ = await cg.templatable(config[CONF_VALUE], args, cg.std_string)
|
value = await parse_characteristic_value(config[CONF_VALUE], args)
|
||||||
cg.add(var.set_value(template_))
|
cg.add(var.set_value(value))
|
||||||
|
return var
|
||||||
|
|
||||||
|
|
||||||
|
@automation.register_action(
|
||||||
|
"ble_server.characteristic.notify",
|
||||||
|
BLECharacteristicNotifyAction,
|
||||||
|
cv.Schema(
|
||||||
|
{
|
||||||
|
cv.Required(CONF_ID): cv.use_id(BLECharacteristic),
|
||||||
|
}
|
||||||
|
),
|
||||||
|
)
|
||||||
|
async def ble_server_characteristic_notify(config, action_id, template_arg, args):
|
||||||
|
paren = await cg.get_variable(config[CONF_ID])
|
||||||
|
# Check if the NOTIFY property is set from the global configuration
|
||||||
|
assert _ble_server_config is not None
|
||||||
|
for service_config in _ble_server_config[CONF_SERVICES]:
|
||||||
|
for char_conf in service_config[CONF_CHARACTERISTICS]:
|
||||||
|
if char_conf[CONF_ID] == config[CONF_ID]:
|
||||||
|
if not char_conf[CONF_NOTIFY]:
|
||||||
|
raise cv.Invalid(f'Characteristic "{char_conf[CONF_ID]}" does not have the NOTIFY property set')
|
||||||
|
break
|
||||||
|
var = cg.new_Pvariable(action_id, template_arg, paren)
|
||||||
return var
|
return var
|
||||||
|
|
|
@ -37,52 +37,9 @@ void BLECharacteristic::set_value(std::vector<uint8_t> value) {
|
||||||
this->value_ = std::move(value);
|
this->value_ = std::move(value);
|
||||||
xSemaphoreGive(this->set_value_lock_);
|
xSemaphoreGive(this->set_value_lock_);
|
||||||
}
|
}
|
||||||
void BLECharacteristic::set_value(const std::string &value) {
|
|
||||||
this->set_value(std::vector<uint8_t>(value.begin(), value.end()));
|
|
||||||
}
|
|
||||||
void BLECharacteristic::set_value(const uint8_t *data, size_t length) {
|
void BLECharacteristic::set_value(const uint8_t *data, size_t length) {
|
||||||
this->set_value(std::vector<uint8_t>(data, data + length));
|
this->set_value(std::vector<uint8_t>(data, data + length));
|
||||||
}
|
}
|
||||||
void BLECharacteristic::set_value(uint8_t &data) {
|
|
||||||
uint8_t temp[1];
|
|
||||||
temp[0] = data;
|
|
||||||
this->set_value(temp, 1);
|
|
||||||
}
|
|
||||||
void BLECharacteristic::set_value(uint16_t &data) {
|
|
||||||
uint8_t temp[2];
|
|
||||||
temp[0] = data;
|
|
||||||
temp[1] = data >> 8;
|
|
||||||
this->set_value(temp, 2);
|
|
||||||
}
|
|
||||||
void BLECharacteristic::set_value(uint32_t &data) {
|
|
||||||
uint8_t temp[4];
|
|
||||||
temp[0] = data;
|
|
||||||
temp[1] = data >> 8;
|
|
||||||
temp[2] = data >> 16;
|
|
||||||
temp[3] = data >> 24;
|
|
||||||
this->set_value(temp, 4);
|
|
||||||
}
|
|
||||||
void BLECharacteristic::set_value(int &data) {
|
|
||||||
uint8_t temp[4];
|
|
||||||
temp[0] = data;
|
|
||||||
temp[1] = data >> 8;
|
|
||||||
temp[2] = data >> 16;
|
|
||||||
temp[3] = data >> 24;
|
|
||||||
this->set_value(temp, 4);
|
|
||||||
}
|
|
||||||
void BLECharacteristic::set_value(float &data) {
|
|
||||||
float temp = data;
|
|
||||||
this->set_value((uint8_t *) &temp, 4);
|
|
||||||
}
|
|
||||||
void BLECharacteristic::set_value(double &data) {
|
|
||||||
double temp = data;
|
|
||||||
this->set_value((uint8_t *) &temp, 8);
|
|
||||||
}
|
|
||||||
void BLECharacteristic::set_value(bool &data) {
|
|
||||||
uint8_t temp[1];
|
|
||||||
temp[0] = data;
|
|
||||||
this->set_value(temp, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
void BLECharacteristic::notify(bool require_ack) {
|
void BLECharacteristic::notify(bool require_ack) {
|
||||||
if (require_ack) {
|
if (require_ack) {
|
||||||
|
@ -223,6 +180,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);
|
||||||
|
|
||||||
uint16_t max_offset = 22;
|
uint16_t max_offset = 22;
|
||||||
|
|
||||||
esp_gatt_rsp_t response;
|
esp_gatt_rsp_t response;
|
||||||
|
@ -289,8 +248,7 @@ void BLECharacteristic::gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!param->write.is_prep) {
|
if (!param->write.is_prep) {
|
||||||
if (this->on_write_)
|
this->EventEmitter<BLECharacteristicEvt::VectorEvt, std::vector<uint8_t>>::emit(BLECharacteristicEvt::VectorEvt::ON_WRITE, this->value_);
|
||||||
this->on_write_(this->value_);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
@ -301,8 +259,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) {
|
||||||
if (this->on_write_)
|
this->EventEmitter<BLECharacteristicEvt::VectorEvt, std::vector<uint8_t>>::emit(BLECharacteristicEvt::VectorEvt::ON_WRITE, this->value_);
|
||||||
this->on_write_(this->value_);
|
|
||||||
}
|
}
|
||||||
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);
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
|
|
||||||
#include "ble_descriptor.h"
|
#include "ble_descriptor.h"
|
||||||
#include "esphome/components/esp32_ble/ble_uuid.h"
|
#include "esphome/components/esp32_ble/ble_uuid.h"
|
||||||
|
#include "esphome/core/event_emitter.h"
|
||||||
|
#include "esphome/core/helpers.h"
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
@ -22,21 +24,31 @@ using namespace esp32_ble;
|
||||||
|
|
||||||
class BLEService;
|
class BLEService;
|
||||||
|
|
||||||
class BLECharacteristic {
|
namespace BLECharacteristicEvt {
|
||||||
|
enum VectorEvt {
|
||||||
|
ON_WRITE,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum EmptyEvt {
|
||||||
|
ON_READ,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
class BLECharacteristic : public EventEmitter<BLECharacteristicEvt::VectorEvt, std::vector<uint8_t>>, public EventEmitter<BLECharacteristicEvt::EmptyEvt> {
|
||||||
public:
|
public:
|
||||||
BLECharacteristic(ESPBTUUID uuid, uint32_t properties);
|
BLECharacteristic(ESPBTUUID uuid, uint32_t properties);
|
||||||
~BLECharacteristic();
|
~BLECharacteristic();
|
||||||
|
|
||||||
void set_value(const uint8_t *data, size_t length);
|
void set_value(const uint8_t *data, size_t length);
|
||||||
void set_value(std::vector<uint8_t> value);
|
void set_value(std::vector<uint8_t> value);
|
||||||
void set_value(const std::string &value);
|
void set_value(const std::string &value) { this->set_value(to_vector(value)); }
|
||||||
void set_value(uint8_t &data);
|
void set_value(uint8_t data) { this->set_value(to_vector(data)); }
|
||||||
void set_value(uint16_t &data);
|
void set_value(uint16_t data) { this->set_value(to_vector(data)); }
|
||||||
void set_value(uint32_t &data);
|
void set_value(uint32_t data) { this->set_value(to_vector(data)); }
|
||||||
void set_value(int &data);
|
void set_value(int data) { this->set_value(to_vector(data)); }
|
||||||
void set_value(float &data);
|
void set_value(float data) { this->set_value(to_vector(data)); }
|
||||||
void set_value(double &data);
|
void set_value(double data) { this->set_value(to_vector(data)); }
|
||||||
void set_value(bool &data);
|
void set_value(bool data) { this->set_value(to_vector(data)); }
|
||||||
|
|
||||||
void set_broadcast_property(bool value);
|
void set_broadcast_property(bool value);
|
||||||
void set_indicate_property(bool value);
|
void set_indicate_property(bool value);
|
||||||
|
@ -51,8 +63,6 @@ class BLECharacteristic {
|
||||||
void do_create(BLEService *service);
|
void do_create(BLEService *service);
|
||||||
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 on_write(const std::function<void(const std::vector<uint8_t> &)> &&func) { this->on_write_ = func; }
|
|
||||||
|
|
||||||
void add_descriptor(BLEDescriptor *descriptor);
|
void add_descriptor(BLEDescriptor *descriptor);
|
||||||
void remove_descriptor(BLEDescriptor *descriptor);
|
void remove_descriptor(BLEDescriptor *descriptor);
|
||||||
|
|
||||||
|
@ -83,8 +93,6 @@ class BLECharacteristic {
|
||||||
|
|
||||||
std::vector<BLEDescriptor *> descriptors_;
|
std::vector<BLEDescriptor *> descriptors_;
|
||||||
|
|
||||||
std::function<void(const std::vector<uint8_t> &)> on_write_;
|
|
||||||
|
|
||||||
esp_gatt_perm_t permissions_ = ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE;
|
esp_gatt_perm_t permissions_ = ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE;
|
||||||
|
|
||||||
enum State : uint8_t {
|
enum State : uint8_t {
|
||||||
|
|
|
@ -39,47 +39,7 @@ void BLEDescriptor::do_create(BLECharacteristic *characteristic) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void BLEDescriptor::set_value(std::vector<uint8_t> value) { this->set_value(value.data(), value.size()); }
|
void BLEDescriptor::set_value(std::vector<uint8_t> value) { this->set_value(value.data(), value.size()); }
|
||||||
void BLEDescriptor::set_value(const std::string &value) { this->set_value((uint8_t *) value.data(), value.length()); }
|
|
||||||
void BLEDescriptor::set_value(uint8_t &data) {
|
|
||||||
uint8_t temp[1];
|
|
||||||
temp[0] = data;
|
|
||||||
this->set_value(temp, 1);
|
|
||||||
}
|
|
||||||
void BLEDescriptor::set_value(uint16_t &data) {
|
|
||||||
uint8_t temp[2];
|
|
||||||
temp[0] = data;
|
|
||||||
temp[1] = data >> 8;
|
|
||||||
this->set_value(temp, 2);
|
|
||||||
}
|
|
||||||
void BLEDescriptor::set_value(uint32_t &data) {
|
|
||||||
uint8_t temp[4];
|
|
||||||
temp[0] = data;
|
|
||||||
temp[1] = data >> 8;
|
|
||||||
temp[2] = data >> 16;
|
|
||||||
temp[3] = data >> 24;
|
|
||||||
this->set_value(temp, 4);
|
|
||||||
}
|
|
||||||
void BLEDescriptor::set_value(int &data) {
|
|
||||||
uint8_t temp[4];
|
|
||||||
temp[0] = data;
|
|
||||||
temp[1] = data >> 8;
|
|
||||||
temp[2] = data >> 16;
|
|
||||||
temp[3] = data >> 24;
|
|
||||||
this->set_value(temp, 4);
|
|
||||||
}
|
|
||||||
void BLEDescriptor::set_value(float &data) {
|
|
||||||
float temp = data;
|
|
||||||
this->set_value((uint8_t *) &temp, 4);
|
|
||||||
}
|
|
||||||
void BLEDescriptor::set_value(double &data) {
|
|
||||||
double temp = data;
|
|
||||||
this->set_value((uint8_t *) &temp, 8);
|
|
||||||
}
|
|
||||||
void BLEDescriptor::set_value(bool &data) {
|
|
||||||
uint8_t temp[1];
|
|
||||||
temp[0] = data;
|
|
||||||
this->set_value(temp, 1);
|
|
||||||
}
|
|
||||||
void BLEDescriptor::set_value(const uint8_t *data, size_t length) {
|
void BLEDescriptor::set_value(const uint8_t *data, size_t length) {
|
||||||
if (length > this->value_.attr_max_len) {
|
if (length > this->value_.attr_max_len) {
|
||||||
ESP_LOGE(TAG, "Size %d too large, must be no bigger than %d", length, this->value_.attr_max_len);
|
ESP_LOGE(TAG, "Size %d too large, must be no bigger than %d", length, this->value_.attr_max_len);
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "esphome/components/esp32_ble/ble_uuid.h"
|
#include "esphome/components/esp32_ble/ble_uuid.h"
|
||||||
|
#include "esphome/core/helpers.h"
|
||||||
|
|
||||||
#ifdef USE_ESP32
|
#ifdef USE_ESP32
|
||||||
|
|
||||||
|
@ -21,15 +22,15 @@ class BLEDescriptor {
|
||||||
void do_create(BLECharacteristic *characteristic);
|
void do_create(BLECharacteristic *characteristic);
|
||||||
|
|
||||||
void set_value(const uint8_t *data, size_t length);
|
void set_value(const uint8_t *data, size_t length);
|
||||||
void set_value(const std::string &value);
|
|
||||||
void set_value(std::vector<uint8_t> value);
|
void set_value(std::vector<uint8_t> value);
|
||||||
void set_value(uint8_t &data);
|
void set_value(const std::string &value) { this->set_value(to_vector(value)); }
|
||||||
void set_value(uint16_t &data);
|
void set_value(uint8_t data) { this->set_value(to_vector(data)); }
|
||||||
void set_value(uint32_t &data);
|
void set_value(uint16_t data) { this->set_value(to_vector(data)); }
|
||||||
void set_value(int &data);
|
void set_value(uint32_t data) { this->set_value(to_vector(data)); }
|
||||||
void set_value(float &data);
|
void set_value(int data) { this->set_value(to_vector(data)); }
|
||||||
void set_value(double &data);
|
void set_value(float data) { this->set_value(to_vector(data)); }
|
||||||
void set_value(bool &data);
|
void set_value(double data) { this->set_value(to_vector(data)); }
|
||||||
|
void set_value(bool data) { this->set_value(to_vector(data)); }
|
||||||
|
|
||||||
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);
|
||||||
|
|
||||||
|
|
|
@ -17,15 +17,6 @@
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace esp32_ble_server {
|
namespace esp32_ble_server {
|
||||||
|
|
||||||
Trigger<std::string> *BLEServerAutomationInterface::create_on_write_trigger(BLECharacteristic *characteristic) {
|
|
||||||
Trigger<std::string> *on_write_trigger = new Trigger<std::string>(); // NOLINT(cppcoreguidelines-owning-memory)
|
|
||||||
characteristic->on_write([on_write_trigger](const std::vector<uint8_t> &data) {
|
|
||||||
std::string value(data.begin(), data.end());
|
|
||||||
on_write_trigger->trigger(value);
|
|
||||||
});
|
|
||||||
return on_write_trigger;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const char *const TAG = "esp32_ble_server";
|
static const char *const TAG = "esp32_ble_server";
|
||||||
|
|
||||||
static const uint16_t DEVICE_INFORMATION_SERVICE_UUID = 0x180A;
|
static const uint16_t DEVICE_INFORMATION_SERVICE_UUID = 0x180A;
|
||||||
|
|
|
@ -26,21 +26,6 @@ namespace esp32_ble_server {
|
||||||
|
|
||||||
using namespace esp32_ble;
|
using namespace esp32_ble;
|
||||||
|
|
||||||
class BLEServerAutomationInterface {
|
|
||||||
public:
|
|
||||||
static Trigger<std::string> *create_on_write_trigger(BLECharacteristic *characteristic);
|
|
||||||
|
|
||||||
template<typename... Ts> class BLECharacteristicSetValueAction : public Action<Ts...> {
|
|
||||||
public:
|
|
||||||
BLECharacteristicSetValueAction(BLECharacteristic *characteristic) : parent_(characteristic) {}
|
|
||||||
TEMPLATABLE_VALUE(std::string, value)
|
|
||||||
void play(Ts... x) override { this->parent_->set_value(this->value_.value(x...)); }
|
|
||||||
|
|
||||||
protected:
|
|
||||||
BLECharacteristic *parent_;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
class BLEServer : public Component, public GATTsEventHandler, public BLEStatusEventHandler, public Parented<ESP32BLE> {
|
class BLEServer : public Component, public GATTsEventHandler, public BLEStatusEventHandler, public Parented<ESP32BLE> {
|
||||||
public:
|
public:
|
||||||
void setup() override;
|
void setup() override;
|
||||||
|
|
|
@ -0,0 +1,33 @@
|
||||||
|
#include "ble_server_automations.h"
|
||||||
|
|
||||||
|
#ifdef USE_ESP32
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace esp32_ble_server {
|
||||||
|
// Interface to interact with ESPHome automations and triggers
|
||||||
|
namespace esp32_ble_server_automations {
|
||||||
|
|
||||||
|
using namespace esp32_ble;
|
||||||
|
|
||||||
|
Trigger<std::vector<uint8_t>> *BLETriggers::create_on_write_trigger(BLECharacteristic *characteristic) {
|
||||||
|
Trigger<std::vector<uint8_t>> *on_write_trigger = new Trigger<std::vector<uint8_t>>(); // NOLINT(cppcoreguidelines-owning-memory)
|
||||||
|
characteristic->EventEmitter<BLECharacteristicEvt::VectorEvt, std::vector<uint8_t>>::on(BLECharacteristicEvt::VectorEvt::ON_WRITE, [on_write_trigger](const std::vector<uint8_t> &data) {
|
||||||
|
on_write_trigger->trigger(data);
|
||||||
|
});
|
||||||
|
return on_write_trigger;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BLECharacteristicSetValueActionManager::set_listener(BLECharacteristic *characteristic, EventEmitterListenerID listener_id) {
|
||||||
|
// Check if there is already a listener for this characteristic
|
||||||
|
if (this->listeners_.find(characteristic) != this->listeners_.end()) {
|
||||||
|
// Remove the previous listener
|
||||||
|
characteristic->EventEmitter<BLECharacteristicEvt::EmptyEvt>::off(BLECharacteristicEvt::EmptyEvt::ON_READ, this->listeners_[characteristic]);
|
||||||
|
}
|
||||||
|
this->listeners_[characteristic] = listener_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace esp32_ble_server_automations
|
||||||
|
} // namespace esp32_ble_server
|
||||||
|
} // namespace esphome
|
||||||
|
|
||||||
|
#endif
|
77
esphome/components/esp32_ble_server/ble_server_automations.h
Normal file
77
esphome/components/esp32_ble_server/ble_server_automations.h
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "ble_characteristic.h"
|
||||||
|
|
||||||
|
#include "esphome/core/event_emitter.h"
|
||||||
|
#include "esphome/core/automation.h"
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
#ifdef USE_ESP32
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace esp32_ble_server {
|
||||||
|
// Interface to interact with ESPHome actions and triggers
|
||||||
|
namespace esp32_ble_server_automations {
|
||||||
|
|
||||||
|
using namespace esp32_ble;
|
||||||
|
|
||||||
|
class BLETriggers {
|
||||||
|
public:
|
||||||
|
static Trigger<std::vector<uint8_t>> *create_on_write_trigger(BLECharacteristic *characteristic);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Class to make sure only one BLECharacteristicSetValueAction is active at a time
|
||||||
|
class BLECharacteristicSetValueActionManager {
|
||||||
|
public:
|
||||||
|
// Singleton pattern
|
||||||
|
static BLECharacteristicSetValueActionManager *get_instance() {
|
||||||
|
static BLECharacteristicSetValueActionManager instance;
|
||||||
|
return &instance;
|
||||||
|
}
|
||||||
|
void set_listener(BLECharacteristic *characteristic, EventEmitterListenerID listener_id);
|
||||||
|
EventEmitterListenerID get_listener(BLECharacteristic *characteristic) { return this->listeners_[characteristic]; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::unordered_map<BLECharacteristic *, EventEmitterListenerID> listeners_;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename... Ts> class BLECharacteristicSetValueAction : public Action<Ts...> {
|
||||||
|
public:
|
||||||
|
BLECharacteristicSetValueAction(BLECharacteristic *characteristic) : parent_(characteristic) {}
|
||||||
|
TEMPLATABLE_VALUE(std::vector<uint8_t>, value)
|
||||||
|
void play(Ts... x) override {
|
||||||
|
// If the listener is already set, do nothing
|
||||||
|
if (BLECharacteristicSetValueActionManager::get_instance()->get_listener(this->parent_) == this->listener_id_)
|
||||||
|
return;
|
||||||
|
// Set initial value
|
||||||
|
this->parent_->set_value(this->value_.value(x...));
|
||||||
|
// Set the listener for read events
|
||||||
|
this->listener_id_ = this->parent_->EventEmitter<BLECharacteristicEvt::EmptyEvt>::on(BLECharacteristicEvt::EmptyEvt::ON_READ, [this, x...](void) {
|
||||||
|
// Set the value of the characteristic every time it is read
|
||||||
|
this->parent_->set_value(this->value_.value(x...));
|
||||||
|
});
|
||||||
|
// Set the listener in the global manager so only one BLECharacteristicSetValueAction is set for each characteristic
|
||||||
|
BLECharacteristicSetValueActionManager::get_instance()->set_listener(this->parent_, this->listener_id_);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
BLECharacteristic *parent_;
|
||||||
|
EventEmitterListenerID listener_id_;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename... Ts> class BLECharacteristicNotifyAction : public Action<Ts...> {
|
||||||
|
public:
|
||||||
|
BLECharacteristicNotifyAction(BLECharacteristic *characteristic) : parent_(characteristic) {}
|
||||||
|
void play(Ts... x) override { this->parent_->notify(); }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
BLECharacteristic *parent_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace esp32_ble_server_automations
|
||||||
|
} // namespace esp32_ble_server
|
||||||
|
} // namespace esphome
|
||||||
|
|
||||||
|
#endif
|
|
@ -40,7 +40,7 @@ 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_->on_write([this](const std::vector<uint8_t> &data) {
|
this->rpc_->EventEmitter<BLECharacteristicEvt::VectorEvt, std::vector<uint8_t>>::on(BLECharacteristicEvt::VectorEvt::ON_WRITE, [this](const std::vector<uint8_t> &data) {
|
||||||
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());
|
||||||
}
|
}
|
||||||
|
|
47
esphome/core/event_emitter.h
Normal file
47
esphome/core/event_emitter.h
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
#pragma once
|
||||||
|
#include <map>
|
||||||
|
#include <vector>
|
||||||
|
#include <functional>
|
||||||
|
#include <string>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
|
||||||
|
using EventEmitterListenerID = uint32_t;
|
||||||
|
|
||||||
|
// EventEmitter class that can emit events with a specific name (usually an enum) and a list of arguments.
|
||||||
|
// Supports multiple listeners for each event.
|
||||||
|
template <typename EvtNames, typename... Args>
|
||||||
|
class EventEmitter {
|
||||||
|
public:
|
||||||
|
EventEmitterListenerID on(EvtNames event, std::function<void(Args...)> listener) {
|
||||||
|
listeners_[event].emplace_back(++current_id_, [listener](Args... args) {
|
||||||
|
listener(args...);
|
||||||
|
});
|
||||||
|
return current_id_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void off(EvtNames event, EventEmitterListenerID id) {
|
||||||
|
if (this->listeners_.count(event) == 0)
|
||||||
|
return;
|
||||||
|
auto &vec = this->listeners_[event];
|
||||||
|
vec.erase(std::remove_if(vec.begin(), vec.end(), [id](const std::pair<EventEmitterListenerID, std::function<void(Args...)>> &pair) {
|
||||||
|
return pair.first == id;
|
||||||
|
}), vec.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void emit(EvtNames event, Args... args) {
|
||||||
|
if (listeners_.count(event) == 0)
|
||||||
|
return;
|
||||||
|
for (const auto &listener : listeners_[event]) {
|
||||||
|
listener.second(args...);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::map<EvtNames, std::vector<std::pair<EventEmitterListenerID, std::function<void(Args...)>>>> listeners_;
|
||||||
|
EventEmitterListenerID current_id_ = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace esphome
|
|
@ -534,6 +534,32 @@ std::vector<uint8_t> base64_decode(const std::string &encoded_string) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<uint8_t> to_vector(bool value) { return {value ? (uint8_t)1 : (uint8_t)0}; }
|
||||||
|
std::vector<uint8_t> to_vector(uint8_t value) { return {value}; }
|
||||||
|
std::vector<uint8_t> to_vector(uint16_t value) {
|
||||||
|
return {uint8_t(value >> 8), uint8_t(value & 0xFF)};
|
||||||
|
}
|
||||||
|
std::vector<uint8_t> to_vector(uint32_t value) {
|
||||||
|
return {uint8_t(value >> 24), uint8_t((value >> 16) & 0xFF), uint8_t((value >> 8) & 0xFF), uint8_t(value & 0xFF)};
|
||||||
|
}
|
||||||
|
std::vector<uint8_t> to_vector(uint64_t value) {
|
||||||
|
return {uint8_t(value >> 56), uint8_t((value >> 48) & 0xFF), uint8_t((value >> 40) & 0xFF),
|
||||||
|
uint8_t((value >> 32) & 0xFF), uint8_t((value >> 24) & 0xFF), uint8_t((value >> 16) & 0xFF),
|
||||||
|
uint8_t((value >> 8) & 0xFF), uint8_t(value & 0xFF)};
|
||||||
|
}
|
||||||
|
std::vector<uint8_t> to_vector(int value) {
|
||||||
|
return to_vector(static_cast<uint32_t>(value));
|
||||||
|
}
|
||||||
|
std::vector<uint8_t> to_vector(float value) {
|
||||||
|
return to_vector(*reinterpret_cast<uint32_t *>(&value));
|
||||||
|
}
|
||||||
|
std::vector<uint8_t> to_vector(double value) {
|
||||||
|
return to_vector(*reinterpret_cast<uint64_t *>(&value));
|
||||||
|
}
|
||||||
|
std::vector<uint8_t> to_vector(const std::string &value) {
|
||||||
|
return std::vector<uint8_t>(value.begin(), value.end());
|
||||||
|
}
|
||||||
|
|
||||||
// Colors
|
// Colors
|
||||||
|
|
||||||
float gamma_correct(float value, float gamma) {
|
float gamma_correct(float value, float gamma) {
|
||||||
|
|
|
@ -441,6 +441,17 @@ std::string base64_encode(const std::vector<uint8_t> &buf);
|
||||||
std::vector<uint8_t> base64_decode(const std::string &encoded_string);
|
std::vector<uint8_t> base64_decode(const std::string &encoded_string);
|
||||||
size_t base64_decode(std::string const &encoded_string, uint8_t *buf, size_t buf_len);
|
size_t base64_decode(std::string const &encoded_string, uint8_t *buf, size_t buf_len);
|
||||||
|
|
||||||
|
/// Create a byte vector multiple types of values.
|
||||||
|
std::vector<uint8_t> to_vector(bool value);
|
||||||
|
std::vector<uint8_t> to_vector(uint8_t value);
|
||||||
|
std::vector<uint8_t> to_vector(uint16_t value);
|
||||||
|
std::vector<uint8_t> to_vector(uint32_t value);
|
||||||
|
std::vector<uint8_t> to_vector(uint64_t value);
|
||||||
|
std::vector<uint8_t> to_vector(int value);
|
||||||
|
std::vector<uint8_t> to_vector(float value);
|
||||||
|
std::vector<uint8_t> to_vector(double value);
|
||||||
|
std::vector<uint8_t> to_vector(const std::string &value);
|
||||||
|
|
||||||
///@}
|
///@}
|
||||||
|
|
||||||
/// @name Colors
|
/// @name Colors
|
||||||
|
|
|
@ -8,26 +8,29 @@ esp32_ble_server:
|
||||||
num_handles: 14
|
num_handles: 14
|
||||||
advertise: false
|
advertise: false
|
||||||
characteristics:
|
characteristics:
|
||||||
- uuid: cad48e28-7fbe-41cf-bae9-d77a6c233423
|
- id: test_notify_characteristic
|
||||||
properties:
|
uuid: cad48e28-7fbe-41cf-bae9-d77a6c233423
|
||||||
- read
|
read: true
|
||||||
|
notify: true
|
||||||
value: [0, 1, 2]
|
value: [0, 1, 2]
|
||||||
|
descriptors:
|
||||||
|
- uuid: cad48e28-7fbe-41cf-bae9-d77a6c111111
|
||||||
|
value: 123.1
|
||||||
- uuid: 2a24b789-7a1b-4535-af3e-ee76a35cc42d
|
- uuid: 2a24b789-7a1b-4535-af3e-ee76a35cc42d
|
||||||
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-ee76a35cc11d
|
||||||
properties:
|
read: true
|
||||||
- read
|
|
||||||
value: "Initial"
|
value: "Initial"
|
||||||
- uuid: 2a24b789-7a1b-4535-af3e-ee76a35cc12d
|
- uuid: 2a24b789-7a1b-4535-af3e-ee76a35cc12d
|
||||||
properties:
|
write: true
|
||||||
- read
|
|
||||||
- write
|
|
||||||
on_write:
|
on_write:
|
||||||
then:
|
then:
|
||||||
- lambda: |-
|
- lambda: |-
|
||||||
ESP_LOGD("BLE", "Received: %s", x.c_str());
|
ESP_LOGD("BLE", "Received: %s", std::string(x.begin(), x.end()).c_str());
|
||||||
- ble_server.characteristic_set_value:
|
- ble_server.characteristic.set_value:
|
||||||
id: test_change_characteristic
|
id: test_change_characteristic
|
||||||
value: !lambda 'return "Echo " + x + "";'
|
value: !lambda 'return {0x00, 0x01, 0x02};'
|
||||||
|
- ble_server.characteristic.notify:
|
||||||
|
id: test_notify_characteristic
|
||||||
|
|
Loading…
Reference in a new issue