ESPHome  2024.4.1
automation.h
Go to the documentation of this file.
1 #pragma once
2 
3 #ifdef USE_ESP32
4 
5 #include <utility>
6 #include <vector>
7 
10 #include "esphome/core/log.h"
11 
12 namespace esphome {
13 namespace ble_client {
14 
15 // placeholder class for static TAG .
16 class Automation {
17  public:
18  // could be made inline with C++17
19  static const char *const TAG;
20 };
21 
22 // implement on_connect automation.
23 class BLEClientConnectTrigger : public Trigger<>, public BLEClientNode {
24  public:
25  explicit BLEClientConnectTrigger(BLEClient *parent) { parent->register_ble_node(this); }
26  void loop() override {}
27  void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
28  esp_ble_gattc_cb_param_t *param) override {
29  if (event == ESP_GATTC_SEARCH_CMPL_EVT) {
30  this->node_state = espbt::ClientState::ESTABLISHED;
31  this->trigger();
32  }
33  }
34 };
35 
36 // on_disconnect automation
38  public:
39  explicit BLEClientDisconnectTrigger(BLEClient *parent) { parent->register_ble_node(this); }
40  void loop() override {}
41  void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
42  esp_ble_gattc_cb_param_t *param) override {
43  // test for CLOSE and not DISCONNECT - DISCONNECT can occur even if no virtual connection (OPEN event) occurred.
44  // So this will not trigger unless a complete open has previously succeeded.
45  switch (event) {
46  case ESP_GATTC_SEARCH_CMPL_EVT: {
47  this->node_state = espbt::ClientState::ESTABLISHED;
48  break;
49  }
50  case ESP_GATTC_CLOSE_EVT: {
51  this->trigger();
52  break;
53  }
54  default: {
55  break;
56  }
57  }
58  }
59 };
60 
62  public:
63  explicit BLEClientPasskeyRequestTrigger(BLEClient *parent) { parent->register_ble_node(this); }
64  void loop() override {}
65  void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) override {
66  if (event == ESP_GAP_BLE_PASSKEY_REQ_EVT && this->parent_->check_addr(param->ble_security.auth_cmpl.bd_addr))
67  this->trigger();
68  }
69 };
70 
71 class BLEClientPasskeyNotificationTrigger : public Trigger<uint32_t>, public BLEClientNode {
72  public:
74  void loop() override {}
75  void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) override {
76  if (event == ESP_GAP_BLE_PASSKEY_NOTIF_EVT && this->parent_->check_addr(param->ble_security.auth_cmpl.bd_addr)) {
77  this->trigger(param->ble_security.key_notif.passkey);
78  }
79  }
80 };
81 
83  public:
85  void loop() override {}
86  void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) override {
87  if (event == ESP_GAP_BLE_NC_REQ_EVT && this->parent_->check_addr(param->ble_security.auth_cmpl.bd_addr)) {
88  this->trigger(param->ble_security.key_notif.passkey);
89  }
90  }
91 };
92 
93 // implement the ble_client.ble_write action.
94 template<typename... Ts> class BLEClientWriteAction : public Action<Ts...>, public BLEClientNode {
95  public:
97  ble_client->register_ble_node(this);
98  ble_client_ = ble_client;
99  }
100 
101  void set_service_uuid16(uint16_t uuid) { this->service_uuid_ = espbt::ESPBTUUID::from_uint16(uuid); }
102  void set_service_uuid32(uint32_t uuid) { this->service_uuid_ = espbt::ESPBTUUID::from_uint32(uuid); }
103  void set_service_uuid128(uint8_t *uuid) { this->service_uuid_ = espbt::ESPBTUUID::from_raw(uuid); }
104 
105  void set_char_uuid16(uint16_t uuid) { this->char_uuid_ = espbt::ESPBTUUID::from_uint16(uuid); }
106  void set_char_uuid32(uint32_t uuid) { this->char_uuid_ = espbt::ESPBTUUID::from_uint32(uuid); }
107  void set_char_uuid128(uint8_t *uuid) { this->char_uuid_ = espbt::ESPBTUUID::from_raw(uuid); }
108 
109  void set_value_template(std::function<std::vector<uint8_t>(Ts...)> func) {
110  this->value_template_ = std::move(func);
111  has_simple_value_ = false;
112  }
113 
114  void set_value_simple(const std::vector<uint8_t> &value) {
115  this->value_simple_ = value;
116  has_simple_value_ = true;
117  }
118 
119  void play(Ts... x) override {}
120 
121  void play_complex(Ts... x) override {
122  this->num_running_++;
123  this->var_ = std::make_tuple(x...);
124  auto value = this->has_simple_value_ ? this->value_simple_ : this->value_template_(x...);
125  // on write failure, continue the automation chain rather than stopping so that e.g. disconnect can work.
126  if (!write(value))
127  this->play_next_(x...);
128  }
129 
138  // initiate the write. Return true if all went well, will be followed by a WRITE_CHAR event.
139  bool write(const std::vector<uint8_t> &value) {
140  if (this->node_state != espbt::ClientState::ESTABLISHED) {
141  esph_log_w(Automation::TAG, "Cannot write to BLE characteristic - not connected");
142  return false;
143  }
144  esph_log_vv(Automation::TAG, "Will write %d bytes: %s", value.size(), format_hex_pretty(value).c_str());
145  esp_err_t err = esp_ble_gattc_write_char(this->parent()->get_gattc_if(), this->parent()->get_conn_id(),
146  this->char_handle_, value.size(), const_cast<uint8_t *>(value.data()),
147  this->write_type_, ESP_GATT_AUTH_REQ_NONE);
148  if (err != ESP_OK) {
149  esph_log_e(Automation::TAG, "Error writing to characteristic: %s!", esp_err_to_name(err));
150  return false;
151  }
152  return true;
153  }
154 
155  void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
156  esp_ble_gattc_cb_param_t *param) override {
157  switch (event) {
158  case ESP_GATTC_WRITE_CHAR_EVT:
159  // upstream code checked the MAC address, verify the characteristic.
160  if (param->write.handle == this->char_handle_)
161  this->parent()->run_later([this]() { this->play_next_tuple_(this->var_); });
162  break;
163  case ESP_GATTC_DISCONNECT_EVT:
164  if (this->num_running_ != 0)
165  this->stop_complex();
166  break;
167  case ESP_GATTC_SEARCH_CMPL_EVT: {
168  auto *chr = this->parent()->get_characteristic(this->service_uuid_, this->char_uuid_);
169  if (chr == nullptr) {
170  esph_log_w("ble_write_action", "Characteristic %s was not found in service %s",
171  this->char_uuid_.to_string().c_str(), this->service_uuid_.to_string().c_str());
172  break;
173  }
174  this->char_handle_ = chr->handle;
175  this->char_props_ = chr->properties;
176  if (this->char_props_ & ESP_GATT_CHAR_PROP_BIT_WRITE) {
177  this->write_type_ = ESP_GATT_WRITE_TYPE_RSP;
178  esph_log_d(Automation::TAG, "Write type: ESP_GATT_WRITE_TYPE_RSP");
179  } else if (this->char_props_ & ESP_GATT_CHAR_PROP_BIT_WRITE_NR) {
180  this->write_type_ = ESP_GATT_WRITE_TYPE_NO_RSP;
181  esph_log_d(Automation::TAG, "Write type: ESP_GATT_WRITE_TYPE_NO_RSP");
182  } else {
183  esph_log_e(Automation::TAG, "Characteristic %s does not allow writing", this->char_uuid_.to_string().c_str());
184  break;
185  }
186  this->node_state = espbt::ClientState::ESTABLISHED;
187  esph_log_d(Automation::TAG, "Found characteristic %s on device %s", this->char_uuid_.to_string().c_str(),
188  ble_client_->address_str().c_str());
189  break;
190  }
191  default:
192  break;
193  }
194  }
195 
196  private:
197  BLEClient *ble_client_;
198  bool has_simple_value_ = true;
199  std::vector<uint8_t> value_simple_;
200  std::function<std::vector<uint8_t>(Ts...)> value_template_{};
201  espbt::ESPBTUUID service_uuid_;
202  espbt::ESPBTUUID char_uuid_;
203  std::tuple<Ts...> var_{};
204  uint16_t char_handle_{};
205  esp_gatt_char_prop_t char_props_{};
206  esp_gatt_write_type_t write_type_{};
207 };
208 
209 template<typename... Ts> class BLEClientPasskeyReplyAction : public Action<Ts...> {
210  public:
211  BLEClientPasskeyReplyAction(BLEClient *ble_client) { parent_ = ble_client; }
212 
213  void play(Ts... x) override {
214  uint32_t passkey;
215  if (has_simple_value_) {
216  passkey = this->value_simple_;
217  } else {
218  passkey = this->value_template_(x...);
219  }
220  if (passkey > 999999)
221  return;
222  esp_bd_addr_t remote_bda;
223  memcpy(remote_bda, parent_->get_remote_bda(), sizeof(esp_bd_addr_t));
224  esp_ble_passkey_reply(remote_bda, true, passkey);
225  }
226 
227  void set_value_template(std::function<uint32_t(Ts...)> func) {
228  this->value_template_ = std::move(func);
229  has_simple_value_ = false;
230  }
231 
232  void set_value_simple(const uint32_t &value) {
233  this->value_simple_ = value;
234  has_simple_value_ = true;
235  }
236 
237  private:
238  BLEClient *parent_{nullptr};
239  bool has_simple_value_ = true;
240  uint32_t value_simple_{0};
241  std::function<uint32_t(Ts...)> value_template_{};
242 };
243 
244 template<typename... Ts> class BLEClientNumericComparisonReplyAction : public Action<Ts...> {
245  public:
246  BLEClientNumericComparisonReplyAction(BLEClient *ble_client) { parent_ = ble_client; }
247 
248  void play(Ts... x) override {
249  esp_bd_addr_t remote_bda;
250  memcpy(remote_bda, parent_->get_remote_bda(), sizeof(esp_bd_addr_t));
251  if (has_simple_value_) {
252  esp_ble_confirm_reply(remote_bda, this->value_simple_);
253  } else {
254  esp_ble_confirm_reply(remote_bda, this->value_template_(x...));
255  }
256  }
257 
258  void set_value_template(std::function<bool(Ts...)> func) {
259  this->value_template_ = std::move(func);
260  has_simple_value_ = false;
261  }
262 
263  void set_value_simple(const bool &value) {
264  this->value_simple_ = value;
265  has_simple_value_ = true;
266  }
267 
268  private:
269  BLEClient *parent_{nullptr};
270  bool has_simple_value_ = true;
271  bool value_simple_{false};
272  std::function<bool(Ts...)> value_template_{};
273 };
274 
275 template<typename... Ts> class BLEClientRemoveBondAction : public Action<Ts...> {
276  public:
277  BLEClientRemoveBondAction(BLEClient *ble_client) { parent_ = ble_client; }
278 
279  void play(Ts... x) override {
280  esp_bd_addr_t remote_bda;
281  memcpy(remote_bda, parent_->get_remote_bda(), sizeof(esp_bd_addr_t));
282  esp_ble_remove_bond_device(remote_bda);
283  }
284 
285  private:
286  BLEClient *parent_{nullptr};
287 };
288 
289 template<typename... Ts> class BLEClientConnectAction : public Action<Ts...>, public BLEClientNode {
290  public:
292  ble_client->register_ble_node(this);
293  ble_client_ = ble_client;
294  }
295  void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
296  esp_ble_gattc_cb_param_t *param) override {
297  if (this->num_running_ == 0)
298  return;
299  switch (event) {
300  case ESP_GATTC_SEARCH_CMPL_EVT:
301  this->node_state = espbt::ClientState::ESTABLISHED;
302  this->parent()->run_later([this]() { this->play_next_tuple_(this->var_); });
303  break;
304  // if the connection is closed, terminate the automation chain.
305  case ESP_GATTC_DISCONNECT_EVT:
306  this->stop_complex();
307  break;
308  default:
309  break;
310  }
311  }
312 
313  // not used since we override play_complex_
314  void play(Ts... x) override {}
315 
316  void play_complex(Ts... x) override {
317  // it makes no sense to have multiple instances of this running at the same time.
318  // this would occur only if the same automation was re-triggered while still
319  // running. So just cancel the second chain if this is detected.
320  if (this->num_running_ != 0) {
321  this->stop_complex();
322  return;
323  }
324  this->num_running_++;
325  if (this->node_state == espbt::ClientState::ESTABLISHED) {
326  this->play_next_(x...);
327  } else {
328  this->var_ = std::make_tuple(x...);
329  this->ble_client_->connect();
330  }
331  }
332 
333  private:
334  BLEClient *ble_client_;
335  std::tuple<Ts...> var_{};
336 };
337 
338 template<typename... Ts> class BLEClientDisconnectAction : public Action<Ts...>, public BLEClientNode {
339  public:
341  ble_client->register_ble_node(this);
342  ble_client_ = ble_client;
343  }
344  void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
345  esp_ble_gattc_cb_param_t *param) override {
346  if (this->num_running_ == 0)
347  return;
348  switch (event) {
349  case ESP_GATTC_CLOSE_EVT:
350  case ESP_GATTC_DISCONNECT_EVT:
351  this->parent()->run_later([this]() { this->play_next_tuple_(this->var_); });
352  break;
353  default:
354  break;
355  }
356  }
357 
358  // not used since we override play_complex_
359  void play(Ts... x) override {}
360 
361  void play_complex(Ts... x) override {
362  this->num_running_++;
363  if (this->node_state == espbt::ClientState::IDLE) {
364  this->play_next_(x...);
365  } else {
366  this->var_ = std::make_tuple(x...);
367  this->ble_client_->disconnect();
368  }
369  }
370 
371  private:
372  BLEClient *ble_client_;
373  std::tuple<Ts...> var_{};
374 };
375 } // namespace ble_client
376 } // namespace esphome
377 
378 #endif
void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) override
Definition: automation.h:75
void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param) override
Definition: automation.h:41
std::string format_hex_pretty(const uint8_t *data, size_t length)
Format the byte array data of length len in pretty-printed, human-readable hex.
Definition: helpers.cpp:361
BLEClientRemoveBondAction(BLEClient *ble_client)
Definition: automation.h:277
void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param) override
Definition: automation.h:344
uint16_t x
Definition: tt21100.cpp:17
BLEClientDisconnectAction(BLEClient *ble_client)
Definition: automation.h:340
bool write(const std::vector< uint8_t > &value)
Note about logging: the esph_log_X macros are used here because the CI checks complain about use of t...
Definition: automation.h:139
void set_value_simple(const std::vector< uint8_t > &value)
Definition: automation.h:114
void set_value_simple(const uint32_t &value)
Definition: automation.h:232
void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) override
Definition: automation.h:86
BLEClientConnectAction(BLEClient *ble_client)
Definition: automation.h:291
void set_value_template(std::function< std::vector< uint8_t >(Ts...)> func)
Definition: automation.h:109
void set_value_template(std::function< uint32_t(Ts...)> func)
Definition: automation.h:227
static ESPBTUUID from_uint32(uint32_t uuid)
Definition: ble_uuid.cpp:22
void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) override
Definition: automation.h:65
static ESPBTUUID from_uint16(uint16_t uuid)
Definition: ble_uuid.cpp:16
static const char *const TAG
Definition: automation.h:19
void register_ble_node(BLEClientNode *node)
Definition: ble_client.h:62
void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param) override
Definition: automation.h:295
This is a workaround until we can figure out a way to get the tflite-micro idf component code availab...
Definition: a01nyub.cpp:7
static ESPBTUUID from_raw(const uint8_t *data)
Definition: ble_uuid.cpp:28
void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param) override
Definition: automation.h:155
void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param) override
Definition: automation.h:27
BLEClientWriteAction(BLEClient *ble_client)
Definition: automation.h:96
BLEClientPasskeyReplyAction(BLEClient *ble_client)
Definition: automation.h:211
void set_value_template(std::function< bool(Ts...)> func)
Definition: automation.h:258