Introduces ble_client.ble_write Action (#3398)

This commit is contained in:
rbaron 2022-08-08 02:59:55 +02:00 committed by GitHub
parent a12c6b5f35
commit baad92515b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 157 additions and 4 deletions

View File

@ -2,12 +2,15 @@ import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import esp32_ble_tracker
from esphome.const import (
CONF_CHARACTERISTIC_UUID,
CONF_ID,
CONF_MAC_ADDRESS,
CONF_NAME,
CONF_ON_CONNECT,
CONF_ON_DISCONNECT,
CONF_SERVICE_UUID,
CONF_TRIGGER_ID,
CONF_VALUE,
)
from esphome import automation
@ -27,6 +30,8 @@ BLEClientConnectTrigger = ble_client_ns.class_(
BLEClientDisconnectTrigger = ble_client_ns.class_(
"BLEClientDisconnectTrigger", automation.Trigger.template(BLEClientNodeConstRef)
)
# Actions
BLEWriteAction = ble_client_ns.class_("BLEClientWriteAction", automation.Action)
# Espressif platformio framework is built with MAX_BLE_CONN to 3, so
# enforce this in yaml checks.
@ -72,6 +77,33 @@ async def register_ble_node(var, config):
cg.add(parent.register_ble_node(var))
BLE_WRITE_ACTION_SCHEMA = cv.Schema(
{
cv.Required(CONF_ID): cv.use_id(BLEClient),
cv.Required(CONF_SERVICE_UUID): esp32_ble_tracker.bt_uuid,
cv.Required(CONF_CHARACTERISTIC_UUID): esp32_ble_tracker.bt_uuid,
cv.Required(CONF_VALUE): cv.ensure_list(cv.hex_uint8_t),
}
)
@automation.register_action(
"ble_client.ble_write", BLEWriteAction, BLE_WRITE_ACTION_SCHEMA
)
async def ble_write_to_code(config, action_id, template_arg, args):
paren = await cg.get_variable(config[CONF_ID])
var = cg.new_Pvariable(action_id, template_arg, paren)
value = config[CONF_VALUE]
cg.add(var.set_value(value))
serv_uuid128 = esp32_ble_tracker.as_reversed_hex_array(config[CONF_SERVICE_UUID])
cg.add(var.set_service_uuid128(serv_uuid128))
char_uuid128 = esp32_ble_tracker.as_reversed_hex_array(
config[CONF_CHARACTERISTIC_UUID]
)
cg.add(var.set_char_uuid128(char_uuid128))
return var
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)

View File

@ -0,0 +1,74 @@
#include "automation.h"
#include <esp_gap_ble_api.h>
#include <esp_gattc_api.h>
#include <esp_bt_defs.h>
#include "esphome/core/log.h"
namespace esphome {
namespace ble_client {
static const char *const TAG = "ble_client.automation";
void BLEWriterClientNode::write() {
if (this->node_state != espbt::ClientState::ESTABLISHED) {
ESP_LOGW(TAG, "Cannot write to BLE characteristic - not connected");
return;
} else if (this->ble_char_handle_ == 0) {
ESP_LOGW(TAG, "Cannot write to BLE characteristic - characteristic not found");
return;
}
esp_gatt_write_type_t write_type;
if (this->char_props_ & ESP_GATT_CHAR_PROP_BIT_WRITE) {
write_type = ESP_GATT_WRITE_TYPE_RSP;
ESP_LOGD(TAG, "Write type: ESP_GATT_WRITE_TYPE_RSP");
} else if (this->char_props_ & ESP_GATT_CHAR_PROP_BIT_WRITE_NR) {
write_type = ESP_GATT_WRITE_TYPE_NO_RSP;
ESP_LOGD(TAG, "Write type: ESP_GATT_WRITE_TYPE_NO_RSP");
} else {
ESP_LOGE(TAG, "Characteristic %s does not allow writing", this->char_uuid_.to_string().c_str());
return;
}
ESP_LOGVV(TAG, "Will write %d bytes: %s", this->value_.size(), format_hex_pretty(this->value_).c_str());
esp_err_t err = esp_ble_gattc_write_char(this->parent()->gattc_if, this->parent()->conn_id, this->ble_char_handle_,
value_.size(), value_.data(), write_type, ESP_GATT_AUTH_REQ_NONE);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Error writing to characteristic: %s!", esp_err_to_name(err));
}
}
void BLEWriterClientNode::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_REG_EVT:
break;
case ESP_GATTC_OPEN_EVT:
this->node_state = espbt::ClientState::ESTABLISHED;
ESP_LOGD(TAG, "Connection established with %s", ble_client_->address_str().c_str());
break;
case ESP_GATTC_SEARCH_CMPL_EVT: {
auto *chr = this->parent()->get_characteristic(this->service_uuid_, this->char_uuid_);
if (chr == nullptr) {
ESP_LOGW("ble_write_action", "Characteristic %s was not found in service %s",
this->char_uuid_.to_string().c_str(), this->service_uuid_.to_string().c_str());
break;
}
this->ble_char_handle_ = chr->handle;
this->char_props_ = chr->properties;
this->node_state = espbt::ClientState::ESTABLISHED;
ESP_LOGD(TAG, "Found characteristic %s on device %s", this->char_uuid_.to_string().c_str(),
ble_client_->address_str().c_str());
break;
}
case ESP_GATTC_DISCONNECT_EVT:
this->node_state = espbt::ClientState::IDLE;
this->ble_char_handle_ = 0;
ESP_LOGD(TAG, "Disconnected from %s", ble_client_->address_str().c_str());
break;
default:
break;
}
}
} // namespace ble_client
} // namespace esphome

