bluetooth — низкоуровневый Bluetooth

Этот модуль предоставляет интерфейс к встроенному контроллеру Bluetooth. Он поддерживает Bluetooth Low Energy (BLE) в ролях Central, Peripheral, Broadcaster и Observer, а также GATT-сервер и клиент и каналы L2CAP, ориентированные на соединение. Устройство может одновременно работать в нескольких ролях. Также поддерживаются сопряжение (pairing) и привязка (bonding).

Этот API предназначен для соответствия низкоуровневому протоколу Bluetooth и предоставления строительных блоков для более высокоуровневых абстракций, таких как конкретные типы устройств.

Совет

Для большинства приложений предпочтительнее использовать более высокоуровневую библиотеку aioble, которая предоставляет основанную на asyncio обёртку вокруг этого модуля. См. aioble — асинхронный BLE.

class BLE

class bluetooth.BLE

Возвращает синглтон-объект BLE.

Конфигурация

active(active: bool | None = None, /) bool

Опционально изменяет активное состояние BLE-радио и возвращает текущее состояние.

Радио должно быть переведено в активное состояние перед использованием любых других методов этого класса.

config(param: str, /) Any
config(*, **kwargs: Any) None

Получает или задаёт значения конфигурации интерфейса BLE. Для получения значения имя параметра должно быть указано в виде строки в кавычках, и за один раз запрашивается только один параметр. Для задания значений используйте синтаксис с ключевыми словами, при этом за один раз можно задать один или несколько параметров.

В настоящее время поддерживаются следующие значения:

  • 'mac': Текущий используемый адрес в зависимости от текущего режима адресации. Возвращает кортеж (addr_type, addr).

    См. gap_scan для получения подробностей о типе адреса.

    Это значение может быть запрошено только когда интерфейс в данный момент активен.

  • 'addr_mode': Задаёт режим адресации. Значения:

    Значение

    Имя

    Поведение

    0x00

    PUBLIC

    Использовать публичный адрес контроллера.

    0x01

    RANDOM

    Использовать сгенерированный статический адрес.

    0x02

    RPA

    Использовать разрешаемые приватные адреса.

    0x03

    NRPA

    Использовать неразрешаемые приватные адреса.

    По умолчанию интерфейс будет использовать адрес PUBLIC, если он доступен, в противном случае будет использован адрес RANDOM.

  • 'gap_name': Получает/задаёт имя GAP-устройства, используемое сервисом Generic Access (UUID 0x1800), характеристикой Device Name (UUID 0x2a00). Его можно задавать в любое время и изменять несколько раз.

  • 'rxbuf': Получает/задаёт размер в байтах внутреннего буфера, используемого для хранения входящих событий. Этот буфер является глобальным для всего драйвера BLE и поэтому обрабатывает входящие данные для всех событий, включая все характеристики. Его увеличение позволяет лучше обрабатывать пакетные входящие данные (например, результаты сканирования) и принимать значения характеристик большего размера.

  • 'mtu': Получает/задаёт MTU, который будет использоваться во время обмена ATT MTU. Результирующий MTU будет минимумом из этого значения и MTU удалённого устройства. Обмен ATT MTU не происходит автоматически (если только удалённое устройство не инициирует его) и должен быть запущен вручную с помощью gattc_exchange_mtu. Используйте событие _IRQ_MTU_EXCHANGED, чтобы узнать MTU для данного соединения.

  • 'bond': Задаёт, будет ли включена привязка (bonding) во время сопряжения. Когда включено, запросы на сопряжение будут устанавливать флаг «bond», и ключи будут сохраняться обоими устройствами.

  • 'mitm': Задаёт, требуется ли защита от MITM для сопряжения.

  • 'io': Задаёт возможности ввода/вывода (I/O) этого устройства.

    Доступны следующие варианты:

    Константа

    Значение

    Возможность

    _IO_CAPABILITY_DISPLAY_ONLY

    0

    Только дисплей

    _IO_CAPABILITY_DISPLAY_YESNO

    1

    Дисплей с вводом да/нет

    _IO_CAPABILITY_KEYBOARD_ONLY

    2

    Только клавиатура

    _IO_CAPABILITY_NO_INPUT_OUTPUT

    3

    Нет ввода или вывода

    _IO_CAPABILITY_KEYBOARD_DISPLAY

    4

    Клавиатура и дисплей

  • 'le_secure': Задаёт, требуется ли сопряжение «LE Secure». По умолчанию false (т. е. разрешается «Legacy Pairing»).

