diff --git a/esphome/components/ble_client/ble_client.cpp b/esphome/components/ble_client/ble_client.cpp index e6cdb0c23d..407f1a1d17 100644 --- a/esphome/components/ble_client/ble_client.cpp +++ b/esphome/components/ble_client/ble_client.cpp @@ -388,6 +388,15 @@ BLEDescriptor *BLECharacteristic::get_descriptor(uint16_t uuid) { return this->get_descriptor(espbt::ESPBTUUID::from_uint16(uuid)); } +void BLECharacteristic::write_value(uint8_t *new_val, int16_t new_val_size) { + auto client = this->service->client; + auto status = esp_ble_gattc_write_char(client->gattc_if, client->conn_id, this->handle, new_val_size, new_val, + ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE); + if (status) { + ESP_LOGW(TAG, "Error sending write value to BLE gattc server, status=%d", status); + } +} + } // namespace ble_client } // namespace esphome diff --git a/esphome/components/ble_client/ble_client.h b/esphome/components/ble_client/ble_client.h index 23123914e8..5680b69f72 100644 --- a/esphome/components/ble_client/ble_client.h +++ b/esphome/components/ble_client/ble_client.h @@ -59,7 +59,7 @@ class BLECharacteristic { void parse_descriptors(); BLEDescriptor *get_descriptor(espbt::ESPBTUUID uuid); BLEDescriptor *get_descriptor(uint16_t uuid); - + void write_value(uint8_t *new_val, int16_t new_val_size); BLEService *service; }; diff --git a/esphome/components/ble_client/output/__init__.py b/esphome/components/ble_client/output/__init__.py new file mode 100644 index 0000000000..fe5835ca82 --- /dev/null +++ b/esphome/components/ble_client/output/__init__.py @@ -0,0 +1,67 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.components import output, ble_client, esp32_ble_tracker +from esphome.const import CONF_ID, CONF_SERVICE_UUID +from .. import ble_client_ns + + +DEPENDENCIES = ["ble_client"] + +CONF_CHARACTERISTIC_UUID = "characteristic_uuid" + +BLEBinaryOutput = ble_client_ns.class_( + "BLEBinaryOutput", output.BinaryOutput, ble_client.BLEClientNode, cg.Component +) + +CONFIG_SCHEMA = cv.All( + output.BINARY_OUTPUT_SCHEMA.extend( + { + cv.Required(CONF_ID): cv.declare_id(BLEBinaryOutput), + cv.Required(CONF_SERVICE_UUID): esp32_ble_tracker.bt_uuid, + cv.Required(CONF_CHARACTERISTIC_UUID): esp32_ble_tracker.bt_uuid, + } + ) + .extend(cv.COMPONENT_SCHEMA) + .extend(ble_client.BLE_CLIENT_SCHEMA) +) + + +def to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + if len(config[CONF_SERVICE_UUID]) == len(esp32_ble_tracker.bt_uuid16_format): + cg.add( + var.set_service_uuid16(esp32_ble_tracker.as_hex(config[CONF_SERVICE_UUID])) + ) + elif len(config[CONF_SERVICE_UUID]) == len(esp32_ble_tracker.bt_uuid32_format): + cg.add( + var.set_service_uuid32(esp32_ble_tracker.as_hex(config[CONF_SERVICE_UUID])) + ) + elif len(config[CONF_SERVICE_UUID]) == len(esp32_ble_tracker.bt_uuid128_format): + uuid128 = esp32_ble_tracker.as_reversed_hex_array(config[CONF_SERVICE_UUID]) + cg.add(var.set_service_uuid128(uuid128)) + + if len(config[CONF_CHARACTERISTIC_UUID]) == len(esp32_ble_tracker.bt_uuid16_format): + cg.add( + var.set_char_uuid16( + esp32_ble_tracker.as_hex(config[CONF_CHARACTERISTIC_UUID]) + ) + ) + elif len(config[CONF_CHARACTERISTIC_UUID]) == len( + esp32_ble_tracker.bt_uuid32_format + ): + cg.add( + var.set_char_uuid32( + esp32_ble_tracker.as_hex(config[CONF_CHARACTERISTIC_UUID]) + ) + ) + elif len(config[CONF_CHARACTERISTIC_UUID]) == len( + esp32_ble_tracker.bt_uuid128_format + ): + uuid128 = esp32_ble_tracker.as_reversed_hex_array( + config[CONF_CHARACTERISTIC_UUID] + ) + cg.add(var.set_char_uuid128(uuid128)) + + yield output.register_output(var, config) + yield ble_client.register_ble_node(var, config) + yield cg.register_component(var, config) diff --git a/esphome/components/ble_client/output/ble_binary_output.cpp b/esphome/components/ble_client/output/ble_binary_output.cpp new file mode 100644 index 0000000000..ff3711e842 --- /dev/null +++ b/esphome/components/ble_client/output/ble_binary_output.cpp @@ -0,0 +1,71 @@ +#include "ble_binary_output.h" +#include "esphome/core/log.h" +#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h" + +#ifdef USE_ESP32 +namespace esphome { +namespace ble_client { + +static const char *const TAG = "ble_binary_output"; + +void BLEBinaryOutput::dump_config() { + ESP_LOGCONFIG(TAG, "BLE Binary Output:"); + ESP_LOGCONFIG(TAG, " MAC address : %s", this->parent_->address_str().c_str()); + ESP_LOGCONFIG(TAG, " Service UUID : %s", this->service_uuid_.to_string().c_str()); + ESP_LOGCONFIG(TAG, " Characteristic UUID: %s", this->char_uuid_.to_string().c_str()); + LOG_BINARY_OUTPUT(this); +} + +void BLEBinaryOutput::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, + esp_ble_gattc_cb_param_t *param) { + switch (event) { + case ESP_GATTC_OPEN_EVT: + this->client_state_ = espbt::ClientState::ESTABLISHED; + ESP_LOGW(TAG, "[%s] Connected successfully!", this->char_uuid_.to_string().c_str()); + break; + case ESP_GATTC_DISCONNECT_EVT: + ESP_LOGW(TAG, "[%s] Disconnected", this->char_uuid_.to_string().c_str()); + this->client_state_ = espbt::ClientState::IDLE; + break; + case ESP_GATTC_WRITE_CHAR_EVT: { + if (param->write.status == 0) { + break; + } + + auto chr = this->parent()->get_characteristic(this->service_uuid_, this->char_uuid_); + if (chr == nullptr) { + ESP_LOGW(TAG, "[%s] Characteristic not found.", this->char_uuid_.to_string().c_str()); + break; + } + if (param->write.handle == chr->handle) { + ESP_LOGW(TAG, "[%s] Write error, status=%d", this->char_uuid_.to_string().c_str(), param->write.status); + } + break; + } + default: + break; + } +} + +void BLEBinaryOutput::write_state(bool state) { + if (this->client_state_ != espbt::ClientState::ESTABLISHED) { + ESP_LOGW(TAG, "[%s] Not connected to BLE client. State update can not be written.", + this->char_uuid_.to_string().c_str()); + return; + } + + auto chr = this->parent()->get_characteristic(this->service_uuid_, this->char_uuid_); + if (chr == nullptr) { + ESP_LOGW(TAG, "[%s] Characteristic not found. State update can not be written.", + this->char_uuid_.to_string().c_str()); + return; + } + + uint8_t state_as_uint = (uint8_t) state; + ESP_LOGV(TAG, "[%s] Write State: %d", this->char_uuid_.to_string().c_str(), state_as_uint); + chr->write_value(&state_as_uint, sizeof(state_as_uint)); +} + +} // namespace ble_client +} // namespace esphome +#endif diff --git a/esphome/components/ble_client/output/ble_binary_output.h b/esphome/components/ble_client/output/ble_binary_output.h new file mode 100644 index 0000000000..e1d62a267b --- /dev/null +++ b/esphome/components/ble_client/output/ble_binary_output.h @@ -0,0 +1,39 @@ +#pragma once + +#include "esphome/core/component.h" +#include "esphome/components/ble_client/ble_client.h" +#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h" +#include "esphome/components/output/binary_output.h" + +#ifdef USE_ESP32 +#include +namespace esphome { +namespace ble_client { + +namespace espbt = esphome::esp32_ble_tracker; + +class BLEBinaryOutput : public output::BinaryOutput, public BLEClientNode, public Component { + public: + void dump_config() override; + void loop() override {} + float get_setup_priority() const override { return setup_priority::DATA; } + void set_service_uuid16(uint16_t uuid) { this->service_uuid_ = espbt::ESPBTUUID::from_uint16(uuid); } + void set_service_uuid32(uint32_t uuid) { this->service_uuid_ = espbt::ESPBTUUID::from_uint32(uuid); } + void set_service_uuid128(uint8_t *uuid) { this->service_uuid_ = espbt::ESPBTUUID::from_raw(uuid); } + void set_char_uuid16(uint16_t uuid) { this->char_uuid_ = espbt::ESPBTUUID::from_uint16(uuid); } + void set_char_uuid32(uint32_t uuid) { this->char_uuid_ = espbt::ESPBTUUID::from_uint32(uuid); } + void set_char_uuid128(uint8_t *uuid) { this->char_uuid_ = espbt::ESPBTUUID::from_raw(uuid); } + void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, + esp_ble_gattc_cb_param_t *param) override; + + protected: + void write_state(bool state) override; + espbt::ESPBTUUID service_uuid_; + espbt::ESPBTUUID char_uuid_; + espbt::ClientState client_state_; +}; + +} // namespace ble_client +} // namespace esphome + +#endif