View File

@ -1,5 +1,7 @@
#pragma once
#include <utility>
#include "esphome/core/automation.h"
#include "esphome/components/ble_client/ble_client.h"
@ -33,6 +35,41 @@ class BLEClientDisconnectTrigger : public Trigger<>, public BLEClientNode {
}
};
class BLEWriterClientNode : public BLEClientNode {
public:
BLEWriterClientNode(BLEClient *ble_client) {
ble_client->register_ble_node(this);
ble_client_ = ble_client;
}
void set_value(std::vector<uint8_t> value) { value_ = std::move(value); }
// Attempts to write the contents of value_ to char_uuid_.
void write();
void set_char_uuid128(uint8_t *uuid) { this->char_uuid_ = espbt::ESPBTUUID::from_raw(uuid); }
void set_service_uuid128(uint8_t *uuid) { this->service_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;
private:
BLEClient *ble_client_;
int ble_char_handle_ = 0;
esp_gatt_char_prop_t char_props_;
espbt::ESPBTUUID service_uuid_;
espbt::ESPBTUUID char_uuid_;
std::vector<uint8_t> value_;
};
template<typename... Ts> class BLEClientWriteAction : public Action<Ts...>, public BLEWriterClientNode {
public:
BLEClientWriteAction(BLEClient *ble_client) : BLEWriterClientNode(ble_client) {}
void play(Ts... x) override { return write(); }
};
} // namespace ble_client
} // namespace esphome

View File

@ -1,13 +1,12 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import ble_client, esp32_ble_tracker, output
from esphome.const import CONF_ID, CONF_SERVICE_UUID
from esphome.const import CONF_CHARACTERISTIC_UUID, CONF_ID, CONF_SERVICE_UUID
from .. import ble_client_ns
DEPENDENCIES = ["ble_client"]
CONF_CHARACTERISTIC_UUID = "characteristic_uuid"
CONF_REQUIRE_RESPONSE = "require_response"
BLEBinaryOutput = ble_client_ns.class_(

View File

@ -2,6 +2,7 @@ import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import sensor, ble_client, esp32_ble_tracker
from esphome.const import (
CONF_CHARACTERISTIC_UUID,
CONF_LAMBDA,
CONF_TRIGGER_ID,
CONF_SERVICE_UUID,
@ -11,7 +12,6 @@ from .. import ble_client_ns
DEPENDENCIES = ["ble_client"]
CONF_CHARACTERISTIC_UUID = "characteristic_uuid"
CONF_DESCRIPTOR_UUID = "descriptor_uuid"
CONF_NOTIFY = "notify"

View File

@ -2,6 +2,7 @@ import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import text_sensor, ble_client, esp32_ble_tracker
from esphome.const import (
CONF_CHARACTERISTIC_UUID,
CONF_ID,
CONF_TRIGGER_ID,
CONF_SERVICE_UUID,
@ -11,7 +12,6 @@ from .. import ble_client_ns
DEPENDENCIES = ["ble_client"]
CONF_CHARACTERISTIC_UUID = "characteristic_uuid"
CONF_DESCRIPTOR_UUID = "descriptor_uuid"
CONF_NOTIFY = "notify"

View File

@ -88,6 +88,7 @@ CONF_CERTIFICATE_AUTHORITY = "certificate_authority"
CONF_CHANGE_MODE_EVERY = "change_mode_every"
CONF_CHANNEL = "channel"
CONF_CHANNELS = "channels"
CONF_CHARACTERISTIC_UUID = "characteristic_uuid"
CONF_CHIPSET = "chipset"
CONF_CLEAR_IMPEDANCE = "clear_impedance"
CONF_CLIENT_ID = "client_id"

View File

@ -605,3 +605,13 @@ cap1188:
touch_threshold: 0x20
allow_multiple_touches: true
reset_pin: 14
switch:
- platform: template
name: "Test BLE Write Action"
turn_on_action:
- ble_client.ble_write:
id: airthings01
service_uuid: F61E3BE9-2826-A81B-970A-4D4DECFABBAE
characteristic_uuid: 6490FAFE-0734-732C-8705-91B653A081FC
value: [0x01, 0xab, 0xff]