Обработка событий

irq(handler: Callable[[int, Tuple], Any | None], /) None

Регистрирует функцию обратного вызова для событий от стека BLE. handler принимает два аргумента: event (который будет одним из кодов ниже) и data (который представляет собой специфичный для события кортеж значений).

Примечание: В качестве оптимизации для предотвращения ненужных аллокаций записи addr, adv_data, char_data, notify_data и uuid в кортежах являются доступными только для чтения экземплярами memoryview, указывающими на внутренний кольцевой буфер модуля bluetooth, и действительны только во время вызова функции-обработчика IRQ. Если вашей программе нужно сохранить одно из этих значений для доступа после возврата из обработчика IRQ (например, сохранив его в экземпляре класса или глобальной переменной), то ей нужно сделать копию данных, используя либо bytes(), либо bluetooth.UUID(), например так:

connected_addr = bytes(addr)  # equivalently: adv_data, char_data, or notify_data
matched_uuid = bluetooth.UUID(uuid)

Например, обработчик IRQ для результата сканирования может проверить adv_data, чтобы решить, является ли это правильным устройством, и только затем скопировать данные адреса для использования в другом месте программы. А для вывода данных изнутри обработчика IRQ потребуется print(bytes(addr)).

Обработчик обычно выполняет диспетчеризацию по коду события и распаковывает специфичный для события кортеж полезной нагрузки:

def bt_irq(event, data):
    if event == _IRQ_CENTRAL_CONNECT:
        conn_handle, addr_type, addr = data
        ...
    elif event == _IRQ_SCAN_RESULT:
        addr_type, addr, adv_type, rssi, adv_data = data
        ...

Каждый код события, доставляемая им полезная нагрузка и краткое описание перечислены ниже. Для событий, у которых упоминается поле status, значение status равно 0 при успехе и зависящему от реализации ненулевому значению при неудаче.

Константа

Значение

Событие

Кортеж полезной нагрузки

_IRQ_CENTRAL_CONNECT

1

Центральное устройство подключилось к этому периферийному устройству.

(conn_handle, addr_type, addr)

_IRQ_CENTRAL_DISCONNECT

2

Центральное устройство отключилось от этого периферийного устройства.

(conn_handle, addr_type, addr)

_IRQ_GATTS_WRITE

3

Подключённый клиент произвёл запись в локальную характеристику или дескриптор. Используйте gatts_read для получения нового значения.

(conn_handle, attr_handle)

_IRQ_GATTS_READ_REQUEST

4

Подключённый клиент выполнил чтение. Верните ненулевой код ошибки из таблицы ниже, чтобы запретить чтение, или 0 / None, чтобы принять его.

(conn_handle, attr_handle)

_IRQ_SCAN_RESULT

5

Во время активного сканирования был получен один рекламный (advertising) пакет.

(addr_type, addr, adv_type, rssi, adv_data)

_IRQ_SCAN_DONE

6

Текущее сканирование завершилось — либо потому что истекла настроенная продолжительность, либо потому что был вызван gap_scan(None).

()

_IRQ_PERIPHERAL_CONNECT

7

Ранее выполненный gap_connect завершился успешно.

(conn_handle, addr_type, addr)

_IRQ_PERIPHERAL_DISCONNECT

8

Подключённое периферийное устройство отключилось.

(conn_handle, addr_type, addr)

_IRQ_GATTC_SERVICE_RESULT

9

Один сервис был найден с помощью gattc_discover_services.

(conn_handle, start_handle, end_handle, uuid)

_IRQ_GATTC_SERVICE_DONE

10

Обнаружение сервисов завершено.

(conn_handle, status)

_IRQ_GATTC_CHARACTERISTIC_RESULT

11

Одна характеристика была найдена с помощью gattc_discover_characteristics.

(conn_handle, end_handle, value_handle, properties, uuid)

_IRQ_GATTC_CHARACTERISTIC_DONE

12

Обнаружение характеристик завершено.

(conn_handle, status)

_IRQ_GATTC_DESCRIPTOR_RESULT

13

Один дескриптор был найден с помощью gattc_discover_descriptors.

(conn_handle, dsc_handle, uuid)

_IRQ_GATTC_DESCRIPTOR_DONE

14

Обнаружение дескрипторов завершено.

(conn_handle, status)

_IRQ_GATTC_READ_RESULT

15

Ранее выполненный gattc_read вернул данные.

(conn_handle, value_handle, char_data)

_IRQ_GATTC_READ_DONE

16

Ранее выполненный gattc_read завершился.

(conn_handle, value_handle, status)

_IRQ_GATTC_WRITE_DONE

17

Ранее выполненный gattc_write был подтверждён.

(conn_handle, value_handle, status)

_IRQ_GATTC_NOTIFY

18

Удалённый сервер отправил (неподтверждаемое) уведомление (notification).

(conn_handle, value_handle, notify_data)

_IRQ_GATTC_INDICATE

19

Удалённый сервер отправил (подтверждаемое) указание (indication).

(conn_handle, value_handle, notify_data)

_IRQ_GATTS_INDICATE_DONE

20

Ранее отправленное указание было подтверждено клиентом (или истёк его тайм-аут).

(conn_handle, value_handle, status)

_IRQ_MTU_EXCHANGED

21

Обмен ATT MTU завершён (инициированный любой из сторон).

(conn_handle, mtu)

_IRQ_L2CAP_ACCEPT

22

Удалённое устройство запросило L2CAP-соединение на PSM, который прослушивает это устройство. Верните ненулевое целое число, чтобы отклонить, или 0 / None, чтобы принять.

(conn_handle, cid, psm, our_mtu, peer_mtu)

_IRQ_L2CAP_CONNECT

23

L2CAP-канал теперь установлен — либо принятием входящего запроса, либо завершением исходящего l2cap_connect.

(conn_handle, cid, psm, our_mtu, peer_mtu)

_IRQ_L2CAP_DISCONNECT

24

L2CAP-канал был отключён. status равен 0 для чистого отключения или ненулевому значению, если исходящая попытка соединения не удалась.

(conn_handle, cid, psm, status)

_IRQ_L2CAP_RECV

25

На L2CAP-канал поступили данные. Вызовите l2cap_recvinto для их чтения.

(conn_handle, cid)

_IRQ_L2CAP_SEND_READY

26

Предыдущий вызов l2cap_send, вернувший False, опустошился, и канал снова готов. Ненулевой status означает, что буфер передачи переполнился, и приложение должно повторно отправить данные.

(conn_handle, cid, status)

_IRQ_CONNECTION_UPDATE

27

Удалённое устройство обновило параметры соединения (интервал, задержку, тайм-аут супервизии).

(conn_handle, conn_interval, conn_latency, supervision_timeout, status)

_IRQ_ENCRYPTION_UPDATE

28

Состояние шифрования соединения изменилось, обычно после завершения сопряжения или привязки.

(conn_handle, encrypted, authenticated, bonded, key_size)

_IRQ_GET_SECRET

29

Стек запрашивает сохранённый секрет привязки. Если key равен None, верните index-е по счёту сохранённое значение типа sec_type; в противном случае верните значение, связанное с заданным (sec_type, key). Верните None, если ничего не сохранено.

(sec_type, index, key)

_IRQ_SET_SECRET

30

Стек просит приложение сохранить секрет привязки в постоянном хранилище. Верните True после сохранения.

(sec_type, key, value)

_IRQ_PASSKEY_ACTION

31

В рамках сопряжения требуется действие с ключом доступа (passkey). Ответьте с помощью gap_passkey; возможные действия см. в таблице действий с passkey ниже.

(conn_handle, action, passkey)

Для события _IRQ_GATTS_READ_REQUEST доступны следующие коды возврата:

Константа

Значение

Значение

_GATTS_NO_ERROR

0x00

Принять чтение.

_GATTS_ERROR_READ_NOT_PERMITTED

0x02

Чтение не разрешено.

_GATTS_ERROR_WRITE_NOT_PERMITTED

0x03

Запись не разрешена.

_GATTS_ERROR_INSUFFICIENT_AUTHENTICATION

0x05

Клиент не аутентифицирован.

_GATTS_ERROR_INSUFFICIENT_AUTHORIZATION

0x08

Клиент не авторизован.

_GATTS_ERROR_INSUFFICIENT_ENCRYPTION

0x0f

Канал не зашифрован.

Для события _IRQ_PASSKEY_ACTION доступны следующие действия:

Константа

Значение

Значение

_PASSKEY_ACTION_NONE

