Add byte_length

This commit is contained in:
Rapsssito 2024-10-19 13:09:41 +02:00
parent 2d1a90a583
commit 0e05f95b17
2 changed files with 66 additions and 41 deletions

View File

@ -18,6 +18,7 @@ AUTO_LOAD = ["esp32_ble"]
CODEOWNERS = ["@jesserockz", "@clydebarrow", "@Rapsssito"]
DEPENDENCIES = ["esp32"]
CONF_BYTE_LENGTH = "byte_length"
CONF_MANUFACTURER = "manufacturer"
CONF_MANUFACTURER_DATA = "manufacturer_data"
CONF_ADVERTISE = "advertise"
@ -30,6 +31,7 @@ CONF_INDICATE = "indicate"
CONF_WRITE_NO_RESPONSE = "write_no_response"
CONF_DESCRIPTORS = "descriptors"
CONF_VALUE_ACTION_ID_ = "value_action_id_"
CONF_VALUE_BUFFER_ = "value_buffer_"
# Core key to store the global configuration
_KEY_NOTIFY_REQUIRED = "esp32_ble_server_notify_required"
@ -132,6 +134,8 @@ DESCRIPTOR_SCHEMA = cv.Schema(
cv.GenerateID(): cv.declare_id(BLEDescriptor),
cv.Required(CONF_UUID): bt_uuid,
cv.Required(CONF_VALUE): DESCRIPTOR_VALUE_SCHEMA,
cv.Optional(CONF_BYTE_LENGTH): cv.uint16_t,
cv.GenerateID(CONF_VALUE_BUFFER_): cv.declare_id(ByteBuffer),
}
)
@ -141,6 +145,8 @@ SERVICE_CHARACTERISTIC_SCHEMA = cv.Schema(
cv.Required(CONF_UUID): bt_uuid,
cv.Optional(CONF_WRITE_NO_RESPONSE, default=False): cv.boolean,
cv.Optional(CONF_VALUE): CHARACTERISTIC_VALUE_SCHEMA,
cv.Optional(CONF_BYTE_LENGTH): cv.uint16_t,
cv.GenerateID(CONF_VALUE_BUFFER_): cv.declare_id(ByteBuffer),
cv.GenerateID(CONF_VALUE_ACTION_ID_): cv.declare_id(
BLECharacteristicSetValueAction
),
@ -184,10 +190,11 @@ def parse_properties(char_conf):
)
def parse_descriptor_value(value):
# Compute the maximum length of the descriptor value
def _parse_value_(value, buffer_id, byte_length=None):
# Compute the maximum length of the value
# Also parse the value for byte arrays
for val_method in [
for val_method, put_method in zip(
(
cv.boolean,
cv.float_,
cv.uint8_t,
@ -195,24 +202,53 @@ def parse_descriptor_value(value):
cv.uint32_t,
cv.int_,
cv.string,
]:
),
(
"put_bool",
"put_float",
"put_uint8",
"put_uint16",
"put_uint32",
"put_int",
"put_vector",
),
):
try:
val = val_method(value)
buffer = ByteBuffer_ns.wrap(val)
return buffer, buffer.get_capacity()
if byte_length is None:
# If no byte length is specified, use the default length
buffer_var = cg.variable(buffer_id, ByteBuffer_ns.wrap(val))
else:
# Create a buffer with the specified length and add the value
buffer_var = cg.variable(buffer_id, ByteBuffer(byte_length))
if isinstance(val, str):
# Split in characters
val = [ord(c) for c in val]
cg.add(getattr(buffer_var, put_method)(val))
return buffer_var, buffer_var.get_capacity()
except cv.Invalid:
pass
# Assume it's a list of bytes
try:
val = cv.All(cv.ensure_list(cv.uint8_t), cv.Length(min=1))(value)
buffer = ByteBuffer_ns.wrap(cg.std_vector.template(cg.uint8)(val))
return buffer, buffer.get_capacity()
if byte_length is None:
buffer_var = cg.variable(
buffer_id, ByteBuffer_ns.wrap(cg.std_vector.template(cg.uint8)(val))
)
else:
buffer_var = cg.variable(buffer_id, ByteBuffer(byte_length))
cg.add(buffer_var.put_vector(val))
return buffer_var, buffer_var.get_capacity()
except cv.Invalid:
pass
raise cv.Invalid(f"Could not find type for value: {value}")
async def parse_characteristic_value(value, args):
def parse_descriptor_value(value, buffer_id, byte_length=None):
return _parse_value_(value, buffer_id, byte_length)
async def parse_characteristic_value(value, buffer_id, args):
if isinstance(value, cv.Lambda):
return await cg.templatable(
value,
@ -220,27 +256,7 @@ async def parse_characteristic_value(value, args):
ByteBuffer,
ByteBuffer_ns.wrap,
)
for val_method in [
cv.boolean,
cv.float_,
cv.uint8_t,
cv.uint16_t,
cv.uint32_t,
cv.int_,
cv.string,
]:
try:
val = val_method(value)
return ByteBuffer_ns.wrap(val)
except cv.Invalid:
pass
# Assume it's a list of bytes
try:
val = cv.All(cv.ensure_list(cv.uint8_t), cv.Length(min=1))(value)
return ByteBuffer_ns.wrap(cg.std_vector.template(cg.uint8)(val))
except cv.Invalid:
pass
raise cv.Invalid(f"Could not find type for value: {value}")
return _parse_value_(value, buffer_id)[0]
def calculate_num_handles(service_config):
@ -296,6 +312,8 @@ async def to_code(config):
action_conf = {
CONF_ID: char_conf[CONF_ID],
CONF_VALUE: char_conf[CONF_VALUE],
CONF_BYTE_LENGTH: char_conf.get(CONF_BYTE_LENGTH, None),
CONF_VALUE_BUFFER_: char_conf[CONF_VALUE_BUFFER_],
}
value_action = await ble_server_characteristic_set_value(
action_conf,
@ -306,7 +324,9 @@ async def to_code(config):
cg.add(value_action.play())
for descriptor_conf in char_conf[CONF_DESCRIPTORS]:
descriptor_value, max_length = parse_descriptor_value(
descriptor_conf[CONF_VALUE]
descriptor_conf[CONF_VALUE],
descriptor_conf[CONF_VALUE_BUFFER_],
descriptor_conf.get(CONF_BYTE_LENGTH, None),
)
desc_var = cg.new_Pvariable(
descriptor_conf[CONF_ID],
@ -329,13 +349,15 @@ async def to_code(config):
{
cv.Required(CONF_ID): cv.use_id(BLECharacteristic),
cv.Required(CONF_VALUE): CHARACTERISTIC_VALUE_SCHEMA,
cv.Optional(CONF_BYTE_LENGTH): cv.uint16_t,
cv.GenerateID(CONF_VALUE_BUFFER_): cv.declare_id(ByteBuffer),
}
),
)
async def ble_server_characteristic_set_value(config, action_id, template_arg, args):
paren = await cg.get_variable(config[CONF_ID])
var = cg.new_Pvariable(action_id, template_arg, paren)
value = await parse_characteristic_value(config[CONF_VALUE], args)
value = await parse_characteristic_value(config[CONF_VALUE], config[CONF_VALUE_BUFFER_], args)
cg.add(var.set_buffer(value))
return var

View File

@ -1,11 +1,10 @@
esp32_ble_server:
id: ble
id: ble_server
manufacturer_data: [0x72, 0x4, 0x00, 0x23]
manufacturer: ESPHome
model: Test
services:
- uuid: 2a24b789-7aab-4535-af3e-ee76a35cc42d
num_handles: 14
advertise: false
characteristics:
- id: test_notify_characteristic
@ -16,6 +15,7 @@ esp32_ble_server:
descriptors:
- uuid: cad48e28-7fbe-41cf-bae9-d77a6c111111
value: 123.1
byte_length: 100
- uuid: 2a24b789-7a1b-4535-af3e-ee76a35cc42d
advertise: false
characteristics:
@ -23,6 +23,9 @@ esp32_ble_server:
uuid: 2a24b789-7a1b-4535-af3e-ee76a35cc11d
read: true
value: "Initial"
descriptors:
- uuid: cad48e28-7fbe-41cf-bae9-d77a6c111111
value: 0x12
- uuid: 2a24b789-7a1b-4535-af3e-ee76a35cc12d
write: true
on_write: