1
0
mirror of https://github.com/esphome/esphome.git synced 2025-03-15 15:18:16 +00:00

Implement BLE controller

This commit is contained in:
Jesse Hills 2021-05-31 08:49:05 +12:00
parent 9ad9d64ac7
commit 44b24a54b2
No known key found for this signature in database
GPG Key ID: BEAAE804EFD8E83A
7 changed files with 563 additions and 6 deletions

View File

@ -0,0 +1,60 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import esp32_ble_server, logger
from esphome.const import (
CONF_BLE_SERVER_ID,
CONF_ID,
CONF_LEVEL,
CONF_LOGGER,
ESP_PLATFORM_ESP32,
)
AUTO_LOAD = ["esp32_ble_server"]
ESP_PLATFORMS = [ESP_PLATFORM_ESP32]
CODEOWNERS = ["@jesserockz"]
CONFLICTS_WITH = ["esp32_ble_tracker", "esp32_ble_beacon"]
CONF_LOG_LEVEL = "log_level"
esp32_ble_controller_ns = cg.esphome_ns.namespace("esp32_ble_controller")
BLEController = esp32_ble_controller_ns.class_(
"BLEController",
cg.Component,
cg.Controller,
esp32_ble_server.BLEServiceComponent,
)
def validate(config, item_config):
global_level = config[CONF_LOGGER][CONF_LEVEL]
level = item_config.get(CONF_LOG_LEVEL, "DEBUG")
if logger.LOG_LEVEL_SEVERITY.index(level) > logger.LOG_LEVEL_SEVERITY.index(
global_level
):
raise ValueError(
"The esp32_ble_controller log level {} must be less severe than the "
"global log level {}.".format(level, global_level)
)
CONFIG_SCHEMA = cv.Schema(
{
cv.GenerateID(): cv.declare_id(BLEController),
cv.GenerateID(CONF_BLE_SERVER_ID): cv.use_id(esp32_ble_server.BLEServer),
cv.Optional(CONF_LOG_LEVEL): cv.All(
cv.requires_component("logger"),
logger.is_log_level,
),
}
).extend(cv.COMPONENT_SCHEMA)
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
ble_server = await cg.get_variable(config[CONF_BLE_SERVER_ID])
cg.add(ble_server.register_service_component(var))
if CONF_LOG_LEVEL in config:
cg.add(var.set_log_level(logger.LOG_LEVELS[config[CONF_LOG_LEVEL]]))

View File