0

Действие не требуется.

_PASSKEY_ACTION_INPUT

2

Предложить пользователю ввести ключ доступа, показанный на удалённом устройстве.

_PASSKEY_ACTION_DISPLAY

3

Отобразить 6-значный ключ доступа для ввода на удалённом устройстве.

_PASSKEY_ACTION_NUMERIC_COMPARISON

4

Подтвердить, что ключ доступа совпадает с тем, который показан на удалённом устройстве.

Для экономии места в прошивке эти константы не включены в модуль bluetooth. Добавьте нужные вам из списков выше в свою программу.

Роль Broadcaster (рекламодатель)

gap_advertise(interval_us: int | None, adv_data: bytes | None = None, *, resp_data: bytes | None = None, connectable: bool = True) None

Запускает рекламу (advertising) с указанным интервалом (в микросекундах). Этот интервал будет округлён вниз до ближайших 625 мкс. Чтобы остановить рекламу, установите interval_us в None.

adv_data и resp_data могут быть любого типа, реализующего протокол буфера (например, bytes, bytearray, str). adv_data включается во все рассылки, а resp_data отправляется в ответ на активное сканирование.

Примечание: если adv_data (или resp_data) равны None, то будут повторно использованы данные, переданные при предыдущем вызове gap_advertise. Это позволяет рекламодателю возобновить рекламу просто с помощью gap_advertise(interval_us). Чтобы очистить рекламную полезную нагрузку, передайте пустой bytes, т. е. b''.

Роль Observer (сканер)

gap_scan(duration_ms: int | None, interval_us: int = 1280000, window_us: int = 11250, active: bool = False, /) None

Выполняет операцию сканирования, длящуюся указанную продолжительность (в миллисекундах).

Для бесконечного сканирования установите duration_ms в 0.

Чтобы остановить сканирование, установите duration_ms в None.

Используйте interval_us и window_us для опциональной настройки рабочего цикла. Сканер будет работать в течение window_us микросекунд каждые interval_us микросекунд в общей сложности duration_ms миллисекунд. Интервал и окно по умолчанию составляют 1,28 секунды и 11,25 миллисекунды соответственно (фоновое сканирование).

Для каждого результата сканирования будет вызвано событие _IRQ_SCAN_RESULT с данными события (addr_type, addr, adv_type, rssi, adv_data).

Значения addr_type указывают на публичные или случайные адреса:

Значение

Имя

Значение

0x00

PUBLIC

Публичный адрес устройства.

0x01

RANDOM

Случайный адрес (статический, RPA или NRPA; тип закодирован в самом адресе).

Значения adv_type соответствуют спецификации Bluetooth:

Значение

Имя

Значение

0x00

ADV_IND

Подключаемая и сканируемая ненаправленная реклама.

0x01

ADV_DIRECT_IND

Подключаемая направленная реклама.

0x02

ADV_SCAN_IND

Сканируемая ненаправленная реклама.

0x03

ADV_NONCONN_IND

Неподключаемая ненаправленная реклама.

0x04

SCAN_RSP

Ответ на сканирование.

active можно установить в True, если вы хотите получать ответы на сканирование в результатах.

Когда сканирование останавливается (либо из-за завершения продолжительности, либо при явной остановке), будет вызвано событие _IRQ_SCAN_DONE.

Роль Central

Центральное устройство может подключаться к периферийным устройствам, которые оно обнаружило с помощью роли Observer (см. gap_scan), или к устройствам с известным адресом.

gap_connect(addr_type: int | None, addr: bytes | None = None, scan_duration_ms: int = 2000, min_conn_interval_us: int | None = None, max_conn_interval_us: int | None = None, /) None

Подключиться к периферийному устройству.

См. gap_scan для получения подробностей о типах адресов.

Чтобы досрочно отменить незавершённую попытку соединения, вызовите gap_connect(None).

При успехе будет вызвано событие _IRQ_PERIPHERAL_CONNECT. При отмене попытки соединения будет вызвано событие _IRQ_PERIPHERAL_DISCONNECT.

Устройство будет ждать до scan_duration_ms, чтобы получить рекламную полезную нагрузку от устройства.

Интервал соединения можно настроить в микросекундах с помощью одного или обоих параметров min_conn_interval_us и max_conn_interval_us. В противном случае будет выбран интервал по умолчанию, обычно от 30000 до 50000 микросекунд. Более короткий интервал увеличит пропускную способность за счёт энергопотребления.

