esphome-docs/components/ble_client.rst
2024-08-01 22:38:39 +12:00

514 lines
19 KiB
ReStructuredText

BLE Client
==========
.. seo::
:description: Configuration of the BLE client on ESP32.
:image: bluetooth.svg
The ``ble_client`` component enables connections to Bluetooth Low Energy devices in order to query and
control them. This component does not expose any sensors or output components itself, but merely manages
connections to them for use by other components.
.. warning::
The BLE software stack on the ESP32 consumes a significant amount of RAM on the device.
**Crashes are likely to occur** if you include too many additional components in your device's
configuration. Memory-intensive components such as :doc:`/components/voice_assistant` and other
audio components are most likely to cause issues.
.. note::
A maximum of three devices is supported due to limitations in the ESP32 BLE stack. If you wish to
connect more devices, use additional ESP32 boards.
This component supports devices that require a 6 digit PIN code for authentication.
Currently, devices connected with the client cannot be supported by other components based on
:doc:`/components/esp32_ble_tracker` as they listen to advertisements which are only sent by devices
without an active connection.
Despite the last point above, the ``ble_client`` component requires the ``esp32_ble_tracker`` component in order
to discover available client devices.
.. code-block:: yaml
esp32_ble_tracker:
ble_client:
- mac_address: XX:XX:XX:XX:XX:XX
id: itag_black
auto_connect: true
Configuration variables:
------------------------
- **mac_address** (**Required**, MAC Address): The MAC address of the BLE device to connect to.
- **auto_connect** (*Optional*, boolean): If true the device will be automatically connected when found by the :doc:`/components/esp32_ble_tracker`. Defaults to true.
- **id** (**Required**, :ref:`config-id`): The ID to use for code generation, and for reference by dependent components.
Automations:
- **on_connect** (*Optional*, :ref:`Automation <automation>`): An automation to perform
when the client connects to a device. See :ref:`ble_client-on_connect`.
- **on_disconnect** (*Optional*, :ref:`Automation <automation>`): An automation to perform
when the client disconnects from a device. See :ref:`ble_client-on_disconnect`.
- **on_passkey_request** (*Optional*, :ref:`Automation <automation>`): An automation to enter
the passkey required by the other BLE device. See :ref:`ble_client-on_passkey_request`.
- **on_passkey_notification** (*Optional*, :ref:`Automation <automation>`): An automation to
display the passkey to the user. See :ref:`ble_client-on_passkey_notification`.
- **on_numeric_comparison_request** (*Optional*, :ref:`Automation <automation>`): An automation to
compare the passkeys shown on the two BLE devices. See :ref:`ble_client-on_numeric_comparison_request`.
BLE Client Automation
---------------------
.. _ble_client-on_connect:
``on_connect``
**************
This automation is triggered when the client connects to the BLE device.
.. code-block:: yaml
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_client-on_disconnect:
``on_disconnect``
*****************
This automation is triggered when the client disconnects from a BLE device.
.. code-block:: yaml
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_client-on_passkey_request:
``on_passkey_request``
**********************
This automation is triggered when the BLE device requests a passkey for authentication.
.. code-block:: yaml
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_client-on_passkey_notification:
``on_passkey_notification``
***************************
This automation is triggered when a passkey is received from the BLE device.
.. code-block:: yaml
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_client-on_numeric_comparison_request:
``on_numeric_comparison_request``
*********************************
This automation is triggered when a numeric comparison is requested by the BLE device.
.. code-block:: yaml
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
.. _ble_client-connect_action:
``ble_client.connect`` Action
-----------------------------
This action is useful only for devices with ``auto_connect: false`` and allows a connection to be made from
within an automation. Once connected other actions like ``ble_write`` can be used. This is useful where
a BLE server needs only to be interacted with occasionally, and thus does not need a constant
connection held.
The following example updates the time of a Xiaomi MHO-C303 clock once per hour. Note that the BLE tracker must
be stopped during the connect attempt, and restarted afterwards. This would not be necessary if the tracker had
``continuous: false`` set. In this example scenario there is another BLE device that does require the scanner to be
on, hence the stop and start of the scan during connect.
.. code-block:: yaml
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:
Any actions after the ``connect`` action will proceed only after the connect succeeds. If the connect
fails the subsequent actions in the automation block will *not* be executed. This should be considered
if scanning has been stopped - another mechanism may be required to restart it.
.. _ble_client-disconnect_action:
``ble_client.disconnect`` Action
--------------------------------
This action disconnects a device that was connected with the ``ble_client.connect`` action.
Execution of the automation block sequence resumes after the disconnect has completed.
.. _ble_client-ble_write_action:
``ble_client.ble_write`` Action
-------------------------------
This action triggers a write to a specified BLE characteristic. The write is attempted in
a best-effort fashion and will only succeed if the ``ble_client``'s connection has been
established and the peripheral exposes the expected BLE service and characteristic.
Execution of the automation block sequence resumes after the write has completed. A write failure will *not*
stop execution of succeeding actions (this allows a disconnect to be executed, for example.)
Example usage:
.. code-block:: yaml
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
# List of bytes to write.
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
# A lambda returning an std::vector<uint8_t>.
value: !lambda |-
return {0x13, 0x37};
Configuration variables:
- **id** (**Required**, :ref:`config-id`): ID of the associated BLE client.
- **service_uuid** (**Required**, UUID): UUID of the service to write to.
- **characteristic_uuid** (**Required**, UUID): UUID of the service's characteristic to write to.
- **value** (**Required**, Array of bytes or :ref:`lambda <config-lambda>`): The value to be written.
.. _ble_client-passkey_reply_action:
``ble_client.passkey_reply`` Action
-----------------------------------
This action triggers an authentication attempt using the specified ``passkey``.
Example usage:
.. code-block:: yaml
on_...:
then:
- ble_client.passkey_reply:
id: my_ble_client
passkey: 123456
Configuration variables:
- **id** (**Required**, :ref:`config-id`): ID of the associated BLE client.
- **passkey** (**Required**, int): The 6-digit passkey.
.. _ble_client-numeric_comparison_reply_action:
``ble_client.numeric_comparison_reply`` Action
----------------------------------------------
This action triggers an authentication attempt after a numeric comparison.
Example usage:
.. code-block:: yaml
on_...:
then:
- ble_client.numeric_comparison_reply:
id: my_ble_client
accept: True
Configuration variables:
- **id** (**Required**, :ref:`config-id`): ID of the associated BLE client.
- **accept** (**Required**, boolean): Should be ``true`` if the passkeys
displayed on both BLE devices are matching.
.. _ble_client-remove_bond_action:
``ble_client.remove_bond`` Action
----------------------------------------------
This action removes a device from the security database and manages
unpairing.
Example usage:
.. code-block:: yaml
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
Configuration variables:
- **id** (**Required**, :ref:`config-id`): ID of the associated BLE client.
BLE Overview
------------
This section gives a brief overview of the Bluetooth LE architecture
to help with understanding this and the related components. There are
plenty of more detailed references online.
BLE uses the concept of a *server* and a *client*. In simple terms,
the server is implemented on the device providing services, usually
these are the devices such as heart monitors, tags, weather stations,
etc. The client connects to the server and makes use of its services.
The client will often be an app on a phone, or in the case of ESPHome,
it's the ESP32 device.
When a client connects to a server, the client queries for *services*
provided by the server. Services expose categories of functionality
on the server. These might be well defined and supported services,
such as the Battery Level service, Device Information or Heart Rate.
Or they might be custom services designed just for that device. For
example the button on cheap iTags uses a custom service.
Each service then defines one or more *characteristics* which are
typically the discrete values of that service. For example for the
Environmental Sensor service characteristics exposed include the
Wind Speed, Humidity and Rainfall. Each of these may be read-only
or read-write, depending on their functionality.
A characteristic may also expose one or more *descriptors*, which carry
further information about the characteristic. This could be things
like the units, the valid ranges, and whether notifications (see below)
are enabled.
BLE also supports *notifications*. A client continuously polling for
updates could consume a lot of power, which is undesirable for a
protocol that's designed to be low energy. Instead, a server can push
updates to the client only when they change. Depending on their purpose
and design, a characteristic may allow for notifications to be sent. The
client can then enable notifications by setting the configuration
descriptor for the characteristic.
Each service, characteristic, and descriptor is identified by a
unique identifier (UUID) that may be between 16 and 128 bits long.
A client will typically identify a device's capabilities based on
the UUIDs.
Once the connection is established, referencing each
service/characteristic/descriptor by the full UUID would take a
considerable portion of the small (~23 byte) packet. So the
characteristics and descriptors also provide a small 2-byte
*handle* (alias) to maximize available data space.
Setting Up Devices
------------------
Whilst the component can connect to most BLE devices, useful functionality
is only obtained through dependent components, such as :doc:`/components/sensor/ble_client`.
See the documentation for these components for details on setting up
specific devices.
In order to use the ``ble_client`` component, you need to enable the
:doc:`/components/esp32_ble_tracker` component. This will also allow you to discover
the MAC address of the device.
When you have discovered the MAC address of the device, you can add it
to the ``ble_client`` stanza.
If you then build and upload this configuration, the ESP will listen for
the device and attempt to connect to it when it is discovered. The component
will then query the device for all available services and characteristics and
display them in the log:
.. code-block:: text
[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
The discovered services can then be used to enable and configure other
ESPHome components, for example Service UUID 0xFFE0 is used for iTag style
keychain button events, used by the :doc:`/components/sensor/ble_client` component.
Passkey examples
----------------
Secure connection with a fixed passkey:
.. code-block:: yaml
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
Secure connection with a dynamically generated passkey:
.. code-block:: yaml
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"
See Also
--------
- :doc:`/components/sensor/ble_client`
- :ref:`Automation <automation>`
- :apiref:`ble_client/ble_client.h`
- :ghedit:`Edit`