@ -0,0 +1,387 @@
#include "ble_controller.h"
#include "esphome/core/application.h"
#include "esphome/components/esp32_ble_server/ble_2901.h"
#include "esphome/components/esp32_ble_server/ble_2902.h"
#ifdef USE_LOGGER
#include "esphome/components/logger/logger.h"
#endif
#ifdef ARDUINO_ARCH_ESP32
namespace esphome {
namespace esp32_ble_controller {
static const char *const ESPHOME_SERVICE_UUID = "03774663-d394-496e-8dcd-000000000000";
static const char *const LOGGER_CHARACTERISTIC_UUID = "03774663-d394-496e-8dcd-000000000001";
static const char *const BINARY_SENSOR_SERVICE_UUID = "03774663-d394-496e-8dcd-000100000000";
static const char *const COVER_SERVICE_UUID = "03774663-d394-496e-8dcd-000200000000";
static const char *const FAN_SERVICE_UUID = "03774663-d394-496e-8dcd-000300000000";
static const char *const LIGHT_SERVICE_UUID = "03774663-d394-496e-8dcd-000400000000";
static const char *const SENSOR_SERVICE_UUID = "03774663-d394-496e-8dcd-000500000000";
static const char *const SWITCH_SERVICE_UUID = "03774663-d394-496e-8dcd-000600000000";
static const char *const TEXT_SENSOR_SERVICE_UUID = "03774663-d394-496e-8dcd-000700000000";
static const char *const CLIMATE_SERVICE_UUID = "03774663-d394-496e-8dcd-000800000000";
static const char *const TAG = "esp32_ble_controller";
void BLEController::setup() {
ESP_LOGD(TAG, "Setting up BLE controller");
this->esphome_service_ = global_ble_server->create_service(ESPHOME_SERVICE_UUID);
#ifdef USE_LOGGER
{
this->logger_characteristic_ = this->esphome_service_->create_characteristic(
LOGGER_CHARACTERISTIC_UUID, BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_NOTIFY);
BLEDescriptor *logger_name = new BLE2901("Logger");
this->logger_characteristic_->add_descriptor(logger_name);
BLEDescriptor *descriptor_2902 = new BLE2902();
this->logger_characteristic_->add_descriptor(descriptor_2902);
}
#endif
#ifdef USE_BINARY_SENSOR
{
auto binary_sensors = App.get_binary_sensors();
if (!binary_sensors.empty()) {
this->binary_sensor_service_ = global_ble_server->create_service(BINARY_SENSOR_SERVICE_UUID);
for (auto *obj : binary_sensors) {
std::string uuid = std::string(BINARY_SENSOR_SERVICE_UUID).substr(0, 28);
uuid += uint32_to_string(obj->get_object_id_hash());
BLECharacteristic *characteristic = this->binary_sensor_service_->create_characteristic(
uuid, BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_NOTIFY);
BLEDescriptor *name = new BLE2901(obj->get_name());
characteristic->add_descriptor(name);
BLEDescriptor *descriptor = new BLE2902();
characteristic->add_descriptor(descriptor);
this->characteristics_.insert(
std::pair<uint32_t, BLECharacteristic *>(obj->get_object_id_hash(), characteristic));
}
}
}
#endif
#ifdef USE_COVER
if (!App.get_covers().empty()) {
this->cover_service_ = global_ble_server->create_service(COVER_SERVICE_UUID);
}
#endif
#ifdef USE_FAN
if (!App.get_fans().empty()) {
this->fan_service_ = global_ble_server->create_service(FAN_SERVICE_UUID);
}
#endif
#ifdef USE_LIGHT
if (!App.get_lights().empty()) {
this->light_service_ = global_ble_server->create_service(LIGHT_SERVICE_UUID);
}
#endif
#ifdef USE_SENSOR
{
auto sensors = App.get_sensors();
if (!sensors.empty()) {
this->sensor_service_ = global_ble_server->create_service(SENSOR_SERVICE_UUID);
for (auto *obj : sensors) {
std::string uuid = std::string(SENSOR_SERVICE_UUID).substr(0, 28);
uuid += uint32_to_string(obj->get_object_id_hash());
BLECharacteristic *characteristic = this->sensor_service_->create_characteristic(
uuid, BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_NOTIFY);
BLEDescriptor *name = new BLE2901(obj->get_name());
characteristic->add_descriptor(name);
BLEDescriptor *descriptor = new BLE2902();
characteristic->add_descriptor(descriptor);
this->characteristics_.insert(
std::pair<uint32_t, BLECharacteristic *>(obj->get_object_id_hash(), characteristic));
}
}
}
#endif
#ifdef USE_SWITCH
{
auto switches = App.get_switches();
if (!switches.empty()) {
this->switch_service_ = global_ble_server->create_service(SWITCH_SERVICE_UUID);
for (auto *obj : switches) {
std::string uuid = std::string(SWITCH_SERVICE_UUID).substr(0, 28);
uuid += uint32_to_string(obj->get_object_id_hash());
BLECharacteristic *characteristic = this->switch_service_->create_characteristic(
uuid,
BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_NOTIFY | BLECharacteristic::PROPERTY_WRITE);
BLEDescriptor *name = new BLE2901(obj->get_name());
characteristic->add_descriptor(name);
BLEDescriptor *descriptor = new BLE2902();
characteristic->add_descriptor(descriptor);
this->characteristics_.insert(
std::pair<uint32_t, BLECharacteristic *>(obj->get_object_id_hash(), characteristic));
characteristic->on_write([obj](std::vector<uint8_t> data) {
if (data[0])
obj->turn_on();
else
obj->turn_off();
});
}
}
}
#endif
#ifdef USE_TEXT_SENSOR
{
auto text_sensors = App.get_text_sensors();
if (!text_sensors.empty()) {
this->text_sensor_service_ = global_ble_server->create_service(TEXT_SENSOR_SERVICE_UUID);
for (auto *obj : text_sensors) {
std::string uuid = std::string(TEXT_SENSOR_SERVICE_UUID).substr(0, 28);
uuid += uint32_to_string(obj->get_object_id_hash());
BLECharacteristic *characteristic = this->text_sensor_service_->create_characteristic(
uuid, BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_NOTIFY);
BLEDescriptor *name = new BLE2901(obj->get_name());
characteristic->add_descriptor(name);
BLEDescriptor *descriptor = new BLE2902();
characteristic->add_descriptor(descriptor);
this->characteristics_.insert(
std::pair<uint32_t, BLECharacteristic *>(obj->get_object_id_hash(), characteristic));
}
}
}
#endif
#ifdef USE_CLIMATE
if (!App.get_climates().empty()) {
this->cover_service_ = global_ble_server->create_service(COVER_SERVICE_UUID);
}
#endif
this->state_ = CREATING;
this->setup_controller();
}
void BLEController::loop() {
switch (this->state_) {
case CREATING: {
bool all_created = true;
all_created &= this->esphome_service_->is_created();
#ifdef USE_BINARY_SENSOR
all_created &= this->binary_sensor_service_ == nullptr || this->binary_sensor_service_->is_created();
#endif
#ifdef USE_COVER
all_created &= this->cover_service_ == nullptr || this->cover_service_->is_created();
#endif
#ifdef USE_FAN
all_created &= this->fan_service_ == nullptr || this->fan_service_->is_created();
#endif
#ifdef USE_LIGHT
all_created &= this->light_service_ == nullptr || this->light_service_->is_created();
#endif
#ifdef USE_SENSOR
all_created &= this->sensor_service_ == nullptr || this->sensor_service_->is_created();
#endif
#ifdef USE_SWITCH
all_created &= this->switch_service_ == nullptr || this->switch_service_->is_created();
#endif
#ifdef USE_TEXT_SENSOR
all_created &= this->text_sensor_service_ == nullptr || this->text_sensor_service_->is_created();
#endif
#ifdef USE_CLIMATE
all_created &= this->climate_service_ == nullptr || this->climate_service_->is_created();
#endif
if (all_created) {
ESP_LOGI(TAG, "All services created");
this->state_ = STARTING;
}
break;
}
case STARTING: {
bool all_running = true;
all_running &= this->esphome_service_->is_running();
#ifdef USE_BINARY_SENSOR
all_running &= this->binary_sensor_service_ == nullptr || this->binary_sensor_service_->is_running();
#endif
#ifdef USE_COVER
all_running &= this->cover_service_ == nullptr || this->cover_service_->is_running();
#endif
#ifdef USE_FAN
all_running &= this->fan_service_ == nullptr || this->fan_service_->is_running();
#endif
#ifdef USE_LIGHT
all_running &= this->light_service_ == nullptr || this->light_service_->is_running();
#endif
#ifdef USE_SENSOR
all_running &= this->sensor_service_ == nullptr || this->sensor_service_->is_running();
#endif
#ifdef USE_SWITCH
all_running &= this->switch_service_ == nullptr || this->switch_service_->is_running();
#endif
#ifdef USE_TEXT_SENSOR
all_running &= this->text_sensor_service_ == nullptr || this->text_sensor_service_->is_running();
#endif
#ifdef USE_CLIMATE
all_running &= this->climate_service_ == nullptr || this->climate_service_->is_running();
#endif
if (all_running) {
ESP_LOGD(TAG, "BLE Controller started");
this->state_ = RUNNING;
#ifdef USE_LOGGER
logger::global_logger->add_on_log_callback([this](int level, const char *tag, const char *message) {
if (level > this->log_level_)
return;
std::string log;
log += "[";
log += tag;
log += "] ";
log += message;
this->logger_characteristic_->set_value(log);
this->logger_characteristic_->notify();
});
#endif
} else {
this->esphome_service_->start();
#ifdef USE_BINARY_SENSOR
this->binary_sensor_service_->start();
#endif
#ifdef USE_COVER
this->cover_service_->start();
#endif
#ifdef USE_FAN
this->fan_service_->start();
#endif
#ifdef USE_LIGHT
this->light_service_->start();
#endif
#ifdef USE_SENSOR
this->sensor_service_->start();
#endif
#ifdef USE_SWITCH
this->switch_service_->start();
#endif
#ifdef USE_TEXT_SENSOR
this->text_sensor_service_->start();
#endif
#ifdef USE_CLIMATE
this->climate_service_->start();
#endif
}
break;
}
case RUNNING:
case INIT:
break;
default:
break;
}
}
void BLEController::start() {
if (this->state_ == RUNNING)
return;
this->state_ = STARTING;
}
void BLEController::stop() {
this->esphome_service_->stop();
#ifdef USE_BINARY_SENSOR
this->binary_sensor_service_->stop();
#endif
#ifdef USE_COVER
this->cover_service_->stop();
#endif
#ifdef USE_FAN
this->fan_service_->stop();
#endif
#ifdef USE_LIGHT
this->light_service_->stop();
#endif
#ifdef USE_SENSOR
this->sensor_service_->stop();
#endif
#ifdef USE_SWITCH
this->switch_service_->stop();
#endif
#ifdef USE_TEXT_SENSOR
this->text_sensor_service_->stop();
#endif
#ifdef USE_CLIMATE
this->climate_service_->stop();
#endif
}
float BLEController::get_setup_priority() const { return setup_priority::AFTER_BLUETOOTH; }
#ifdef USE_BINARY_SENSOR
void BLEController::on_binary_sensor_update(binary_sensor::BinarySensor *obj, bool state) {
if (obj->is_internal())
return;
auto *characteristic = this->characteristics_[obj->get_object_id_hash()];
characteristic->set_value(state);
characteristic->notify();
}
#endif
#ifdef USE_COVER
void BLEController::on_cover_update(cover::Cover *obj) {
if (obj->is_internal())
return;
}
#endif
#ifdef USE_FAN
void BLEController::on_fan_update(fan::FanState *obj) {
if (obj->is_internal())
return;
}
#endif
#ifdef USE_LIGHT
void BLEController::on_light_update(light::LightState *obj) {
if (obj->is_internal())
return;
}
#endif
#ifdef USE_SENSOR
void BLEController::on_sensor_update(sensor::Sensor *obj, float state) {
if (obj->is_internal())
return;
auto *characteristic = this->characteristics_[obj->get_object_id_hash()];
characteristic->set_value(state);
characteristic->notify();
}
#endif
#ifdef USE_SWITCH
void BLEController::on_switch_update(switch_::Switch *obj, bool state) {
if (obj->is_internal())
return;
auto *characteristic = this->characteristics_[obj->get_object_id_hash()];
characteristic->set_value(state);
characteristic->notify();
}
#endif
#ifdef USE_TEXT_SENSOR
void BLEController::on_text_sensor_update(text_sensor::TextSensor *obj, const std::string &state) {
if (obj->is_internal())
return;
auto *characteristic = this->characteristics_[obj->get_object_id_hash()];
characteristic->set_value(state);
characteristic->notify();
}
#endif
#ifdef USE_CLIMATE
void BLEController::on_climate_update(climate::Climate *obj) {
if (obj->is_internal())
return;
}
#endif
} // namespace esp32_ble_controller
} // namespace esphome
#endif

View File

@ -0,0 +1,106 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/core/controller.h"
#include "esphome/components/esp32_ble_server/ble_2901.h"
#include "esphome/components/esp32_ble_server/ble_2902.h"
#include "esphome/components/esp32_ble_server/ble_characteristic.h"
#include "esphome/components/esp32_ble_server/ble_server.h"
#include <map>
#ifdef USE_LOGGER
#include "esphome/core/log.h"
#endif
#ifdef ARDUINO_ARCH_ESP32
namespace esphome {
namespace esp32_ble_controller {
using namespace esp32_ble_server;
class BLEController : public Component, public Controller, public esp32_ble_server::BLEServiceComponent {
public:
void loop() override;
void setup() override;
void start() override;
void stop() override;
float get_setup_priority() const override;
#ifdef USE_LOGGER
void set_log_level(int level) { this->log_level_ = level; }
#endif
#ifdef USE_BINARY_SENSOR
void on_binary_sensor_update(binary_sensor::BinarySensor *obj, bool state) override;
#endif
#ifdef USE_COVER
void on_cover_update(cover::Cover *obj) override;
#endif
#ifdef USE_FAN
void on_fan_update(fan::FanState *obj) override;
#endif
#ifdef USE_LIGHT
void on_light_update(light::LightState *obj) override;
#endif
#ifdef USE_SENSOR
void on_sensor_update(sensor::Sensor *obj, float state) override;
#endif
#ifdef USE_SWITCH
void on_switch_update(switch_::Switch *obj, bool state) override;
#endif
#ifdef USE_TEXT_SENSOR
void on_text_sensor_update(text_sensor::TextSensor *obj, const std::string &state) override;
#endif
#ifdef USE_CLIMATE
void on_climate_update(climate::Climate *obj) override;
#endif
protected:
enum State : uint8_t {
FAILED = 0x00,
INIT,
CREATING,
STARTING,
RUNNING,
} state_{INIT};
std::map<uint32_t, BLECharacteristic *> characteristics_;
BLEService *esphome_service_;
#ifdef USE_LOGGER
BLECharacteristic *logger_characteristic_;
int log_level_{ESPHOME_LOG_LEVEL_DEBUG};
#endif
#ifdef USE_BINARY_SENSOR
BLEService *binary_sensor_service_;
#endif
#ifdef USE_COVER
BLEService *cover_service_;
#endif
#ifdef USE_FAN
BLEService *fan_service_;
#endif
#ifdef USE_LIGHT
BLEService *light_service_;
#endif
#ifdef USE_SENSOR
BLEService *sensor_service_;
#endif
#ifdef USE_SWITCH
BLEService *switch_service_;
#endif
#ifdef USE_TEXT_SENSOR
BLEService *text_sensor_service_;
#endif
#ifdef USE_CLIMATE
BLEService *climate_service_;
#endif
};
} // namespace esp32_ble_controller
} // namespace esphome
#endif

View File

@ -61,14 +61,11 @@ void BLEServer::loop() {
break;
}
case STARTING_SERVICE: {
if (!this->device_information_service_->is_created()) {
break;
}
if (this->device_information_service_->is_running()) {
this->state_ = RUNNING;
this->can_proceed_ = true;
ESP_LOGD(TAG, "BLE server setup successfully");
} else if (!this->device_information_service_->is_starting()) {
} else if (this->device_information_service_->is_created() && !this->device_information_service_->is_starting()) {
this->device_information_service_->start();
}
break;

View File

@ -71,9 +71,14 @@ bool BLEService::do_create_characteristics_() {
}
void BLEService::start() {
if (this->running_state_ == RUNNING)
return;
if (this->do_create_characteristics_())
return;
ESP_LOGD(TAG, "Starting BLE service %s", this->uuid_.to_string().c_str());
esp_err_t err = esp_ble_gatts_start_service(this->handle_);
if (err != ESP_OK) {
ESP_LOGE(TAG, "esp_ble_gatts_start_service failed: %d", err);
@ -111,12 +116,14 @@ void BLEService::gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t g
if (this->uuid_ == ESPBTUUID::from_uuid(param->create.service_id.id.uuid) &&
this->inst_id_ == param->create.service_id.id.inst_id) {
this->handle_ = param->create.service_handle;
ESP_LOGI(TAG, "Service %s created", this->uuid_.to_string().c_str());
this->init_state_ = CREATED;
}
break;
}
case ESP_GATTS_START_EVT: {
if (param->start.service_handle == this->handle_) {
ESP_LOGI(TAG, "Service %s started", this->uuid_.to_string().c_str());
this->running_state_ = RUNNING;
}
break;

View File

@ -1,7 +1,7 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import binary_sensor, output, esp32_ble_server
from esphome.const import CONF_ID, ESP_PLATFORM_ESP32
from esphome.const import CONF_BLE_SERVER_ID, CONF_ID, ESP_PLATFORM_ESP32
AUTO_LOAD = ["binary_sensor", "output", "improv", "esp32_ble_server"]
@ -12,7 +12,6 @@ ESP_PLATFORMS = [ESP_PLATFORM_ESP32]
CONF_AUTHORIZED_DURATION = "authorized_duration"
CONF_AUTHORIZER = "authorizer"
CONF_BLE_SERVER_ID = "ble_server_id"
CONF_IDENTIFY_DURATION = "identify_duration"
CONF_STATUS_INDICATOR = "status_indicator"
CONF_WIFI_TIMEOUT = "wifi_timeout"

View File

@ -86,6 +86,7 @@ CONF_BINARY_SENSORS = "binary_sensors"
CONF_BINDKEY = "bindkey"
CONF_BIRTH_MESSAGE = "birth_message"
CONF_BIT_DEPTH = "bit_depth"
CONF_BLE_SERVER_ID = "ble_server_id"
CONF_BLUE = "blue"
CONF_BOARD = "board"
CONF_BOARD_FLASH_MODE = "board_flash_mode"