Роль Peripheral

Ожидается, что периферийное устройство отправляет подключаемую рекламу (см. gap_advertise). Обычно оно действует как GATT-сервер, предварительно зарегистрировав сервисы и характеристики с помощью gatts_register_services.

Когда центральное устройство подключается, будет вызвано событие _IRQ_CENTRAL_CONNECT.

Роли Central и Peripheral

gap_disconnect(conn_handle: int, /) bool

Разорвать соединение с указанным дескриптором соединения. Это может быть как центральное устройство, подключившееся к этому устройству (если оно действует как периферийное), так и периферийное устройство, к которому это устройство ранее подключилось (если оно действует как центральное).

При успехе будет вызвано событие _IRQ_PERIPHERAL_DISCONNECT или _IRQ_CENTRAL_DISCONNECT.

Возвращает False, если дескриптор соединения не был подключён, и True в противном случае.

GATT-сервер

GATT-сервер имеет набор зарегистрированных сервисов. Каждый сервис может содержать характеристики, каждая из которых имеет значение. Характеристики также могут содержать дескрипторы, которые сами имеют значения.

Эти значения хранятся локально и доступны по их «дескриптору значения» (value handle), который генерируется во время регистрации сервиса. Их также может читать или записывать удалённое клиентское устройство. Кроме того, сервер может «уведомлять» (notify) о характеристике подключённого клиента через дескриптор соединения.

Устройство в роли Central или Peripheral может функционировать как GATT-сервер, однако в большинстве случаев чаще именно периферийное устройство будет действовать как сервер.

Характеристики и дескрипторы по умолчанию имеют максимальный размер 20 байт (ATT MTU по умолчанию в 23 байта минус 3-байтовый заголовок ATT; больший согласованный MTU сам по себе не увеличивает этот предел). Всё, что клиент в них записывает, будет усечено до этой длины. Однако любая локальная запись увеличит максимальный размер, поэтому, если вы хотите разрешить более крупные записи от клиента в данную характеристику, используйте gatts_write после регистрации, например gatts_write(char_handle, bytes(100)).

gatts_register_services(services_definition: Sequence[Sequence], /) Sequence[Sequence[int]]

Настраивает сервер с указанными сервисами, заменяя любые существующие сервисы.

services_definition — это список сервисов, где каждый сервис представляет собой кортеж из двух элементов, содержащий UUID и список характеристик.

Каждая характеристика — это кортеж из двух или трёх элементов, содержащий UUID, значение flags и опционально список дескрипторов.

Каждый дескриптор — это кортеж из двух элементов, содержащий UUID и значение flags.

flags представляют собой побитовую комбинацию (OR) флагов, определённых ниже. Они задают как поведение характеристики (или дескриптора), так и требования к безопасности и приватности.

Возвращаемое значение — это список (по одному элементу на сервис) кортежей (каждый элемент — это дескриптор значения). Дескрипторы характеристик и дескрипторов сведены в один кортеж в том порядке, в котором они определены.

В следующем примере регистрируются два сервиса (Heart Rate и Nordic UART):

bt = bluetooth.BLE()
bt.active(True)

# Heart Rate service: one Heart Rate Measurement characteristic.
HR_SERVICE = (
    bluetooth.UUID(0x180D),
    (
        (bluetooth.UUID(0x2A37),
         bluetooth.FLAG_READ | bluetooth.FLAG_NOTIFY),
    ),
)

# Nordic UART service: a TX characteristic the client subscribes
# to for notifications, and an RX characteristic it writes to.
UART_SERVICE = (
    bluetooth.UUID('6E400001-B5A3-F393-E0A9-E50E24DCCA9E'),
    (
        (bluetooth.UUID('6E400003-B5A3-F393-E0A9-E50E24DCCA9E'),
         bluetooth.FLAG_READ | bluetooth.FLAG_NOTIFY),
        (bluetooth.UUID('6E400002-B5A3-F393-E0A9-E50E24DCCA9E'),
         bluetooth.FLAG_WRITE),
    ),
)

((hr,), (tx, rx)) = bt.gatts_register_services(
    (HR_SERVICE, UART_SERVICE),
)

Три дескриптора значений (hr, tx, rx) можно использовать с gatts_read, gatts_write, gatts_notify и gatts_indicate.

Примечание: Реклама должна быть остановлена перед регистрацией сервисов.

Доступные флаги для характеристик и дескрипторов:

Константа

Значение

Значение

_FLAG_BROADCAST

0x0001

О характеристике можно вещать (broadcast).

_FLAG_READ

0x0002

Клиент может читать значение.

_FLAG_WRITE_NO_RESPONSE

0x0004

Клиент может выполнять запись без ожидания ответа.

_FLAG_WRITE

0x0008

Клиент может выполнять запись с подтверждаемым ответом.

_FLAG_NOTIFY

0x0010

Сервер может отправлять уведомления (неподтверждаемые).

_FLAG_INDICATE

0x0020

Сервер может отправлять указания (подтверждаемые).

_FLAG_AUTHENTICATED_SIGNED_WRITE

0x0040

Клиент может выполнять подписанные записи.

_FLAG_AUX_WRITE

0x0100

Расширенные свойства: разрешены записи в очередь/надёжные записи.

_FLAG_READ_ENCRYPTED

0x0200

Чтение требует зашифрованного канала.

_FLAG_READ_AUTHENTICATED

0x0400

Чтение требует аутентифицированного (с защитой от MITM) канала.

_FLAG_READ_AUTHORIZED

0x0800

Чтение требует авторизации на уровне приложения.

_FLAG_WRITE_ENCRYPTED

0x1000

Запись требует зашифрованного канала.

_FLAG_WRITE_AUTHENTICATED

0x2000

Запись требует аутентифицированного (с защитой от MITM) канала.

_FLAG_WRITE_AUTHORIZED

0x4000

Запись требует авторизации на уровне приложения.

Как и в случае с константами событий выше, эти флаги не предоставляются модулем bluetooth; скопируйте нужные вам в свою программу.

gatts_read(value_handle: int, /) bytes

Читает локальное значение для этого дескриптора (которое было записано либо с помощью gatts_write, либо удалённым клиентом).

gatts_write(value_handle: int, data: bytes, send_update: bool = False, /) None

Записывает локальное значение для этого дескриптора, которое может быть прочитано клиентом.

Если send_update равно True, то все подписанные клиенты будут уведомлены (или им будет отправлено указание, в зависимости от того, на что они подписаны и какие операции поддерживает характеристика) об этой записи.

gatts_notify(conn_handle: int, value_handle: int, data: bytes | None = None, /) None

Отправляет запрос на уведомление подключённому клиенту.

Если data равно None (по умолчанию), то будет отправлено текущее локальное значение (как установлено с помощью gatts_write).

В противном случае, если data не равно None, то это значение отправляется клиенту в составе уведомления. Локальное значение не будет изменено.

Примечание: Уведомление будет отправлено независимо от статуса подписки клиента на эту характеристику.

gatts_indicate(conn_handle: int, value_handle: int, data: bytes | None = None, /) None

Отправляет запрос на указание подключённому клиенту.

Если data равно None (по умолчанию), то будет отправлено текущее локальное значение (как установлено с помощью gatts_write).

В противном случае, если data не равно None, то это значение отправляется клиенту в составе указания. Локальное значение не будет изменено.

При подтверждении (или неудаче, например тайм-ауте) будет вызвано событие _IRQ_GATTS_INDICATE_DONE.

Примечание: Указание будет отправлено независимо от статуса подписки клиента на эту характеристику.

gatts_set_buffer(value_handle: int, len: int, append: bool = False, /) None

Задаёт размер внутреннего буфера для значения в байтах. Это ограничит наибольшую возможную запись, которая может быть получена. По умолчанию 20 байт (ATT MTU по умолчанию в 23 минус 3-байтовый заголовок ATT).

Установка append в True приведёт к тому, что все удалённые записи будут добавляться к текущему значению, а не заменять его. Таким образом можно буферизировать не более len байт. Когда вы используете gatts_read, значение будет очищено после чтения. Эта функция полезна при реализации чего-то вроде Nordic UART Service.

GATT-клиент

GATT-клиент может обнаруживать и читать/записывать характеристики на удалённом GATT-сервере.

Чаще именно устройство в роли Central действует как GATT-клиент, однако периферийное устройство также может действовать как клиент, чтобы обнаружить информацию о центральном устройстве, которое к нему подключилось (например, прочитать имя устройства из сервиса device information).

gattc_discover_services(conn_handle: int, uuid: UUID | None = None, /) None

