跳转到内容

BLE 客户端

ble_client 组件用于连接蓝牙低功耗设备,以便查询和控制它们。该组件本身不暴露任何传感器或输出组件,只是管理连接以供其他组件使用。

WARNING

ESP32 上的 BLE 软件栈会消耗设备上大量的 RAM。

如果您在设备配置中包含过多的额外组件,很可能发生崩溃。占用内存较大的组件,如语音助手和其他音频组件,最容易导致问题。

NOTE

由于 ESP32 BLE 栈的限制,最多支持三个设备。如果您希望连接更多设备,请使用额外的 ESP32 开发板。

该组件支持需要 6 位 PIN 码进行认证的设备。

目前,通过客户端连接的设备无法被基于ESP32 BLE 跟踪器的其他组件支持,因为它们监听的是广播消息,而只有没有活动连接的设备才会发送广播。

尽管有上面最后一点说明,ble_client 组件仍需要 esp32_ble_tracker 组件来发现可用的客户端设备。

esp32_ble_tracker:
ble_client:
- mac_address: XX:XX:XX:XX:XX:XX
id: itag_black
auto_connect: true
  • mac_address (必需, MAC 地址): 要连接的 BLE 设备的 MAC 地址。
  • auto_connect (可选, 布尔值): 如果为 true,当ESP32 BLE 跟踪器发现设备时将自动连接。默认为 true。
  • id (必需, ID): 用于代码生成和被依赖组件引用的 ID。

自动化:

当客户端连接到 BLE 设备时触发此自动化。

ble_client:
- mac_address: XX:XX:XX:XX:XX:XX
id: ble_itag
on_connect:
then:
- lambda: |-
ESP_LOGD("ble_client_lambda", "Connected to BLE device");

当客户端与 BLE 设备断开连接时触发此自动化。

ble_client:
- mac_address: XX:XX:XX:XX:XX:XX
id: ble_itag
on_disconnect:
then:
- lambda: |-
ESP_LOGD("ble_client_lambda", "Disconnected from BLE device");

当 BLE 设备请求密钥进行认证时触发此自动化。

ble_client:
- mac_address: XX:XX:XX:XX:XX:XX
id: ble_itag
on_passkey_request:
then:
- ble_client.passkey_reply:
id: ble_itag
passkey: 123456

当从 BLE 设备收到密钥时触发此自动化。

ble_client:
- mac_address: XX:XX:XX:XX:XX:XX
id: ble_itag
on_passkey_notification:
then:
- logger.log:
format: "Enter this passkey on your BLE device: %06d"
args: [ passkey ]

当 BLE 设备请求数值比较时触发此自动化。

ble_client:
- mac_address: XX:XX:XX:XX:XX:XX
id: ble_itag
on_numeric_comparison_request:
then:
- logger.log:
format: "Compare this passkey with the one on your BLE device: %06d"
args: [ passkey ]
- ble_client.numeric_comparison_reply:
id: ble_itag
accept: True

此动作仅适用于设置了 auto_connect: false 的设备,允许在自动化内部发起连接。一旦连接成功,其他动作如 ble_write 就可以使用。这对于只需要偶尔与 BLE 服务器交互、因此不需要保持持续连接的情况很有用。

以下示例每小时更新一次小米 MHO-C303 时钟的时间。请注意,在连接尝试期间必须停止 BLE 跟踪器,之后重新启动。如果跟踪器设置了 continuous: false,则不需要这样做。在此示例场景中,还有另一个 BLE 设备确实需要扫描器处于开启状态,因此在连接期间需要停止和启动扫描。

ble_client:
- id: ble_clock
mac_address: XX:XX:XX:XX:XX:XX
auto_connect: false
- id: other_device
mac_address: XX:XX:XX:XX:XX:XX
interval:
- interval: 60min
then:
- esp32_ble_tracker.stop_scan:
- ble_client.connect: ble_clock
- ble_client.ble_write:
id: ble_clock
service_uuid: EBE0CCB0-7A0A-4B0C-8A1A-6FF2997DA3A6
characteristic_uuid: EBE0CCB7-7A0A-4B0C-8A1A-6FF2997DA3A6
value: !lambda |-
uint32_t t = id(sntp_time).now().timestamp + ESPTime::timezone_offset();
return {(uint8_t)t, (uint8_t)(t >> 8), (uint8_t)(t >> 16), (uint8_t)(t >> 24), 0};
- ble_client.disconnect: ble_clock
- esp32_ble_tracker.start_scan:

connect 动作之后的任何动作只有在连接成功后才会继续执行。如果连接失败,自动化块中的后续动作将不会被执行。如果已停止扫描,应考虑这一点 - 可能需要另一种机制来重新启动它。

此动作断开通过 ble_client.connect 动作连接的设备。 自动化块序列的执行将在断开连接完成后继续。

此动作触发向指定 BLE 特征值的写入操作。写入以尽力而为的方式尝试,只有在 ble_client 的连接已建立且外设暴露了预期的 BLE 服务和特征值时才会成功。自动化块序列的执行将在写入完成后继续。写入失败不会停止后续动作的执行(例如,这允许执行断开连接操作)。

示例用法:

ble_client:
- mac_address: XX:XX:XX:XX:XX:XX
id: my_ble_client
switch:
- platform: template
name: "My Switch"
turn_on_action:
- ble_client.ble_write:
id: my_ble_client
service_uuid: F61E3BE9-2826-A81B-970A-4D4DECFABBAE
characteristic_uuid: 6490FAFE-0734-732C-8705-91B653A081FC
# 要写入的字节列表。
value: [0x01, 0xab, 0xff]
- ble_client.ble_write:
id: my_ble_client
service_uuid: F61E3BE9-2826-A81B-970A-4D4DECFABBAE
characteristic_uuid: 6490FAFE-0734-732C-8705-91B653A081FC
# 返回 std::vector<uint8_t> 的 lambda。
value: !lambda |-
return {0x13, 0x37};
  • id (必需, ID): 关联的 BLE 客户端的 ID。
  • service_uuid (必需, UUID): 要写入的服务的 UUID。
  • characteristic_uuid (必需, UUID): 要写入的服务特征值的 UUID。
  • value (必需, 字节数组或 lambda): 要写入的值。

此动作使用指定的 passkey 触发认证尝试。

示例用法:

on_...:
then:
- ble_client.passkey_reply:
id: my_ble_client
passkey: 123456
  • id (必需, ID): 关联的 BLE 客户端的 ID。
  • passkey (必需, 整数): 6 位密钥。

ble_client.numeric_comparison_reply 动作

Section titled “ble_client.numeric_comparison_reply 动作”

此动作在数值比较后触发认证尝试。

示例用法:

on_...:
then:
- ble_client.numeric_comparison_reply:
id: my_ble_client
accept: True
  • id (必需, ID): 关联的 BLE 客户端的 ID。
  • accept (必需, 布尔值): 如果两个 BLE 设备上显示的密钥匹配,则应为 true

此动作从安全数据库中移除设备并管理取消配对。

示例用法:

ble_client:
- mac_address: XX:XX:XX:XX:XX:XX
id: my_ble_client
on_connect:
then:
- ble_client.remove_bond:
id: my_ble_client
  • id (必需, ID): 关联的 BLE 客户端的 ID。

本节简要介绍蓝牙 LE 架构,以帮助理解此组件和相关组件。网上有许多更详细的参考资料。

BLE 使用服务器客户端的概念。简单来说,服务器在提供服务的设备上实现,通常是心率监测器、标签、气象站等设备。客户端连接到服务器并使用其服务。客户端通常是手机上的应用程序,或者在 ESPHome 的情况下,是 ESP32 设备。

当客户端连接到服务器时,客户端会查询服务器提供的服务。服务暴露服务器上的功能类别。这些可能是定义明确且受支持的服务,如电池电量服务、设备信息或心率。或者它们可能是专门为该设备设计的自定义服务。例如,廉价 iTag 上的按钮使用自定义服务。

每个服务然后定义一个或多个特征值,通常是该服务的离散值。例如,环境传感器服务暴露的特征值包括风速、湿度和降雨量。根据其功能,每个特征值可能是只读或读写的。

特征值还可能暴露一个或多个描述符,携带有关特征值的更多信息。这可能是单位、有效范围以及是否启用了通知(见下文)等内容。

BLE 还支持通知。客户端持续轮询更新可能会消耗大量电力,这对于设计为低能耗的协议来说是不可取的。相反,服务器可以仅在值更改时将更新推送给客户端。根据其目的和设计,特征值可能允许发送通知。然后,客户端可以通过设置特征值的配置描述符来启用通知。

每个服务、特征值和描述符都由一个唯一标识符(UUID)标识,长度可能在 16 到 128 位之间。客户端通常会根据 UUID 识别设备的功能。

一旦建立连接,通过完整的 UUID 引用每个服务/特征值/描述符将占用小型(约 23 字节)数据包的相当大一部分。因此,特征值和描述符还提供了一个小型 2 字节句柄(别名),以最大化可用数据空间。

虽然该组件可以连接大多数 BLE 设备,但有用的功能只能通过依赖组件获得,例如BLE 客户端传感器。 有关设置特定设备的详细信息,请参阅这些组件的文档。

要使用 ble_client 组件,您需要启用ESP32 BLE 跟踪器组件。这也将允许您发现设备的 MAC 地址。

当您发现设备的 MAC 地址后,可以将其添加到 ble_client 配置节中。

如果您随后构建并上传此配置,ESP 将监听该设备,并在发现时尝试连接。然后组件将查询设备的所有可用服务和特征值,并在日志中显示它们:

[18:24:56][D][ble_client:043]: Found device at MAC address [XX:XX:XX:XX:XX:XX]
[18:24:56][I][ble_client:072]: Attempting BLE connection to XX:XX:XX:XX:XX:XX
[18:24:56][I][ble_client:097]: [XX:XX:XX:XX:XX:XX] ESP_GATTC_OPEN_EVT
[18:24:57][I][ble_client:143]: Service UUID: 0x1800
[18:24:57][I][ble_client:144]: start_handle: 0x1 end_handle: 0x5
[18:24:57][I][ble_client:305]: characteristic 0x2A00, handle 0x3, properties 0x2
[18:24:57][I][ble_client:305]: characteristic 0x2A01, handle 0x5, properties 0x2
[18:24:57][I][ble_client:143]: Service UUID: 0x1801
[18:24:57][I][ble_client:144]: start_handle: 0x6 end_handle: 0x6
[18:24:57][I][ble_client:143]: Service UUID: 0x180A
[18:24:57][I][ble_client:144]: start_handle: 0x7 end_handle: 0x19
[18:24:57][I][ble_client:305]: characteristic 0x2A29, handle 0x9, properties 0x2
[18:24:57][I][ble_client:305]: characteristic 0x2A24, handle 0xb, properties 0x2
[18:24:57][I][ble_client:305]: characteristic 0x2A25, handle 0xd, properties 0x2
[18:24:57][I][ble_client:305]: characteristic 0x2A27, handle 0xf, properties 0x2
[18:24:57][I][ble_client:305]: characteristic 0x2A26, handle 0x11, properties 0x2
[18:24:57][I][ble_client:305]: characteristic 0x2A28, handle 0x13, properties 0x2
[18:24:57][I][ble_client:305]: characteristic 0x2A23, handle 0x15, properties 0x2
[18:24:57][I][ble_client:305]: characteristic 0x2A2A, handle 0x17, properties 0x2
[18:24:57][I][ble_client:305]: characteristic 0x2A50, handle 0x19, properties 0x2
[18:24:57][I][ble_client:143]: Service UUID: F000FFC0045140-00B0-0000-0000-000000
[18:24:57][I][ble_client:144]: start_handle: 0x1a end_handle: 0x22
[18:24:57][I][ble_client:305]: characteristic F000FFC1045140-00B0-0000-0000-000000, handle 0x1c, properties 0x1c
[18:24:57][I][ble_client:343]: descriptor 0x2902, handle 0x1d
[18:24:57][I][ble_client:343]: descriptor 0x2901, handle 0x1e
[18:24:57][I][ble_client:305]: characteristic F000FFC2045140-00B0-0000-0000-000000, handle 0x20, properties 0x1c
[18:24:57][I][ble_client:343]: descriptor 0x2902, handle 0x21
[18:24:57][I][ble_client:343]: descriptor 0x2901, handle 0x22
[18:24:57][I][ble_client:143]: Service UUID: 0xFFE0
[18:24:57][I][ble_client:144]: start_handle: 0x23 end_handle: 0x26
[18:24:57][I][ble_client:305]: characteristic 0xFFE1, handle 0x25, properties 0x10
[18:24:57][I][ble_client:343]: descriptor 0x2902, handle 0x26
[18:24:57][I][ble_client:143]: Service UUID: 0x1802
[18:24:57][I][ble_client:144]: start_handle: 0x27 end_handle: 0x29
[18:24:57][I][ble_client:305]: characteristic 0x2A06, handle 0x29, properties 0x4

发现的服务随后可用于启用和配置其他 ESPHome 组件,例如服务 UUID 0xFFE0 用于 iTag 风格的钥匙扣按钮事件,由BLE 客户端传感器组件使用。

使用固定密钥的安全连接:

esp32_ble:
io_capability: keyboard_only
esp32_ble_tracker:
ble_client:
- mac_address: XX:XX:XX:XX:XX:XX
id: pvvx_ble_display
on_passkey_request:
then:
- logger.log: "Authenticating with passkey"
- ble_client.passkey_reply:
id: pvvx_ble_display
passkey: 123456

使用动态生成密钥的安全连接:

api:
actions:
- action: passkey_reply
variables:
passkey: int
then:
- logger.log: "Authenticating with passkey"
- ble_client.passkey_reply:
id: my_ble_client
passkey: !lambda return passkey;
- action: numeric_comparison_reply
variables:
accept: bool
then:
- logger.log: "Authenticating with numeric comparison"
- ble_client.numeric_comparison_reply:
id: my_ble_client
accept: !lambda return accept;
esp32_ble:
io_capability: keyboard_display
esp32_ble_tracker:
ble_client:
- mac_address: XX:XX:XX:XX:XX:XX
id: my_ble_client
on_passkey_request:
then:
- logger.log: "Enter the passkey displayed on your BLE device"
- logger.log: " Go to https://my.home-assistant.io/redirect/developer_services/ and select passkey_reply"
on_passkey_notification:
then:
- logger.log:
format: "Enter this passkey on your BLE device: %06d"
args: [ passkey ]
on_numeric_comparison_request:
then:
- logger.log:
format: "Compare this passkey with the one on your BLE device: %06d"
args: [ passkey ]
- logger.log: " Go to https://my.home-assistant.io/redirect/developer_services/ and select numeric_comparison_reply"
on_connect:
then:
- logger.log: "Connected"