mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-31 15:12:06 +00:00 
			
		
		
		
	Add support for BLE passkey authentication (#4258)
Co-authored-by: Branden Cash <203336+ammmze@users.noreply.github.com>
This commit is contained in:
		| @@ -29,8 +29,35 @@ BLEClientConnectTrigger = ble_client_ns.class_( | |||||||
| BLEClientDisconnectTrigger = ble_client_ns.class_( | BLEClientDisconnectTrigger = ble_client_ns.class_( | ||||||
|     "BLEClientDisconnectTrigger", automation.Trigger.template(BLEClientNodeConstRef) |     "BLEClientDisconnectTrigger", automation.Trigger.template(BLEClientNodeConstRef) | ||||||
| ) | ) | ||||||
|  | BLEClientPasskeyRequestTrigger = ble_client_ns.class_( | ||||||
|  |     "BLEClientPasskeyRequestTrigger", automation.Trigger.template(BLEClientNodeConstRef) | ||||||
|  | ) | ||||||
|  | BLEClientPasskeyNotificationTrigger = ble_client_ns.class_( | ||||||
|  |     "BLEClientPasskeyNotificationTrigger", | ||||||
|  |     automation.Trigger.template(BLEClientNodeConstRef, cg.uint32), | ||||||
|  | ) | ||||||
|  | BLEClientNumericComparisonRequestTrigger = ble_client_ns.class_( | ||||||
|  |     "BLEClientNumericComparisonRequestTrigger", | ||||||
|  |     automation.Trigger.template(BLEClientNodeConstRef, cg.uint32), | ||||||
|  | ) | ||||||
|  |  | ||||||
| # Actions | # Actions | ||||||
| BLEWriteAction = ble_client_ns.class_("BLEClientWriteAction", automation.Action) | BLEWriteAction = ble_client_ns.class_("BLEClientWriteAction", automation.Action) | ||||||
|  | BLEPasskeyReplyAction = ble_client_ns.class_( | ||||||
|  |     "BLEClientPasskeyReplyAction", automation.Action | ||||||
|  | ) | ||||||
|  | BLENumericComparisonReplyAction = ble_client_ns.class_( | ||||||
|  |     "BLEClientNumericComparisonReplyAction", automation.Action | ||||||
|  | ) | ||||||
|  | BLERemoveBondAction = ble_client_ns.class_( | ||||||
|  |     "BLEClientRemoveBondAction", automation.Action | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | CONF_PASSKEY = "passkey" | ||||||
|  | CONF_ACCEPT = "accept" | ||||||
|  | CONF_ON_PASSKEY_REQUEST = "on_passkey_request" | ||||||
|  | CONF_ON_PASSKEY_NOTIFICATION = "on_passkey_notification" | ||||||
|  | CONF_ON_NUMERIC_COMPARISON_REQUEST = "on_numeric_comparison_request" | ||||||
|  |  | ||||||
| # Espressif platformio framework is built with MAX_BLE_CONN to 3, so | # Espressif platformio framework is built with MAX_BLE_CONN to 3, so | ||||||
| # enforce this in yaml checks. | # enforce this in yaml checks. | ||||||
| @@ -56,6 +83,29 @@ CONFIG_SCHEMA = ( | |||||||
|                     ), |                     ), | ||||||
|                 } |                 } | ||||||
|             ), |             ), | ||||||
|  |             cv.Optional(CONF_ON_PASSKEY_REQUEST): automation.validate_automation( | ||||||
|  |                 { | ||||||
|  |                     cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id( | ||||||
|  |                         BLEClientPasskeyRequestTrigger | ||||||
|  |                     ), | ||||||
|  |                 } | ||||||
|  |             ), | ||||||
|  |             cv.Optional(CONF_ON_PASSKEY_NOTIFICATION): automation.validate_automation( | ||||||
|  |                 { | ||||||
|  |                     cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id( | ||||||
|  |                         BLEClientPasskeyNotificationTrigger | ||||||
|  |                     ), | ||||||
|  |                 } | ||||||
|  |             ), | ||||||
|  |             cv.Optional( | ||||||
|  |                 CONF_ON_NUMERIC_COMPARISON_REQUEST | ||||||
|  |             ): automation.validate_automation( | ||||||
|  |                 { | ||||||
|  |                     cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id( | ||||||
|  |                         BLEClientNumericComparisonRequestTrigger | ||||||
|  |                     ), | ||||||
|  |                 } | ||||||
|  |             ), | ||||||
|         } |         } | ||||||
|     ) |     ) | ||||||
|     .extend(cv.COMPONENT_SCHEMA) |     .extend(cv.COMPONENT_SCHEMA) | ||||||
| @@ -85,13 +135,34 @@ BLE_WRITE_ACTION_SCHEMA = cv.Schema( | |||||||
|     } |     } | ||||||
| ) | ) | ||||||
|  |  | ||||||
|  | BLE_NUMERIC_COMPARISON_REPLY_ACTION_SCHEMA = cv.Schema( | ||||||
|  |     { | ||||||
|  |         cv.GenerateID(CONF_ID): cv.use_id(BLEClient), | ||||||
|  |         cv.Required(CONF_ACCEPT): cv.templatable(cv.boolean), | ||||||
|  |     } | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | BLE_PASSKEY_REPLY_ACTION_SCHEMA = cv.Schema( | ||||||
|  |     { | ||||||
|  |         cv.GenerateID(CONF_ID): cv.use_id(BLEClient), | ||||||
|  |         cv.Required(CONF_PASSKEY): cv.templatable(cv.int_range(min=0, max=999999)), | ||||||
|  |     } | ||||||
|  | ) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | BLE_REMOVE_BOND_ACTION_SCHEMA = cv.Schema( | ||||||
|  |     { | ||||||
|  |         cv.GenerateID(CONF_ID): cv.use_id(BLEClient), | ||||||
|  |     } | ||||||
|  | ) | ||||||
|  |  | ||||||
|  |  | ||||||
| @automation.register_action( | @automation.register_action( | ||||||
|     "ble_client.ble_write", BLEWriteAction, BLE_WRITE_ACTION_SCHEMA |     "ble_client.ble_write", BLEWriteAction, BLE_WRITE_ACTION_SCHEMA | ||||||
| ) | ) | ||||||
| async def ble_write_to_code(config, action_id, template_arg, args): | async def ble_write_to_code(config, action_id, template_arg, args): | ||||||
|     paren = await cg.get_variable(config[CONF_ID]) |     parent = await cg.get_variable(config[CONF_ID]) | ||||||
|     var = cg.new_Pvariable(action_id, template_arg, paren) |     var = cg.new_Pvariable(action_id, template_arg, parent) | ||||||
|  |  | ||||||
|     value = config[CONF_VALUE] |     value = config[CONF_VALUE] | ||||||
|     if cg.is_template(value): |     if cg.is_template(value): | ||||||
| @@ -137,6 +208,54 @@ async def ble_write_to_code(config, action_id, template_arg, args): | |||||||
|     return var |     return var | ||||||
|  |  | ||||||
|  |  | ||||||
|  | @automation.register_action( | ||||||
|  |     "ble_client.numeric_comparison_reply", | ||||||
|  |     BLENumericComparisonReplyAction, | ||||||
|  |     BLE_NUMERIC_COMPARISON_REPLY_ACTION_SCHEMA, | ||||||
|  | ) | ||||||
|  | async def numeric_comparison_reply_to_code(config, action_id, template_arg, args): | ||||||
|  |     parent = await cg.get_variable(config[CONF_ID]) | ||||||
|  |     var = cg.new_Pvariable(action_id, template_arg, parent) | ||||||
|  |  | ||||||
|  |     accept = config[CONF_ACCEPT] | ||||||
|  |     if cg.is_template(accept): | ||||||
|  |         templ = await cg.templatable(accept, args, cg.bool_) | ||||||
|  |         cg.add(var.set_value_template(templ)) | ||||||
|  |     else: | ||||||
|  |         cg.add(var.set_value_simple(accept)) | ||||||
|  |  | ||||||
|  |     return var | ||||||
|  |  | ||||||
|  |  | ||||||
|  | @automation.register_action( | ||||||
|  |     "ble_client.passkey_reply", BLEPasskeyReplyAction, BLE_PASSKEY_REPLY_ACTION_SCHEMA | ||||||
|  | ) | ||||||
|  | async def passkey_reply_to_code(config, action_id, template_arg, args): | ||||||
|  |     parent = await cg.get_variable(config[CONF_ID]) | ||||||
|  |     var = cg.new_Pvariable(action_id, template_arg, parent) | ||||||
|  |  | ||||||
|  |     passkey = config[CONF_PASSKEY] | ||||||
|  |     if cg.is_template(passkey): | ||||||
|  |         templ = await cg.templatable(passkey, args, cg.uint32) | ||||||
|  |         cg.add(var.set_value_template(templ)) | ||||||
|  |     else: | ||||||
|  |         cg.add(var.set_value_simple(passkey)) | ||||||
|  |  | ||||||
|  |     return var | ||||||
|  |  | ||||||
|  |  | ||||||
|  | @automation.register_action( | ||||||
|  |     "ble_client.remove_bond", | ||||||
|  |     BLERemoveBondAction, | ||||||
|  |     BLE_REMOVE_BOND_ACTION_SCHEMA, | ||||||
|  | ) | ||||||
|  | async def remove_bond_to_code(config, action_id, template_arg, args): | ||||||
|  |     parent = await cg.get_variable(config[CONF_ID]) | ||||||
|  |     var = cg.new_Pvariable(action_id, template_arg, parent) | ||||||
|  |  | ||||||
|  |     return var | ||||||
|  |  | ||||||
|  |  | ||||||
| async def to_code(config): | async def to_code(config): | ||||||
|     var = cg.new_Pvariable(config[CONF_ID]) |     var = cg.new_Pvariable(config[CONF_ID]) | ||||||
|     await cg.register_component(var, config) |     await cg.register_component(var, config) | ||||||
| @@ -148,3 +267,12 @@ async def to_code(config): | |||||||
|     for conf in config.get(CONF_ON_DISCONNECT, []): |     for conf in config.get(CONF_ON_DISCONNECT, []): | ||||||
|         trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) |         trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) | ||||||
|         await automation.build_automation(trigger, [], conf) |         await automation.build_automation(trigger, [], conf) | ||||||
|  |     for conf in config.get(CONF_ON_PASSKEY_REQUEST, []): | ||||||
|  |         trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) | ||||||
|  |         await automation.build_automation(trigger, [], conf) | ||||||
|  |     for conf in config.get(CONF_ON_PASSKEY_NOTIFICATION, []): | ||||||
|  |         trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) | ||||||
|  |         await automation.build_automation(trigger, [(cg.uint32, "passkey")], conf) | ||||||
|  |     for conf in config.get(CONF_ON_NUMERIC_COMPARISON_REQUEST, []): | ||||||
|  |         trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) | ||||||
|  |         await automation.build_automation(trigger, [(cg.uint32, "passkey")], conf) | ||||||
|   | |||||||
| @@ -37,6 +37,44 @@ class BLEClientDisconnectTrigger : public Trigger<>, public BLEClientNode { | |||||||
|   } |   } | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | class BLEClientPasskeyRequestTrigger : public Trigger<>, public BLEClientNode { | ||||||
|  |  public: | ||||||
|  |   explicit BLEClientPasskeyRequestTrigger(BLEClient *parent) { parent->register_ble_node(this); } | ||||||
|  |   void loop() override {} | ||||||
|  |   void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) override { | ||||||
|  |     if (event == ESP_GAP_BLE_PASSKEY_REQ_EVT && | ||||||
|  |         memcmp(param->ble_security.auth_cmpl.bd_addr, this->parent_->get_remote_bda(), 6) == 0) { | ||||||
|  |       this->trigger(); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | class BLEClientPasskeyNotificationTrigger : public Trigger<uint32_t>, public BLEClientNode { | ||||||
|  |  public: | ||||||
|  |   explicit BLEClientPasskeyNotificationTrigger(BLEClient *parent) { parent->register_ble_node(this); } | ||||||
|  |   void loop() override {} | ||||||
|  |   void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) override { | ||||||
|  |     if (event == ESP_GAP_BLE_PASSKEY_NOTIF_EVT && | ||||||
|  |         memcmp(param->ble_security.auth_cmpl.bd_addr, this->parent_->get_remote_bda(), 6) == 0) { | ||||||
|  |       uint32_t passkey = param->ble_security.key_notif.passkey; | ||||||
|  |       this->trigger(passkey); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | class BLEClientNumericComparisonRequestTrigger : public Trigger<uint32_t>, public BLEClientNode { | ||||||
|  |  public: | ||||||
|  |   explicit BLEClientNumericComparisonRequestTrigger(BLEClient *parent) { parent->register_ble_node(this); } | ||||||
|  |   void loop() override {} | ||||||
|  |   void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) override { | ||||||
|  |     if (event == ESP_GAP_BLE_NC_REQ_EVT && | ||||||
|  |         memcmp(param->ble_security.auth_cmpl.bd_addr, this->parent_->get_remote_bda(), 6) == 0) { | ||||||
|  |       uint32_t passkey = param->ble_security.key_notif.passkey; | ||||||
|  |       this->trigger(passkey); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | }; | ||||||
|  |  | ||||||
| class BLEWriterClientNode : public BLEClientNode { | class BLEWriterClientNode : public BLEClientNode { | ||||||
|  public: |  public: | ||||||
|   BLEWriterClientNode(BLEClient *ble_client) { |   BLEWriterClientNode(BLEClient *ble_client) { | ||||||
| @@ -94,6 +132,86 @@ template<typename... Ts> class BLEClientWriteAction : public Action<Ts...>, publ | |||||||
|   std::function<std::vector<uint8_t>(Ts...)> value_template_{}; |   std::function<std::vector<uint8_t>(Ts...)> value_template_{}; | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | template<typename... Ts> class BLEClientPasskeyReplyAction : public Action<Ts...> { | ||||||
|  |  public: | ||||||
|  |   BLEClientPasskeyReplyAction(BLEClient *ble_client) { parent_ = ble_client; } | ||||||
|  |  | ||||||
|  |   void play(Ts... x) override { | ||||||
|  |     uint32_t passkey; | ||||||
|  |     if (has_simple_value_) { | ||||||
|  |       passkey = this->value_simple_; | ||||||
|  |     } else { | ||||||
|  |       passkey = this->value_template_(x...); | ||||||
|  |     } | ||||||
|  |     if (passkey > 999999) | ||||||
|  |       return; | ||||||
|  |     esp_bd_addr_t remote_bda; | ||||||
|  |     memcpy(remote_bda, parent_->get_remote_bda(), sizeof(esp_bd_addr_t)); | ||||||
|  |     esp_ble_passkey_reply(remote_bda, true, passkey); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   void set_value_template(std::function<uint32_t(Ts...)> func) { | ||||||
|  |     this->value_template_ = std::move(func); | ||||||
|  |     has_simple_value_ = false; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   void set_value_simple(const uint32_t &value) { | ||||||
|  |     this->value_simple_ = value; | ||||||
|  |     has_simple_value_ = true; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |  private: | ||||||
|  |   BLEClient *parent_{nullptr}; | ||||||
|  |   bool has_simple_value_ = true; | ||||||
|  |   uint32_t value_simple_{0}; | ||||||
|  |   std::function<uint32_t(Ts...)> value_template_{}; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | template<typename... Ts> class BLEClientNumericComparisonReplyAction : public Action<Ts...> { | ||||||
|  |  public: | ||||||
|  |   BLEClientNumericComparisonReplyAction(BLEClient *ble_client) { parent_ = ble_client; } | ||||||
|  |  | ||||||
|  |   void play(Ts... x) override { | ||||||
|  |     esp_bd_addr_t remote_bda; | ||||||
|  |     memcpy(remote_bda, parent_->get_remote_bda(), sizeof(esp_bd_addr_t)); | ||||||
|  |     if (has_simple_value_) { | ||||||
|  |       esp_ble_confirm_reply(remote_bda, this->value_simple_); | ||||||
|  |     } else { | ||||||
|  |       esp_ble_confirm_reply(remote_bda, this->value_template_(x...)); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   void set_value_template(std::function<bool(Ts...)> func) { | ||||||
|  |     this->value_template_ = std::move(func); | ||||||
|  |     has_simple_value_ = false; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   void set_value_simple(const bool &value) { | ||||||
|  |     this->value_simple_ = value; | ||||||
|  |     has_simple_value_ = true; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |  private: | ||||||
|  |   BLEClient *parent_{nullptr}; | ||||||
|  |   bool has_simple_value_ = true; | ||||||
|  |   bool value_simple_{false}; | ||||||
|  |   std::function<bool(Ts...)> value_template_{}; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | template<typename... Ts> class BLEClientRemoveBondAction : public Action<Ts...> { | ||||||
|  |  public: | ||||||
|  |   BLEClientRemoveBondAction(BLEClient *ble_client) { parent_ = ble_client; } | ||||||
|  |  | ||||||
|  |   void play(Ts... x) override { | ||||||
|  |     esp_bd_addr_t remote_bda; | ||||||
|  |     memcpy(remote_bda, parent_->get_remote_bda(), sizeof(esp_bd_addr_t)); | ||||||
|  |     esp_ble_remove_bond_device(remote_bda); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |  private: | ||||||
|  |   BLEClient *parent_{nullptr}; | ||||||
|  | }; | ||||||
|  |  | ||||||
| }  // namespace ble_client | }  // namespace ble_client | ||||||
| }  // namespace esphome | }  // namespace esphome | ||||||
|  |  | ||||||
|   | |||||||
| @@ -27,7 +27,7 @@ class BLEClient; | |||||||
| class BLEClientNode { | class BLEClientNode { | ||||||
|  public: |  public: | ||||||
|   virtual void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, |   virtual void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, | ||||||
|                                    esp_ble_gattc_cb_param_t *param) = 0; |                                    esp_ble_gattc_cb_param_t *param){}; | ||||||
|   virtual void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) {} |   virtual void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) {} | ||||||
|   virtual void loop() {} |   virtual void loop() {} | ||||||
|   void set_address(uint64_t address) { address_ = address; } |   void set_address(uint64_t address) { address_ = address; } | ||||||
|   | |||||||
| @@ -9,6 +9,7 @@ CODEOWNERS = ["@jesserockz"] | |||||||
| CONFLICTS_WITH = ["esp32_ble_beacon"] | CONFLICTS_WITH = ["esp32_ble_beacon"] | ||||||
|  |  | ||||||
| CONF_BLE_ID = "ble_id" | CONF_BLE_ID = "ble_id" | ||||||
|  | CONF_IO_CAPABILITY = "io_capability" | ||||||
|  |  | ||||||
| NO_BLUETOOTH_VARIANTS = [const.VARIANT_ESP32S2] | NO_BLUETOOTH_VARIANTS = [const.VARIANT_ESP32S2] | ||||||
|  |  | ||||||
| @@ -19,10 +20,21 @@ GAPEventHandler = esp32_ble_ns.class_("GAPEventHandler") | |||||||
| GATTcEventHandler = esp32_ble_ns.class_("GATTcEventHandler") | GATTcEventHandler = esp32_ble_ns.class_("GATTcEventHandler") | ||||||
| GATTsEventHandler = esp32_ble_ns.class_("GATTsEventHandler") | GATTsEventHandler = esp32_ble_ns.class_("GATTsEventHandler") | ||||||
|  |  | ||||||
|  | IoCapability = esp32_ble_ns.enum("IoCapability") | ||||||
|  | IO_CAPABILITY = { | ||||||
|  |     "none": IoCapability.IO_CAP_NONE, | ||||||
|  |     "keyboard_only": IoCapability.IO_CAP_IN, | ||||||
|  |     "keyboard_display": IoCapability.IO_CAP_KBDISP, | ||||||
|  |     "display_only": IoCapability.IO_CAP_OUT, | ||||||
|  |     "display_yes_no": IoCapability.IO_CAP_IO, | ||||||
|  | } | ||||||
|  |  | ||||||
| CONFIG_SCHEMA = cv.Schema( | CONFIG_SCHEMA = cv.Schema( | ||||||
|     { |     { | ||||||
|         cv.GenerateID(): cv.declare_id(ESP32BLE), |         cv.GenerateID(): cv.declare_id(ESP32BLE), | ||||||
|  |         cv.Optional(CONF_IO_CAPABILITY, default="none"): cv.enum( | ||||||
|  |             IO_CAPABILITY, lower=True | ||||||
|  |         ), | ||||||
|     } |     } | ||||||
| ).extend(cv.COMPONENT_SCHEMA) | ).extend(cv.COMPONENT_SCHEMA) | ||||||
|  |  | ||||||
| @@ -39,6 +51,7 @@ FINAL_VALIDATE_SCHEMA = validate_variant | |||||||
| async def to_code(config): | async def to_code(config): | ||||||
|     var = cg.new_Pvariable(config[CONF_ID]) |     var = cg.new_Pvariable(config[CONF_ID]) | ||||||
|     await cg.register_component(var, config) |     await cg.register_component(var, config) | ||||||
|  |     cg.add(var.set_io_capability(config[CONF_IO_CAPABILITY])) | ||||||
|  |  | ||||||
|     if CORE.using_esp_idf: |     if CORE.using_esp_idf: | ||||||
|         add_idf_sdkconfig_option("CONFIG_BT_ENABLED", True) |         add_idf_sdkconfig_option("CONFIG_BT_ENABLED", True) | ||||||
|   | |||||||
| @@ -134,8 +134,7 @@ bool ESP32BLE::ble_setup_() { | |||||||
|     return false; |     return false; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   esp_ble_io_cap_t iocap = ESP_IO_CAP_NONE; |   err = esp_ble_gap_set_security_param(ESP_BLE_SM_IOCAP_MODE, &(this->io_cap_), sizeof(uint8_t)); | ||||||
|   err = esp_ble_gap_set_security_param(ESP_BLE_SM_IOCAP_MODE, &iocap, sizeof(uint8_t)); |  | ||||||
|   if (err != ESP_OK) { |   if (err != ESP_OK) { | ||||||
|     ESP_LOGE(TAG, "esp_ble_gap_set_security_param failed: %d", err); |     ESP_LOGE(TAG, "esp_ble_gap_set_security_param failed: %d", err); | ||||||
|     return false; |     return false; | ||||||
| @@ -215,9 +214,31 @@ float ESP32BLE::get_setup_priority() const { return setup_priority::BLUETOOTH; } | |||||||
| void ESP32BLE::dump_config() { | void ESP32BLE::dump_config() { | ||||||
|   const uint8_t *mac_address = esp_bt_dev_get_address(); |   const uint8_t *mac_address = esp_bt_dev_get_address(); | ||||||
|   if (mac_address) { |   if (mac_address) { | ||||||
|  |     const char *io_capability_s; | ||||||
|  |     switch (this->io_cap_) { | ||||||
|  |       case ESP_IO_CAP_OUT: | ||||||
|  |         io_capability_s = "display_only"; | ||||||
|  |         break; | ||||||
|  |       case ESP_IO_CAP_IO: | ||||||
|  |         io_capability_s = "display_yes_no"; | ||||||
|  |         break; | ||||||
|  |       case ESP_IO_CAP_IN: | ||||||
|  |         io_capability_s = "keyboard_only"; | ||||||
|  |         break; | ||||||
|  |       case ESP_IO_CAP_NONE: | ||||||
|  |         io_capability_s = "none"; | ||||||
|  |         break; | ||||||
|  |       case ESP_IO_CAP_KBDISP: | ||||||
|  |         io_capability_s = "keyboard_display"; | ||||||
|  |         break; | ||||||
|  |       default: | ||||||
|  |         io_capability_s = "invalid"; | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|     ESP_LOGCONFIG(TAG, "ESP32 BLE:"); |     ESP_LOGCONFIG(TAG, "ESP32 BLE:"); | ||||||
|     ESP_LOGCONFIG(TAG, "  MAC address: %02X:%02X:%02X:%02X:%02X:%02X", mac_address[0], mac_address[1], mac_address[2], |     ESP_LOGCONFIG(TAG, "  MAC address: %02X:%02X:%02X:%02X:%02X:%02X", mac_address[0], mac_address[1], mac_address[2], | ||||||
|                   mac_address[3], mac_address[4], mac_address[5]); |                   mac_address[3], mac_address[4], mac_address[5]); | ||||||
|  |     ESP_LOGCONFIG(TAG, "  IO Capability: %s", io_capability_s); | ||||||
|   } else { |   } else { | ||||||
|     ESP_LOGCONFIG(TAG, "ESP32 BLE: bluetooth stack is not enabled"); |     ESP_LOGCONFIG(TAG, "ESP32 BLE: bluetooth stack is not enabled"); | ||||||
|   } |   } | ||||||
|   | |||||||
| @@ -25,6 +25,14 @@ typedef struct { | |||||||
|   uint16_t mtu; |   uint16_t mtu; | ||||||
| } conn_status_t; | } conn_status_t; | ||||||
|  |  | ||||||
|  | enum IoCapability { | ||||||
|  |   IO_CAP_OUT = ESP_IO_CAP_OUT, | ||||||
|  |   IO_CAP_IO = ESP_IO_CAP_IO, | ||||||
|  |   IO_CAP_IN = ESP_IO_CAP_IN, | ||||||
|  |   IO_CAP_NONE = ESP_IO_CAP_NONE, | ||||||
|  |   IO_CAP_KBDISP = ESP_IO_CAP_KBDISP, | ||||||
|  | }; | ||||||
|  |  | ||||||
| class GAPEventHandler { | class GAPEventHandler { | ||||||
|  public: |  public: | ||||||
|   virtual void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) = 0; |   virtual void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) = 0; | ||||||
| @@ -44,6 +52,8 @@ class GATTsEventHandler { | |||||||
|  |  | ||||||
| class ESP32BLE : public Component { | class ESP32BLE : public Component { | ||||||
|  public: |  public: | ||||||
|  |   void set_io_capability(IoCapability io_capability) { this->io_cap_ = (esp_ble_io_cap_t) io_capability; } | ||||||
|  |  | ||||||
|   void setup() override; |   void setup() override; | ||||||
|   void loop() override; |   void loop() override; | ||||||
|   void dump_config() override; |   void dump_config() override; | ||||||
| @@ -72,6 +82,7 @@ class ESP32BLE : public Component { | |||||||
|  |  | ||||||
|   Queue<BLEEvent> ble_events_; |   Queue<BLEEvent> ble_events_; | ||||||
|   BLEAdvertising *advertising_; |   BLEAdvertising *advertising_; | ||||||
|  |   esp_ble_io_cap_t io_cap_{ESP_IO_CAP_NONE}; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) | // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) | ||||||
|   | |||||||
| @@ -294,6 +294,9 @@ wled: | |||||||
|  |  | ||||||
| adalight: | adalight: | ||||||
|  |  | ||||||
|  | esp32_ble: | ||||||
|  |   io_capability: keyboard_only | ||||||
|  |  | ||||||
| esp32_ble_tracker: | esp32_ble_tracker: | ||||||
|  |  | ||||||
| ble_client: | ble_client: | ||||||
| @@ -307,6 +310,19 @@ ble_client: | |||||||
|     on_disconnect: |     on_disconnect: | ||||||
|       then: |       then: | ||||||
|         - switch.turn_on: ble1_status |         - switch.turn_on: ble1_status | ||||||
|  |     on_passkey_request: | ||||||
|  |       then: | ||||||
|  |         - ble_client.passkey_reply: | ||||||
|  |             id: ble_blah | ||||||
|  |             passkey: 123456 | ||||||
|  |     on_passkey_notification: | ||||||
|  |       then: | ||||||
|  |         - logger.log: "Passkey notification received" | ||||||
|  |     on_numeric_comparison_request: | ||||||
|  |       then: | ||||||
|  |         - ble_client.numeric_comparison_reply: | ||||||
|  |             id: ble_blah | ||||||
|  |             accept: True | ||||||
|   - mac_address: C4:4F:33:11:22:33 |   - mac_address: C4:4F:33:11:22:33 | ||||||
|     id: my_bedjet_ble_client |     id: my_bedjet_ble_client | ||||||
| bedjet: | bedjet: | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user