Запросить у подключённого сервера его сервисы.

Опционально укажите uuid сервиса, чтобы запросить только этот сервис.

Для каждого обнаруженного сервиса будет вызвано событие _IRQ_GATTC_SERVICE_RESULT, за которым по завершении последует _IRQ_GATTC_SERVICE_DONE.

gattc_discover_characteristics(conn_handle: int, start_handle: int, end_handle: int, uuid: UUID | None = None, /) None

Запросить у подключённого сервера характеристики в указанном диапазоне.

Опционально укажите uuid характеристики, чтобы запросить только эту характеристику.

Передача start_handle=1 и end_handle=0xffff охватывает весь диапазон дескрипторов атрибутов GATT, поэтому эта комбинация фактически просматривает каждый сервис на удалённом устройстве.

Для каждой обнаруженной характеристики будет вызвано событие _IRQ_GATTC_CHARACTERISTIC_RESULT, за которым по завершении последует _IRQ_GATTC_CHARACTERISTIC_DONE.

gattc_discover_descriptors(conn_handle: int, start_handle: int, end_handle: int, /) None

Запросить у подключённого сервера дескрипторы в указанном диапазоне.

Для каждого обнаруженного дескриптора будет вызвано событие _IRQ_GATTC_DESCRIPTOR_RESULT, за которым по завершении последует _IRQ_GATTC_DESCRIPTOR_DONE.

gattc_read(conn_handle: int, value_handle: int, /) None

Выполнить удалённое чтение на подключённом сервере для указанного дескриптора характеристики или дескриптора.

Когда значение будет доступно, будет вызвано событие _IRQ_GATTC_READ_RESULT, за которым по завершении последует _IRQ_GATTC_READ_DONE.

gattc_write(conn_handle: int, value_handle: int, data: bytes, mode: int = 0, /) None

Выполнить удалённую запись на подключённом сервере для указанного дескриптора характеристики или дескриптора.

Аргумент mode задаёт поведение записи, при этом в настоящее время поддерживаются следующие значения:

  • mode=0 (по умолчанию) — это запись без ответа: запись будет отправлена удалённому серверу, но подтверждение не будет возвращено и событие не будет вызвано.

  • mode=1 — это запись с ответом: удалённому серверу предлагается отправить ответ/подтверждение того, что он получил данные.

Если от удалённого сервера получен ответ, будет вызвано событие _IRQ_GATTC_WRITE_DONE.

gattc_exchange_mtu(conn_handle: int, /) None

Инициировать обмен MTU с подключённым сервером, используя предпочтительный MTU, заданный с помощью BLE.config(mtu=value).

Событие _IRQ_MTU_EXCHANGED будет вызвано по завершении обмена MTU.

Обмен MTU обычно инициируется центральным устройством; NimBLE поддерживает обе роли.

L2CAP-каналы, ориентированные на соединение

Эта функция позволяет обмениваться данными между двумя BLE-устройствами способом, похожим на сокеты. После того как устройства соединены через GAP, любое устройство может прослушивать подключение другого на числовом PSM (Protocol/Service Multiplexer).

В каждый момент времени может быть активен только один L2CAP-канал (т. е. вы не можете подключаться во время прослушивания).

Активные L2CAP-каналы идентифицируются дескриптором соединения, на котором они были установлены, и CID (идентификатором канала).

Каналы, ориентированные на соединение, имеют встроенное управление потоком на основе кредитов. В отличие от ATT, где устройства согласуют общий MTU, и прослушивающее, и подключающееся устройство каждое задаёт независимый MTU, который ограничивает максимальный объём ещё не обработанных данных, которые удалённое устройство может отправить, прежде чем они будут полностью использованы в l2cap_recvinto.

l2cap_listen(psm: int, mtu: int, /) None

Начать прослушивание входящих запросов на L2CAP-канал на указанном psm с локальным MTU, установленным в mtu.

Когда удалённое устройство инициирует соединение, будет вызвано событие _IRQ_L2CAP_ACCEPT, которое даёт прослушивающему серверу возможность отклонить входящее соединение (вернув ненулевое целое число).

После принятия соединения будет вызвано событие _IRQ_L2CAP_CONNECT, позволяющее серверу получить идентификатор канала (CID), а также локальный и удалённый MTU.

Примечание: В настоящее время остановить прослушивание невозможно.

l2cap_connect(conn_handle: int, psm: int, mtu: int, /) None

Подключиться к прослушивающему узлу на указанном psm с локальным MTU, установленным в mtu.

При успешном соединении будет вызвано событие _IRQ_L2CAP_CONNECT, позволяющее клиенту получить CID, а также локальный и удалённый (peer) MTU.

Неуспешное соединение вызовет событие _IRQ_L2CAP_DISCONNECT с ненулевым статусом.

l2cap_disconnect(conn_handle: int, cid: int, /) None

Разорвать активный L2CAP-канал с указанными conn_handle и cid.

l2cap_send(conn_handle: int, cid: int, buf: bytes, /) bool

Отправить указанный buf (который должен поддерживать протокол буфера) по L2CAP-каналу, идентифицируемому conn_handle и cid.

Буфер должен удовлетворять обоим ограничениям: он не должен превышать удалённый (peer) MTU и не должен превышать удвоенный локальный MTU.

Это вернёт False, если канал теперь «застопорен» (stalled), что означает, что l2cap_send не должен вызываться снова, пока не будет получено событие _IRQ_L2CAP_SEND_READY (которое произойдёт, когда удалённое устройство предоставит больше кредитов, обычно после того, как оно получит и обработает данные).

l2cap_recvinto(conn_handle: int, cid: int, buf: Any | None, /) int

Получить данные от указанных conn_handle и cid в предоставленный buf (который должен поддерживать протокол буфера, например bytearray или memoryview).

Возвращает количество байт, прочитанных из канала.

Если buf равен None, то возвращает количество доступных байт.

Примечание: После получения события _IRQ_L2CAP_RECV приложение должно продолжать вызывать l2cap_recvinto, пока в приёмном буфере не останется доступных байт (обычно вплоть до размера удалённого (peer) MTU).

Пока приёмный буфер не опустеет, удалённому устройству не будет предоставлено больше кредитов канала, и оно не сможет отправлять больше данных.

Сопряжение и привязка

Сопряжение (pairing) позволяет зашифровать и аутентифицировать соединение посредством обмена секретами (с опциональной защитой от MITM через аутентификацию по ключу доступа).

Привязка (bonding) — это процесс сохранения этих секретов в энергонезависимое хранилище. После привязки устройство способно разрешать разрешаемый приватный адрес (RPA) другого устройства на основе сохранённого ключа разрешения идентичности (IRK). Для поддержки привязки приложение должно реализовать события _IRQ_GET_SECRET и _IRQ_SET_SECRET.

gap_pair(conn_handle: int, /) None

Инициировать сопряжение с удалённым устройством.

Перед вызовом этого метода убедитесь, что параметры конфигурации io, mitm, le_secure и bond заданы (через config).

При успешном сопряжении будет вызвано событие _IRQ_ENCRYPTION_UPDATE.

gap_passkey(conn_handle: int, action: int, passkey: int, /) None

Ответить на событие _IRQ_PASSKEY_ACTION для указанных conn_handle и action. Значение passkey зависит от action (которое, в свою очередь, зависит от настроенной возможности ввода/вывода):

Действие

Требуемый ответ passkey

_PASSKEY_ACTION_INPUT

Ключ доступа, который пользователь считывает с удалённого устройства.

_PASSKEY_ACTION_DISPLAY

Локально сгенерированный случайный 6-значный ключ доступа, показанный пользователю.

_PASSKEY_ACTION_NUMERIC_COMPARISON

1, чтобы принять ключ доступа, показанный в событии _IRQ_PASSKEY_ACTION, или 0, чтобы отменить сопряжение.

class UUID

class bluetooth.UUID(value: int | bytes | str, /)

Создаёт экземпляр UUID с указанным значением value. Bluetooth использует три ширины UUID; UUID принимает любую из них:

Ширина UUID

Принимаемые типы value

Пример

16-бит

int или 2-байтовый буфер (little-endian)

UUID(0x2908) или UUID(b'\x08\x29')

32-бит

4-байтовый буфер (little-endian)

UUID(b'\x08\x29\x00\x00')

128-бит

16-байтовый буфер или строка с дефисами

UUID('6E400001-B5A3-F393-E0A9-E50E24DCCA9E')

16- и 32-битные UUID обычно являются идентификаторами, выделенными SIG (см. присвоенные номера Bluetooth); 128-битные UUID обычно определяются производителем.