mirror of
https://github.com/esphome/esphome.git
synced 2024-11-28 17:54:13 +01:00
Initial schema
This commit is contained in:
parent
ba11f2ab0c
commit
73b4b4059a
8 changed files with 232 additions and 31 deletions
|
@ -12,17 +12,92 @@ DEPENDENCIES = ["esp32"]
|
|||
|
||||
CONF_MANUFACTURER = "manufacturer"
|
||||
CONF_MANUFACTURER_DATA = "manufacturer_data"
|
||||
CONF_SERVICES = "services"
|
||||
CONF_UUID = "uuid"
|
||||
CONF_ADVERTISE = "advertise"
|
||||
CONF_NUM_HANDLES = "num_handles"
|
||||
CONF_CHARACTERISTICS = "characteristics"
|
||||
CONF_PROPERTIES = "properties"
|
||||
CONF_VALUE = "value"
|
||||
CONF_DESCRIPTORS = "descriptors"
|
||||
CONF_MAX_LENGTH = "max_length"
|
||||
|
||||
esp32_ble_server_ns = cg.esphome_ns.namespace("esp32_ble_server")
|
||||
ESPBTUUID_ns = cg.esphome_ns.namespace("esp32_ble").namespace("ESPBTUUID")
|
||||
BLECharacteristic_ns = esp32_ble_server_ns.namespace("BLECharacteristic")
|
||||
BLEServer = esp32_ble_server_ns.class_(
|
||||
"BLEServer",
|
||||
cg.Component,
|
||||
esp32_ble.GATTsEventHandler,
|
||||
cg.Parented.template(esp32_ble.ESP32BLE),
|
||||
)
|
||||
BLEServiceComponent = esp32_ble_server_ns.class_("BLEServiceComponent")
|
||||
BLEDescriptor = esp32_ble_server_ns.class_("BLEDescriptor")
|
||||
BLECharacteristic = esp32_ble_server_ns.class_("BLECharacteristic")
|
||||
BLEService = esp32_ble_server_ns.class_("BLEService")
|
||||
|
||||
|
||||
def validate_uuid(value):
|
||||
if len(value) != 36:
|
||||
raise cv.Invalid("UUID must be exactly 36 characters long")
|
||||
return value
|
||||
|
||||
|
||||
# Define a schema for the properties
|
||||
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)
|
||||
|
||||
CHARACTERISTIC_VALUE_SCHEMA = cv.Any(
|
||||
cv.All(cv.ensure_list(cv.hex_uint8_t), cv.Length(min=1)),
|
||||
cv.string,
|
||||
cv.hex_uint8_t,
|
||||
cv.hex_uint16_t,
|
||||
cv.hex_uint32_t,
|
||||
cv.int_,
|
||||
cv.float_,
|
||||
cv.boolean,
|
||||
)
|
||||
|
||||
SERVICE_CHARACTERISTIC_DESCRIPTOR_SCHEMA = cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(BLEDescriptor),
|
||||
cv.Required(CONF_UUID): UUID_SCHEMA,
|
||||
cv.Optional(CONF_MAX_LENGTH, default=0): cv.int_,
|
||||
cv.Required(CONF_VALUE): CHARACTERISTIC_VALUE_SCHEMA,
|
||||
}
|
||||
)
|
||||
|
||||
SERVICE_CHARACTERISTIC_SCHEMA = cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(BLECharacteristic),
|
||||
cv.Required(CONF_UUID): UUID_SCHEMA,
|
||||
cv.Required(CONF_PROPERTIES): PROPERTIES_SCHEMA,
|
||||
cv.Optional(CONF_VALUE): CHARACTERISTIC_VALUE_SCHEMA,
|
||||
cv.Optional(CONF_DESCRIPTORS, default=[]): cv.ensure_list(SERVICE_CHARACTERISTIC_DESCRIPTOR_SCHEMA),
|
||||
}
|
||||
)
|
||||
|
||||
SERVICE_SCHEMA = cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(BLEService),
|
||||
cv.Required(CONF_UUID): UUID_SCHEMA,
|
||||
cv.Optional(CONF_ADVERTISE, default=False): cv.boolean,
|
||||
cv.Optional(CONF_NUM_HANDLES, default=0): cv.int_,
|
||||
cv.Optional(CONF_CHARACTERISTICS, default=[]): cv.ensure_list(SERVICE_CHARACTERISTIC_SCHEMA),
|
||||
}
|
||||
)
|
||||
|
||||
CONFIG_SCHEMA = cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(BLEServer),
|
||||
|
@ -30,10 +105,48 @@ CONFIG_SCHEMA = cv.Schema(
|
|||
cv.Optional(CONF_MANUFACTURER, default="ESPHome"): cv.string,
|
||||
cv.Optional(CONF_MANUFACTURER_DATA): cv.Schema([cv.hex_uint8_t]),
|
||||
cv.Optional(CONF_MODEL): cv.string,
|
||||
cv.Optional(CONF_SERVICES, default=[]): cv.ensure_list(SERVICE_SCHEMA),
|
||||
}
|
||||
).extend(cv.COMPONENT_SCHEMA)
|
||||
|
||||
|
||||
def parse_properties(properties):
|
||||
result = 0
|
||||
for prop in properties:
|
||||
if prop == "READ":
|
||||
result = result | BLECharacteristic_ns.PROPERTY_READ
|
||||
elif prop == "WRITE":
|
||||
result = result | BLECharacteristic_ns.PROPERTY_WRITE
|
||||
elif prop == "NOTIFY":
|
||||
result = result | BLECharacteristic_ns.PROPERTY_NOTIFY
|
||||
elif prop == "BROADCAST":
|
||||
result = result | BLECharacteristic_ns.PROPERTY_BROADCAST
|
||||
elif prop == "INDICATE":
|
||||
result = result | BLECharacteristic_ns.PROPERTY_INDICATE
|
||||
elif prop == "WRITE_NR":
|
||||
result = result | BLECharacteristic_ns.PROPERTY_WRITE_NR
|
||||
return result
|
||||
|
||||
def parse_uuid(uuid):
|
||||
# If the UUID is a string, use from_raw
|
||||
if isinstance(uuid, str):
|
||||
return ESPBTUUID_ns.from_raw(uuid)
|
||||
# Otherwise, use from_uint32
|
||||
return ESPBTUUID_ns.from_uint32(uuid)
|
||||
|
||||
def parse_value(value):
|
||||
if isinstance(value, list):
|
||||
return cg.std_vector.template(cg.uint8)(value)
|
||||
return value
|
||||
|
||||
def calculate_num_handles(service_config):
|
||||
total = 1
|
||||
for characteristic in service_config[CONF_CHARACTERISTICS]:
|
||||
total += 2 # One for the characteristic itself and one for the value
|
||||
for _ in characteristic[CONF_DESCRIPTORS]:
|
||||
total += 1
|
||||
return total
|
||||
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
|
||||
|
@ -43,13 +156,36 @@ async def to_code(config):
|
|||
cg.add(parent.register_gatts_event_handler(var))
|
||||
cg.add(parent.register_ble_status_event_handler(var))
|
||||
cg.add(var.set_parent(parent))
|
||||
|
||||
cg.add(var.set_manufacturer(config[CONF_MANUFACTURER]))
|
||||
if CONF_MANUFACTURER_DATA in config:
|
||||
cg.add(var.set_manufacturer_data(config[CONF_MANUFACTURER_DATA]))
|
||||
if CONF_MODEL in config:
|
||||
cg.add(var.set_model(config[CONF_MODEL]))
|
||||
for service_config in config[CONF_SERVICES]:
|
||||
num_handles = service_config[CONF_NUM_HANDLES]
|
||||
# If num_handles is 0, calculate the optimal number of handles based on the number of characteristics and descriptors
|
||||
if num_handles == 0:
|
||||
num_handles = calculate_num_handles(service_config)
|
||||
service_var = cg.Pvariable(service_config[CONF_ID], var.create_service(
|
||||
parse_uuid(service_config[CONF_UUID]),
|
||||
service_config[CONF_ADVERTISE],
|
||||
num_handles,
|
||||
))
|
||||
for characteristic in service_config[CONF_CHARACTERISTICS]:
|
||||
char_var = cg.Pvariable(characteristic[CONF_ID], service_var.create_characteristic(
|
||||
parse_uuid(characteristic[CONF_UUID]),
|
||||
parse_properties(characteristic[CONF_PROPERTIES])
|
||||
))
|
||||
if CONF_VALUE in characteristic:
|
||||
cg.add(char_var.set_value(parse_value(characteristic[CONF_VALUE])))
|
||||
for descriptor in characteristic[CONF_DESCRIPTORS]:
|
||||
max_length = descriptor[CONF_MAX_LENGTH]
|
||||
# If max_length is 0, calculate the optimal length based on the value
|
||||
if max_length == 0:
|
||||
max_length = len(parse_value(descriptor[CONF_VALUE]))
|
||||
desc_var = cg.new_Pvariable(descriptor[CONF_ID], parse_uuid(descriptor[CONF_UUID]), max_length)
|
||||
if CONF_VALUE in descriptor:
|
||||
cg.add(desc_var.set_value(parse_value(descriptor[CONF_VALUE])))
|
||||
cg.add_define("USE_ESP32_BLE_SERVER")
|
||||
|
||||
if CORE.using_esp_idf:
|
||||
add_idf_sdkconfig_option("CONFIG_BT_ENABLED", True)
|
||||
|
|
|
@ -38,7 +38,52 @@ void BLEDescriptor::do_create(BLECharacteristic *characteristic) {
|
|||
this->state_ = CREATING;
|
||||
}
|
||||
|
||||
void BLEDescriptor::set_value(const std::string &value) { this->set_value((uint8_t *) value.data(), value.length()); }
|
||||
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) {
|
||||
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);
|
||||
|
|
|
@ -20,8 +20,16 @@ class BLEDescriptor {
|
|||
virtual ~BLEDescriptor();
|
||||
void do_create(BLECharacteristic *characteristic);
|
||||
|
||||
void set_value(const std::string &value);
|
||||
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(uint8_t &data);
|
||||
void set_value(uint16_t &data);
|
||||
void set_value(uint32_t &data);
|
||||
void set_value(int &data);
|
||||
void set_value(float &data);
|
||||
void set_value(double &data);
|
||||
void set_value(bool &data);
|
||||
|
||||
void gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param);
|
||||
|
||||
|
|
|
@ -58,9 +58,7 @@ void BLEServer::loop() {
|
|||
pair.second->do_create(this);
|
||||
}
|
||||
if (this->device_information_service_ == nullptr) {
|
||||
this->create_service(ESPBTUUID::from_uint16(DEVICE_INFORMATION_SERVICE_UUID));
|
||||
this->device_information_service_ =
|
||||
this->get_service(ESPBTUUID::from_uint16(DEVICE_INFORMATION_SERVICE_UUID));
|
||||
this->device_information_service_ = this->create_service(ESPBTUUID::from_uint16(DEVICE_INFORMATION_SERVICE_UUID), false, 7);
|
||||
this->create_device_characteristics_();
|
||||
}
|
||||
this->state_ = STARTING_SERVICE;
|
||||
|
@ -115,39 +113,51 @@ bool BLEServer::create_device_characteristics_() {
|
|||
return true;
|
||||
}
|
||||
|
||||
void BLEServer::create_service(ESPBTUUID uuid, bool advertise, uint16_t num_handles, uint8_t inst_id) {
|
||||
BLEService *BLEServer::create_service(ESPBTUUID uuid, bool advertise, uint16_t num_handles) {
|
||||
ESP_LOGV(TAG, "Creating BLE service - %s", uuid.to_string().c_str());
|
||||
// If the service already exists, do nothing
|
||||
BLEService *service = this->get_service(uuid);
|
||||
if (service != nullptr) {
|
||||
ESP_LOGW(TAG, "BLE service %s already exists", uuid.to_string().c_str());
|
||||
return;
|
||||
// Calculate the inst_id for the service
|
||||
uint8_t inst_id = 0;
|
||||
for (; inst_id < 0xFF; inst_id++) {
|
||||
if (this->get_service(uuid, inst_id) == nullptr) {
|
||||
break;
|
||||
}
|
||||
service = new BLEService(uuid, num_handles, inst_id, advertise); // NOLINT(cppcoreguidelines-owning-memory)
|
||||
this->services_.emplace(uuid.to_string(), service);
|
||||
}
|
||||
if (inst_id == 0xFF) {
|
||||
ESP_LOGW(TAG, "Could not create BLE service %s, too many instances", uuid.to_string().c_str());
|
||||
return nullptr;
|
||||
}
|
||||
BLEService *service = new BLEService(uuid, num_handles, inst_id, advertise); // NOLINT(cppcoreguidelines-owning-memory)
|
||||
this->services_.emplace(BLEServer::get_service_key(uuid, inst_id), service);
|
||||
if (this->is_running()) {
|
||||
service->do_create(this);
|
||||
}
|
||||
return service;
|
||||
}
|
||||
|
||||
void BLEServer::remove_service(ESPBTUUID uuid) {
|
||||
ESP_LOGV(TAG, "Removing BLE service - %s", uuid.to_string().c_str());
|
||||
BLEService *service = this->get_service(uuid);
|
||||
void BLEServer::remove_service(ESPBTUUID uuid, uint8_t inst_id) {
|
||||
ESP_LOGV(TAG, "Removing BLE service - %s %d", uuid.to_string().c_str(), inst_id);
|
||||
BLEService *service = this->get_service(uuid, inst_id);
|
||||
if (service == nullptr) {
|
||||
ESP_LOGW(TAG, "BLE service %s not found", uuid.to_string().c_str());
|
||||
ESP_LOGW(TAG, "BLE service %s %d does not exist", uuid.to_string().c_str(), inst_id);
|
||||
return;
|
||||
}
|
||||
service->do_delete();
|
||||
delete service; // NOLINT(cppcoreguidelines-owning-memory)
|
||||
this->services_.erase(uuid.to_string());
|
||||
this->services_.erase(BLEServer::get_service_key(uuid, inst_id));
|
||||
}
|
||||
|
||||
BLEService *BLEServer::get_service(ESPBTUUID uuid) {
|
||||
BLEService *BLEServer::get_service(ESPBTUUID uuid, uint8_t inst_id) {
|
||||
BLEService *service = nullptr;
|
||||
if (this->services_.count(uuid.to_string()) > 0) {
|
||||
service = this->services_.at(uuid.to_string());
|
||||
if (this->services_.count(BLEServer::get_service_key(uuid, inst_id)) > 0) {
|
||||
service = this->services_.at(BLEServer::get_service_key(uuid, inst_id));
|
||||
}
|
||||
return service;
|
||||
}
|
||||
|
||||
std::string BLEServer::get_service_key(ESPBTUUID uuid, uint8_t inst_id) {
|
||||
return uuid.to_string() + std::to_string(inst_id);
|
||||
}
|
||||
|
||||
void BLEServer::gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if,
|
||||
esp_ble_gatts_cb_param_t *param) {
|
||||
switch (event) {
|
||||
|
|
|
@ -51,9 +51,9 @@ class BLEServer : public Component, public GATTsEventHandler, public BLEStatusEv
|
|||
this->restart_advertising_();
|
||||
}
|
||||
|
||||
void create_service(ESPBTUUID uuid, bool advertise = false, uint16_t num_handles = 15, uint8_t inst_id = 0);
|
||||
void remove_service(ESPBTUUID uuid);
|
||||
BLEService *get_service(ESPBTUUID uuid);
|
||||
BLEService *create_service(ESPBTUUID uuid, bool advertise = false, uint16_t num_handles = 15);
|
||||
void remove_service(ESPBTUUID uuid, uint8_t inst_id = 0);
|
||||
BLEService *get_service(ESPBTUUID uuid, uint8_t inst_id = 0);
|
||||
|
||||
esp_gatt_if_t get_gatts_if() { return this->gatts_if_; }
|
||||
uint32_t get_connected_client_count() { return this->connected_clients_; }
|
||||
|
@ -67,6 +67,7 @@ class BLEServer : public Component, public GATTsEventHandler, public BLEStatusEv
|
|||
void register_service_component(BLEServiceComponent *component) { this->service_components_.push_back(component); }
|
||||
|
||||
protected:
|
||||
static std::string get_service_key(ESPBTUUID uuid, uint8_t inst_id);
|
||||
bool create_device_characteristics_();
|
||||
void restart_advertising_();
|
||||
|
||||
|
|
|
@ -32,6 +32,7 @@ class BLEService {
|
|||
BLECharacteristic *create_characteristic(ESPBTUUID uuid, esp_gatt_char_prop_t properties);
|
||||
|
||||
ESPBTUUID get_uuid() { return this->uuid_; }
|
||||
uint8_t get_inst_id() { return this->inst_id_; }
|
||||
BLECharacteristic *get_last_created_characteristic() { return this->last_created_characteristic_; }
|
||||
uint16_t get_handle() { return this->handle_; }
|
||||
|
||||
|
|
|
@ -17,8 +17,9 @@ CONF_STATUS_INDICATOR = "status_indicator"
|
|||
CONF_WIFI_TIMEOUT = "wifi_timeout"
|
||||
|
||||
esp32_improv_ns = cg.esphome_ns.namespace("esp32_improv")
|
||||
BLEServiceComponent = cg.esphome_ns.namespace("esp32_ble_server").class_("BLEServiceComponent")
|
||||
ESP32ImprovComponent = esp32_improv_ns.class_(
|
||||
"ESP32ImprovComponent", cg.Component, esp32_ble_server.BLEServiceComponent
|
||||
"ESP32ImprovComponent", cg.Component, BLEServiceComponent
|
||||
)
|
||||
|
||||
|
||||
|
|
|
@ -75,8 +75,7 @@ void ESP32ImprovComponent::loop() {
|
|||
if (this->service_ == nullptr) {
|
||||
// Setup the service
|
||||
ESP_LOGD(TAG, "Creating Improv service");
|
||||
global_ble_server->create_service(ESPBTUUID::from_raw(improv::SERVICE_UUID), true);
|
||||
this->service_ = global_ble_server->get_service(ESPBTUUID::from_raw(improv::SERVICE_UUID));
|
||||
this->service_ = global_ble_server->create_service(ESPBTUUID::from_raw(improv::SERVICE_UUID), true);
|
||||
this->setup_characteristics();
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue