Giter Club home page Giter Club logo

bumble's Introduction

 _                 _     _
| |               | |   | |
| |__  _   _ ____ | |__ | | _____
|  _ \| | | |    \|  _ \| || ___ |
| |_) ) |_| | | | | |_) ) || ____|
|____/|____/|_|_|_|____/ \_)_____)

Bluetooth Stack for Apps, Emulation, Test and Experimentation

Logo

Bumble is a full-featured Bluetooth stack written entirely in Python. It supports most of the common Bluetooth Low Energy (BLE) and Bluetooth Classic (BR/EDR) protocols and profiles, including GAP, L2CAP, ATT, GATT, SMP, SDP, RFCOMM, HFP, HID and A2DP. The stack can be used with physical radios via HCI over USB, UART, or the Linux VHCI, as well as virtual radios, including the virtual Bluetooth support of the Android emulator.

Documentation

Browse the pre-built Online Documentation, or see the documentation source under docs/mkdocs/src, or build the static HTML site from the markdown text with:

mkdocs build -f docs/mkdocs/mkdocs.yml

Usage

Getting Started

For a quick start to using Bumble, see the Getting Started guide.

Dependencies

To install package dependencies needed to run the bumble examples, execute the following commands:

python -m pip install --upgrade pip
python -m pip install ".[test,development,documentation]"

Examples

Refer to the Examples Documentation for details on the included example scripts and how to run them.

The complete list of Examples, and what they are designed to do is here.

There are also a set of Apps and Tools that show the utility of Bumble.

Using Bumble With a USB Dongle

Bumble is easiest to use with a dedicated USB dongle. This is because internal Bluetooth interfaces tend to be locked down by the operating system. You can use the usb_probe tool (all platforms) or lsusb (Linux or macOS) to list the available USB devices on your system.

See the USB Transport page for details on how to refer to USB devices. Also, if your are on a mac, see these instructions.

License

Licensed under the Apache 2.0 License.

Disclaimer

This is not an official Google product.

This library is in alpha and will be going through a lot of breaking changes. While releases will be stable enough for prototyping, experimentation and research, we do not recommend using it in any production environment yet. Expect bugs and sharp edges. Please help by trying it out, reporting bugs, and letting us know what you think!

bumble's People

Contributors

akuker avatar alanrosenthal avatar aleksandrovrts avatar barbibulle avatar benjaminlawson avatar benquike avatar deltaevo avatar dependabot[bot] avatar duohoo avatar eukub avatar fafroze avatar hchataing avatar jeru avatar marshallpierce avatar mogenson avatar qiaoccolato avatar rahularya50 avatar rdhavan avatar rramirezmata avatar servusdei2018 avatar silverbzh avatar snekarnataki avatar stefanst3000 avatar tavip avatar turon avatar uael avatar wescande avatar whitevegagabriel avatar yuyangh avatar zxzxwu avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

bumble's Issues

Gettings requirements to build wheel did not run successfully

Hello,
I'm facing an issue while installing the newest Bumble version ( > 0.0.134) on a fedora Linux 37 VM.

The following commands give me an error (see below):

pip install bumble
python -m pip install bumble

even while cloning and trying a local install with pip and python local cmd lines such as:

pip install -e .

error :

Collecting libusb-package==1.0.26.0
  Downloading libusb-package-1.0.26.0.tar.gz (408 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 408.7/408.7 kB 3.1 MB/s eta 0:00:00
  Installing build dependencies ... done
  Getting requirements to build wheel ... error
  error: subprocess-exited-with-error
  
  × Getting requirements to build wheel did not run successfully.
  │ exit code: 1
  ╰─> [18 lines of output]
.
.
.
        File "/tmp/pip-build-env-8ff85nug/overlay/lib/python3.11/site-packages/setuptools/build_meta.py", line 335, in run_setup
          exec(code, locals())
        File "<string>", line 20, in <module>
      ModuleNotFoundError: No module named 'tomli'
      [end of output]
  
  note: This error originates from a subprocess, and is likely not a problem with pip.
error: subprocess-exited-with-error

× Getting requirements to build wheel did not run successfully.
│ exit code: 1
╰─> See above for output.

note: This error originates from a subprocess, and is likely not a problem with pip.

I found one way to fix it: a newest version of libusb-package (1.0.26.1 works on my VMs).

In setup.cfg : libusb-package == 1.0.26.0; platform_system!='Emscripten' $\implies$ libusb-package == 1.0.26.1; platform_system!='Emscripten'

bumble-hci-bridge causing hci devices to dissapear?

Running ubuntu 22.04.1
I run
bumble-hci-bridge android-emulator:mode=controller usb:0
Which works as expected, however after I end this process (I am killing it, maybe there is a graceful way to shut this down?) the hci device I was using "hci0" in this case no longer shows up with a bluetooth command like hciconfig for example, it also no longer shows up if I use the Ubuntu GUI bluetooth. The usb device still shows up with lsusb and dmesg does not show the hci device being removed.

Possibility of connecting bumble tcp-server controller with Zephyr application?

I am reading the docs but something was not clear to me.

My use case is the following, I am developing a BLE peripheral with Zephyr OS. This RTOS has the option of compiling as a linux posix binary to do unit tests, host debugging, etc. It also allows to use an hci linux interface to use the bluetooth controller from host.

https://github.com/zephyrproject-rtos/zephyr/blob/e47fc45c4d4ed3c04d6a147e10d2a52630b7f0d7/drivers/bluetooth/hci/userchan.c#L193

My question is if its possible to add a virtual hci with bumble and make my linux binary (BLE Peripheral) connect to it. Afterwards I would like to have a second virtual hci that I can connect to test that my bluetooth application (BLE Central) works (integration tests).

(BLE Peripheral)_________________________________________________________________(BLE Central)
Zephyr OS Linux Binary <----> Virtual HCI < -----> Virtual HCI <-----> Integration test application (python, C, java...)

From the bumble website I see that this would be covered in Use Case 4. Which example should I refer to create the two virtual HCI interfaces and link my "emulated devices" to these virtual HCIs.?

In addition I might be interested in Use Case 3, where I do my integration tests directly with Bumble. Which example should I refer to for this purpose?

Cannot bridge tcp-server to android-netsim

As a follow up to this issue

#215

I am trying to connect a Zephyr App through a bumble virtual controller to an android emulator. I first executed the run_gatt_server.py example to verify that the grpc server from the android emulator is working. When I scan the device I can see it in the Android emulator.

However at this point I am not sure if I understand how to link the tcp-server virtual controller to the android-netsim host as I have not been able to connect a tcp-server virtual controller to the android-netsim transport.

Here have been my attempts

HCI Bridge

  1. Run the virtual controller

python controllers.py tcp-server:172.22.0.1:9000 tcp-server:172.22.0.1:9001

  1. Run Android emulator

emulator-avd Pixel_7_API_34 -packet-streamer-endpoint default -grpc 8557

  1. Connect my zephyr app to tcp-server:172.22.0.1:9000

  2. Create an hci-bridge

python hci_bridge.py tcp-client:172.22.0.1:9001 android-netsim

Relay Link

  1. Run the link relay

python link_relay

  1. Run the virtual controller with link relay

python controllers.py tcp-server:172.22.0.1:9000 link-relay:ws://localhost:10723/test

  1. Run Android emulator

emulator-avd Pixel_7_API_34 -packet-streamer-endpoint default -grpc 8557

  1. Connect my zephyr app to tcp-server:172.22.0.1:9000

  2. Bridge android-netsim host to link relay

python hci_bridge.py android-netsim link-relay:ws://localhost:10723/test

Macbook bluetooth access

Hello,
I would like to know how can I use the library with a Macbook Pro. Directly by using the mac bluetooth Chip (not a dongle). I don't know if you already have implemented this feature.
Thank you for your help.

[Feature Request] Delegate SMP address information

Currently, SMP always sends the address type corresponding to its connection

addr_type=self.connection.self_address.address_type,

However, in some cases(especially on Dual-mode devices), users may want to perform RPA exchange over a connection using random address(or vice versa, though it makes no sense).

Descriptors showing on Android but not on iOS

Hi!

First, a disclaimer: I am not 100% sure this is an issue with bumble.

I have the following code (based on run_notifier.py), which creates a device with a service that has a characteristic that has a descriptor. These attributes (service, characteristic and descriptor) have custom UUIDs.

ble_device.py:

import asyncio
import os
import logging
import json

from bumble.device import Device, Connection
from bumble.transport import open_transport_or_link
from bumble.gatt import Service, Characteristic, Descriptor
from bumble.att import Attribute
from bumble.core import AdvertisingData

# -----------------------------------------------------------------------------
class Listener(Device.Listener, Connection.Listener):
    def __init__(self, device):
        self.device = device

    def on_connection(self, connection):
        print(f'=== Connected to {connection}')
        connection.listener = self

    def on_disconnection(self, reason):
        print(f'### Disconnected, reason={reason}')

    def on_characteristic_subscription(
        self, connection, characteristic, notify_enabled, indicate_enabled
    ):
        print(
            f'$$$ Characteristic subscription for handle {characteristic.handle} '
            f'from {connection}: '
            f'notify {"enabled" if notify_enabled else "disabled"}, '
            f'indicate {"enabled" if indicate_enabled else "disabled"}'
        )

# -----------------------------------------------------------------------------
async def main():

    print('<<< connecting to HCI...')
    # hardcode the device config and the serial port

    async with await open_transport_or_link('serial:/dev/tty.usbmodem1101') as (hci_source, hci_sink):
        print('<<< connected')

        # Create a device to manage the host
        device = Device.from_config_file_with_hci('brc1k/device1.json', hci_source, hci_sink)
        device.listener = Listener(device)

        descriptor = Descriptor("de42bd76-cfd2-11ed-afa1-0242ac120002",
                        Attribute.READABLE | Attribute.WRITEABLE,
                        )
        characteristic = Characteristic(
                    "de42bbbe-cfd2-11ed-afa1-0242ac120002",
                    Characteristic.READ |  Characteristic.NOTIFY,
                    Characteristic.READABLE | Characteristic.WRITEABLE,
                    bytes(json.dumps("no value here"), encoding='utf8'),
                    descriptors=[descriptor]
                    )

        service = Service("de42b830-cfd2-11ed-afa1-0242ac120002",
                           [characteristic]
                           )

        device.add_services([service])
        device.advertising_data = bytes(
            AdvertisingData(
                [
                    (
                        AdvertisingData.COMPLETE_LOCAL_NAME,
                        bytes('Bumble', 'utf-8'),
                    ),
                ]
            )
        )

        # Get things going
        await device.power_on()     
        await device.start_advertising(auto_restart=True)

        while True:
            await asyncio.sleep(3.0)


# -----------------------------------------------------------------------------
logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper())
asyncio.run(main())

device1.json:

{
  "class_of_device": 2360322,
  "keystore": "JsonKeyStore",
  "advertising_interval": 100
}

On Android I can see this descriptor that I added, but on iOS it is not there. (In both cases, the CCCD descriptor is visible.)

Screenshot from nRF Connect (Android on the left, iOS on the right):
nRF_Connect_Descriptor

Also, when I use one of the pre-defined UUIDs for the descriptor (e.g. this one), it is again seen on Android but not on iOS.

One more thing, this should not be an issue with nRF Connect app, since I see this discrepancy between iOS and Android also using another (custom) app.

User @coalbr reported a similar issue on IOS-nRF-Connect repository.

So, my question is: does anyone have any idea why are these descriptors not showing on iOS and how to fix this? Any help would be very much appreciated!

LeConnectionOrientedChannel enters invalid state in race condition

LeConnectionOrientedChannel has a race condition:

  1. Call disconnect and enter state DISCONNECTING
  2. Receive on_disconnection_request and enter state DISCONNECTED
  3. Receive on_disconnection_response and see that state is not DISCONNECTING, log invalid state and return

Not sure what the best solution is here, but it seems valid (though unlikely) that one might receive a disconnection request before disconnect is done processing.

Notifier - usecase on windows

I am trying the sample notifier with the below command. My use case is to emulate a BT device that is discoverable and can be connected to from either an iOS or Android phone / app.

python ./run_notifier.py test.json usb:0

{
"name": "Test-sim",
"address": "F6:F7:F8:F9:FA:FB",
"keystore": "JsonKeyStore",
"irk": "43E96EC5C5DBD8D0F5204CFFDECE0096",
"advertising_interval": 100,
"advertising_data": "0201061106ba5689a6fabfa2bd01467d6e00fbabad08160a181604659b03"
}

This seem to go through fine. last few debug prints as below. But when I search for the device from my phone it doesnt find it. Trying to understand what I amy be doing wrong.

DEBUG:bumble.host:### HOST -> CONTROLLER: HCI_LE_SET_ADVERTISING_PARAMETERS_COMMAND:
advertising_interval_min: 2000
advertising_interval_max: 2000
advertising_type: ADV_IND
own_address_type: RANDOM
peer_address_type: PUBLIC_DEVICE_ADDRESS
peer_address: 00:00:00:00:00:00
advertising_channel_map: 7
advertising_filter_policy: 0
DEBUG:bumble.transport.usb:submit COMMAND
DEBUG:bumble.host:### CONTROLLER -> HOST: HCI_COMMAND_COMPLETE_EVENT:
num_hci_command_packets: 1
command_opcode: HCI_LE_SET_ADVERTISING_PARAMETERS_COMMAND
return_parameters: HCI_SUCCESS
DEBUG:bumble.host:### HOST -> CONTROLLER: HCI_LE_SET_ADVERTISING_ENABLE_COMMAND:
advertising_enable: 1
DEBUG:bumble.transport.usb:submit COMMAND
DEBUG:bumble.host:### CONTROLLER -> HOST: HCI_COMMAND_COMPLETE_EVENT:
num_hci_command_packets: 1
command_opcode: HCI_LE_SET_ADVERTISING_ENABLE_COMMAND
return_parameters: HCI_SUCCESS

Unable to see bumble servers on raspberry pi

I want to connect the virtual heart rate server example to my raspberry pi running Raspberry Pi OS as a virtual device.
So, the virtual device running on the same machine with a real (or virtual) adapter, that can see this virtual device.
My goal is to be able to see the the virtual device from the built-in bluetooth controller, so I can use bumble to emulate a bluetooth LE heart rate monitor.

I've tried to start with run_controller.py from the examples by following the README.md instructions. But I ran into some problems.

I see an unknown HCI command error in btmon when trying to run run_controller.py in the examples folder. I have tried both vhci and pty transport options, but see the same error from btmon and am unable to bring up the virtual hci device.

Of the three terminals I have open on the pi to run this (excluding the sudo btattach -P h4 -B hci_pty command):

btmon terminal

 MGMT Open: btmon (privileged) version 1.18                  {0x0002} 0.748678
= New Index: 00:00:00:00:00:00 (Primary,UART,hci1)             [hci1] 24.018439
= Open Index: 00:00:00:00:00:00                                [hci1] 24.022026
< HCI Command: Reset (0x03|0x0003) plen 0                   #1 [hci1] 24.023114
> HCI Event: Command Complete (0x0e) plen 4                 #2 [hci1] 24.027083
      Reset (0x03|0x0003) ncmd 1
        Status: Success (0x00)
< HCI Command: Read Local Supported.. (0x04|0x0003) plen 0  #3 [hci1] 24.027215
> HCI Event: Command Complete (0x0e) plen 12                #4 [hci1] 24.037368
      Read Local Supported Features (0x04|0x0003) ncmd 1
        Status: Success (0x00)
        Features: 0x00 0x00 0x00 0x00 0x60 0x00 0x00 0x00
          BR/EDR Not Supported
          LE Supported (Controller)
< HCI Command: Read Local Version I.. (0x04|0x0001) plen 0  #5 [hci1] 24.041859
> HCI Event: Command Complete (0x0e) plen 12                #6 [hci1] 24.049616
      Read Local Version Information (0x04|0x0001) ncmd 1
        Status: Success (0x00)
        HCI version: Bluetooth 5.0 (0x09) - Revision 0 (0x0000)
        LMP version: Bluetooth 5.0 (0x09) - Subversion 0 (0x0000)
        Manufacturer: internal use (65535)
< HCI Command: Read BD ADDR (0x04|0x0009) plen 0            #7 [hci1] 24.050056
> HCI Event: Command Complete (0x0e) plen 10                #8 [hci1] 24.056559
      Read BD ADDR (0x04|0x0009) ncmd 1
        Status: Success (0x00)
        Address: 00:00:00:00:00:00 (OUI 00-00-00)
< HCI Command: LE Read Buffer Size (0x08|0x0002) plen 0     #9 [hci1] 24.056802
> HCI Event: Command Complete (0x0e) plen 7                #10 [hci1] 24.065323
      LE Read Buffer Size (0x08|0x0002) ncmd 1
        Status: Success (0x00)
        Data packet length: 27
        Num data packets: 64
< HCI Command: LE Read Local Suppo.. (0x08|0x0003) plen 0  #11 [hci1] 24.065413
> HCI Event: Command Complete (0x0e) plen 12               #12 [hci1] 24.072635
      LE Read Local Supported Features (0x08|0x0003) ncmd 1
        Status: Success (0x00)
        Features: 0xff 0x49 0x01 0x00 0x00 0x00 0x00 0x00
          LE Encryption
          Connection Parameter Request Procedure
          Extended Reject Indication
          Slave-initiated Features Exchange
          LE Ping
          LE Data Packet Length Extension
          LL Privacy
          Extended Scanner Filter Policies
          LE 2M PHY
          LE Coded PHY
          Channel Selection Algorithm #2
          Minimum Number of Used Channels Procedure
< HCI Command: LE Read Supported S.. (0x08|0x001c) plen 0  #13 [hci1] 24.072747
> HCI Event: Command Complete (0x0e) plen 12               #14 [hci1] 24.075034
      LE Read Supported States (0x08|0x001c) ncmd 1
        Status: Success (0x00)
        States: 0x000003ffff3fffff
          Non-connectable Advertising State
          Scannable Advertising State
          Connectable Advertising State
          High Duty Cycle Directed Advertising State
          Passive Scanning State
          Active Scanning State
          Initiating State
            and Connection State (Master Role)
          Connection State (Slave Role)
          Non-connectable Advertising State
            and Passive Scanning State
          Scannable Advertising State
            and Passive Scanning State
          Connectable Advertising State
            and Passive Scanning State
          High Duty Cycle Directed Advertising State
            and Passive Scanning State
          Non-connectable Advertising State
            and Active Scanning State
          Scannable Advertising State
            and Active Scanning State
          Connectable Advertising State
            and Active Scanning State
          High Duty Cycle Directed Advertising State
            and Active Scanning State
          Non-connectable Advertising State
            and Initiating State
          Scannable Advertising State
            and Initiating State
          Non-connectable Advertising State
            and Connection State (Master Role)
          Scannable Advertising State
            and Connection State (Master Role)
          Non-connectable Advertising State
            and Connection State (Slave Role)
          Scannable Advertising State
            and Connection State (Slave Role)
          Passive Scanning State
            and Connection State (Master Role)
          Active Scanning State
            and Connection State (Master Role)
          Passive Scanning State
            and Connection State (Slave Role)
          Active Scanning State
            and Connection State (Slave Role)
          Initiating State
            and Connection State (Master Role)
            and Master Role & Master Role
          Low Duty Cycle Directed Advertising State
          Low Duty Cycle Directed Advertising State
            and Passive Scanning State
          Low Duty Cycle Directed Advertising State
            and Active Scanning State
          Connectable Advertising State
            and Initiating State
            and Master Role & Slave Role
          High Duty Cycle Directed Advertising State
            and Initiating State
            and Master Role & Slave Role
          Low Duty Cycle Directed Advertising State
            and Initiating State
            and Master Role & Slave Role
          Connectable Advertising State
            and Connection State (Master Role)
            and Master Role & Slave Role
          High Duty Cycle Directed Advertising State
            and Connection State (Master Role)
            and Master Role & Slave Role
          Low Duty Cycle Directed Advertising State
            and Connection State (Master Role)
            and Master Role & Slave Role
          Connectable Advertising State
            and Connection State (Slave Role)
            and Master Role & Slave Role
          High Duty Cycle Directed Advertising State
            and Connection State (Slave Role)
            and Slave Role & Slave Role
          Low Duty Cycle Directed Advertising State
            and Connection State (Slave Role)
            and Slave Role & Slave Role
          Initiating State
            and Connection State (Slave Role)
            and Master Role & Slave Role
< HCI Command: Read Local Supporte.. (0x04|0x0002) plen 0  #15 [hci1] 24.075101
> HCI Event: Command Complete (0x0e) plen 68               #16 [hci1] 24.078791
      Read Local Supported Commands (0x04|0x0002) ncmd 1
        Status: Success (0x00)
        Commands: 66 entries
          Disconnect (Octet 0 - Bit 5)
          Read Remote Version Information (Octet 2 - Bit 7)
          Set Event Mask (Octet 5 - Bit 6)
          Reset (Octet 5 - Bit 7)
          Read Transmit Power Level (Octet 10 - Bit 2)
          Set Controller To Host Flow Control (Octet 10 - Bit 5)
          Host Buffer Size (Octet 10 - Bit 6)
          Host Number of Completed Packets (Octet 10 - Bit 7)
          Read Local Version Information (Octet 14 - Bit 3)
          Read Local Supported Features (Octet 14 - Bit 5)
          Read BD ADDR (Octet 15 - Bit 1)
          Read RSSI (Octet 15 - Bit 5)
          Set Event Mask Page 2 (Octet 22 - Bit 2)
          LE Set Event Mask (Octet 25 - Bit 0)
          LE Read Buffer Size (Octet 25 - Bit 1)
          LE Read Local Supported Features (Octet 25 - Bit 2)
          LE Set Random Address (Octet 25 - Bit 4)
          LE Set Advertising Parameters (Octet 25 - Bit 5)
          LE Read Advertising Channel TX Power (Octet 25 - Bit 6)
          LE Set Advertising Data (Octet 25 - Bit 7)
          LE Set Scan Response Data (Octet 26 - Bit 0)
          LE Set Advertise Enable (Octet 26 - Bit 1)
          LE Set Scan Parameters (Octet 26 - Bit 2)
          LE Set Scan Enable (Octet 26 - Bit 3)
          LE Create Connection (Octet 26 - Bit 4)
          LE Create Connection Cancel (Octet 26 - Bit 5)
          LE Read White List Size (Octet 26 - Bit 6)
          LE Clear White List (Octet 26 - Bit 7)
          LE Add Device To White List (Octet 27 - Bit 0)
          LE Remove Device From White List (Octet 27 - Bit 1)
          LE Connection Update (Octet 27 - Bit 2)
          LE Set Host Channel Classification (Octet 27 - Bit 3)
          LE Read Channel Map (Octet 27 - Bit 4)
          LE Read Remote Used Features (Octet 27 - Bit 5)
          LE Encrypt (Octet 27 - Bit 6)
          LE Rand (Octet 27 - Bit 7)
          LE Start Encryption (Octet 28 - Bit 0)
          LE Long Term Key Request Reply (Octet 28 - Bit 1)
          LE Long Term Key Request Neg Reply (Octet 28 - Bit 2)
          LE Read Supported States (Octet 28 - Bit 3)
          LE Receiver Test (Octet 28 - Bit 4)
          LE Transmitter Test (Octet 28 - Bit 5)
          LE Test End (Octet 28 - Bit 6)
          Read Authenticated Payload Timeout (Octet 32 - Bit 4)
          Write Authenticated Payload Timeout (Octet 32 - Bit 5)
          LE Remote Connection Parameter Request Reply (Octet 33 - Bit 4)
          LE Remote Connection Parameter Request Negative Reply (Octet 33 - Bit 5)
          LE Set Data Length (Octet 33 - Bit 6)
          LE Read Suggested Default Data Length (Octet 33 - Bit 7)
          LE Write Suggested Default Data Length (Octet 34 - Bit 0)
          LE Add Device To Resolving List (Octet 34 - Bit 3)
          LE Remove Device From Resolving List (Octet 34 - Bit 4)
          LE Clear Resolving List (Octet 34 - Bit 5)
          LE Read Resolving List Size (Octet 34 - Bit 6)
          LE Read Peer Resolvable Address (Octet 34 - Bit 7)
          LE Read Local Resolvable Address (Octet 35 - Bit 0)
          LE Set Address Resolution Enable (Octet 35 - Bit 1)
          LE Set Resolvable Private Address Timeout (Octet 35 - Bit 2)
          LE Read Maximum Data Length (Octet 35 - Bit 3)
          LE Read PHY (Octet 35 - Bit 4)
          LE Set Default PHY (Octet 35 - Bit 5)
          LE Set PHY (Octet 35 - Bit 6)
          LE Enhanced Receiver Test (Octet 35 - Bit 7)
          LE Enhanced Transmitter Test (Octet 36 - Bit 0)
          LE Read Transmit Power (Octet 38 - Bit 7)
          LE Set Privacy Mode (Octet 39 - Bit 2)
< HCI Command: Set Event Mask (0x03|0x0001) plen 8         #17 [hci1] 24.078937
        Mask: 0x200080000204e890
          Disconnection Complete
          Encryption Change
          Read Remote Version Information Complete
          Command Complete
          Command Status
          Hardware Error
          Number of Completed Packets
          Data Buffer Overflow
          Encryption Key Refresh Complete
          LE Meta
> HCI Event: Command Complete (0x0e) plen 4                #18 [hci1] 24.084287
      Set Event Mask (0x03|0x0001) ncmd 1
        Status: Success (0x00)
< HCI Command: LE Set Event Mask (0x08|0x0001) plen 8      #19 [hci1] 24.084351
        Mask: 0x0000000000080e7f
          LE Connection Complete
          LE Advertising Report
          LE Connection Update Complete
          LE Read Remote Used Features Complete
          LE Long Term Key Request
          LE Remote Connection Parameter Request
          LE Data Length Change
          LE Enhanced Connection Complete
          LE Direct Advertising Report
          LE PHY Update Complete
          LE Channel Selection Algorithm
> HCI Event: Command Complete (0x0e) plen 4                #20 [hci1] 24.088763
      LE Set Event Mask (0x08|0x0001) ncmd 1
        Status: Success (0x00)
< HCI Command: LE Read Advertising.. (0x08|0x0007) plen 0  #21 [hci1] 24.088827
> HCI Event: Command Complete (0x0e) plen 4                #22 [hci1] 24.093838
      LE Read Advertising Channel TX Power (0x08|0x0007) ncmd 1
        Status: Unknown HCI Command (0x01)
= Close Index: 00:00:00:00:00:00                               [hci1] 24.093945

run_controller.py terminal

python run_controller.py 00:AA:01:01:00:24 device1.json pty:hci_pty
DEBUG:asyncio:Using selector: EpollSelector
>>> connecting to HCI...
DEBUG:bumble.transport.pty:pty open at /dev/pts/5
DEBUG:bumble.transport.pty:linked pty at hci_pty
>>> connected
DEBUG:bumble.link:new controller: <bumble.controller.Controller object at 0x75b768f8>
DEBUG:bumble.controller:new random address: 00:AA:01:01:00:24
DEBUG:bumble.link:new controller: <bumble.controller.Controller object at 0x75b76928>
DEBUG:bumble.keys:JSON keystore: /home/pi/.local/share/Bumble/Pairing/f0-f1-f2-f3-f4-f5.json
Service(handle=0x0001, end=0x0005, uuid=UUID-16:1800 (Generic Access))
CharacteristicDeclaration(handle=0x0002, value_handle=0x0003, uuid=UUID-16:2A00 (Device Name), properties=READ)
Characteristic(handle=0x0003, end=0x0003, uuid=UUID-16:2A00 (Device Name), properties=READ)
CharacteristicDeclaration(handle=0x0004, value_handle=0x0005, uuid=UUID-16:2A01 (Appearance), properties=READ)
Characteristic(handle=0x0005, end=0x0005, uuid=UUID-16:2A01 (Appearance), properties=READ)
Service(handle=0x0006, end=0x0009, uuid=UUID-16:180A (Device Information))
CharacteristicDeclaration(handle=0x0007, value_handle=0x0008, uuid=UUID-16:2A29 (Manufacturer Name String), properties=READ)
Characteristic(handle=0x0008, end=0x0009, uuid=UUID-16:2A29 (Manufacturer Name String), properties=READ)
Descriptor(handle=0x0009, type=UUID-16:2901 (Characteristic User Description), value=4d79204465736372697074696f6e)
DEBUG:bumble.host:### HOST -> CONTROLLER: HCI_RESET_COMMAND
DEBUG:bumble.controller:<<< [C2] HOST -> CONTROLLER: HCI_RESET_COMMAND
DEBUG:bumble.controller:>>> [C2] CONTROLLER -> HOST: HCI_COMMAND_COMPLETE_EVENT:
  num_hci_command_packets: 1
  command_opcode:          HCI_RESET_COMMAND
  return_parameters:       HCI_SUCCESS
DEBUG:bumble.host:### CONTROLLER -> HOST: HCI_COMMAND_COMPLETE_EVENT:
  num_hci_command_packets: 1
  command_opcode:          HCI_RESET_COMMAND
  return_parameters:       HCI_SUCCESS
DEBUG:bumble.host:### HOST -> CONTROLLER: HCI_READ_LOCAL_SUPPORTED_COMMANDS_COMMAND
DEBUG:bumble.controller:<<< [C2] HOST -> CONTROLLER: HCI_READ_LOCAL_SUPPORTED_COMMANDS_COMMAND
DEBUG:bumble.controller:>>> [C2] CONTROLLER -> HOST: HCI_COMMAND_COMPLETE_EVENT:
  num_hci_command_packets: 1
  command_opcode:          HCI_READ_LOCAL_SUPPORTED_COMMANDS_COMMAND
  return_parameters:       002000800000c000000000e40000002822000000000000040000f7ffff7f00000030f0f9ff01008004000000000000000000000000000000000000000000000000
DEBUG:bumble.host:### CONTROLLER -> HOST: HCI_COMMAND_COMPLETE_EVENT:
  num_hci_command_packets: 1
  command_opcode:          HCI_READ_LOCAL_SUPPORTED_COMMANDS_COMMAND
  return_parameters:       
    status:             0
    supported_commands: 2000800000c000000000e40000002822000000000000040000f7ffff7f00000030f0f9ff01008004000000000000000000000000000000000000000000000000
DEBUG:bumble.host:### HOST -> CONTROLLER: HCI_LE_READ_LOCAL_SUPPORTED_FEATURES_COMMAND
DEBUG:bumble.controller:<<< [C2] HOST -> CONTROLLER: HCI_LE_READ_LOCAL_SUPPORTED_FEATURES_COMMAND
DEBUG:bumble.controller:>>> [C2] CONTROLLER -> HOST: HCI_COMMAND_COMPLETE_EVENT:
  num_hci_command_packets: 1
  command_opcode:          HCI_LE_READ_LOCAL_SUPPORTED_FEATURES_COMMAND
  return_parameters:       00ff49010000000000
DEBUG:bumble.host:### CONTROLLER -> HOST: HCI_COMMAND_COMPLETE_EVENT:
  num_hci_command_packets: 1
  command_opcode:          HCI_LE_READ_LOCAL_SUPPORTED_FEATURES_COMMAND
  return_parameters:       
    status:      0
    le_features: ff49010000000000
DEBUG:bumble.host:### HOST -> CONTROLLER: HCI_READ_LOCAL_VERSION_INFORMATION_COMMAND
DEBUG:bumble.controller:<<< [C2] HOST -> CONTROLLER: HCI_READ_LOCAL_VERSION_INFORMATION_COMMAND
DEBUG:bumble.controller:>>> [C2] CONTROLLER -> HOST: HCI_COMMAND_COMPLETE_EVENT:
  num_hci_command_packets: 1
  command_opcode:          HCI_READ_LOCAL_VERSION_INFORMATION_COMMAND
  return_parameters:       0009000009ffff0000
DEBUG:bumble.host:### CONTROLLER -> HOST: HCI_COMMAND_COMPLETE_EVENT:
  num_hci_command_packets: 1
  command_opcode:          HCI_READ_LOCAL_VERSION_INFORMATION_COMMAND
  return_parameters:       
    status:             0
    hci_version:        9
    hci_subversion:     0
    lmp_version:        9
    company_identifier: 65535
    lmp_subversion:     0
DEBUG:bumble.host:### HOST -> CONTROLLER: HCI_SET_EVENT_MASK_COMMAND:
  event_mask: ffffffffffffff3f
DEBUG:bumble.controller:<<< [C2] HOST -> CONTROLLER: HCI_SET_EVENT_MASK_COMMAND:
  event_mask: ffffffffffffff3f
DEBUG:bumble.controller:>>> [C2] CONTROLLER -> HOST: HCI_COMMAND_COMPLETE_EVENT:
  num_hci_command_packets: 1
  command_opcode:          HCI_SET_EVENT_MASK_COMMAND
  return_parameters:       HCI_SUCCESS
DEBUG:bumble.host:### CONTROLLER -> HOST: HCI_COMMAND_COMPLETE_EVENT:
  num_hci_command_packets: 1
  command_opcode:          HCI_SET_EVENT_MASK_COMMAND
  return_parameters:       HCI_SUCCESS
DEBUG:bumble.host:### HOST -> CONTROLLER: HCI_LE_SET_EVENT_MASK_COMMAND:
  le_event_mask: fffff00000000000
DEBUG:bumble.controller:<<< [C2] HOST -> CONTROLLER: HCI_LE_SET_EVENT_MASK_COMMAND:
  le_event_mask: fffff00000000000
DEBUG:bumble.controller:>>> [C2] CONTROLLER -> HOST: HCI_COMMAND_COMPLETE_EVENT:
  num_hci_command_packets: 1
  command_opcode:          HCI_LE_SET_EVENT_MASK_COMMAND
  return_parameters:       HCI_SUCCESS
DEBUG:bumble.host:### CONTROLLER -> HOST: HCI_COMMAND_COMPLETE_EVENT:
  num_hci_command_packets: 1
  command_opcode:          HCI_LE_SET_EVENT_MASK_COMMAND
  return_parameters:       HCI_SUCCESS
DEBUG:bumble.host:### HOST -> CONTROLLER: HCI_LE_READ_BUFFER_SIZE_COMMAND
DEBUG:bumble.controller:<<< [C2] HOST -> CONTROLLER: HCI_LE_READ_BUFFER_SIZE_COMMAND
DEBUG:bumble.controller:>>> [C2] CONTROLLER -> HOST: HCI_COMMAND_COMPLETE_EVENT:
  num_hci_command_packets: 1
  command_opcode:          HCI_LE_READ_BUFFER_SIZE_COMMAND
  return_parameters:       001b0040
DEBUG:bumble.host:### CONTROLLER -> HOST: HCI_COMMAND_COMPLETE_EVENT:
  num_hci_command_packets: 1
  command_opcode:          HCI_LE_READ_BUFFER_SIZE_COMMAND
  return_parameters:       
    status:                           0
    hc_le_acl_data_packet_length:     27
    hc_total_num_le_acl_data_packets: 64
DEBUG:bumble.host:HCI LE ACL flow control: hc_le_acl_data_packet_length=27,hc_total_num_le_acl_data_packets=64
DEBUG:bumble.host:### HOST -> CONTROLLER: HCI_LE_READ_SUGGESTED_DEFAULT_DATA_LENGTH_COMMAND
DEBUG:bumble.controller:<<< [C2] HOST -> CONTROLLER: HCI_LE_READ_SUGGESTED_DEFAULT_DATA_LENGTH_COMMAND
DEBUG:bumble.controller:>>> [C2] CONTROLLER -> HOST: HCI_COMMAND_COMPLETE_EVENT:
  num_hci_command_packets: 1
  command_opcode:          HCI_LE_READ_SUGGESTED_DEFAULT_DATA_LENGTH_COMMAND
  return_parameters:       001b004801
DEBUG:bumble.host:### CONTROLLER -> HOST: HCI_COMMAND_COMPLETE_EVENT:
  num_hci_command_packets: 1
  command_opcode:          HCI_LE_READ_SUGGESTED_DEFAULT_DATA_LENGTH_COMMAND
  return_parameters:       
    status:                  0
    suggested_max_tx_octets: 27
    suggested_max_tx_time:   328
DEBUG:bumble.host:### HOST -> CONTROLLER: HCI_LE_WRITE_SUGGESTED_DEFAULT_DATA_LENGTH_COMMAND:
  suggested_max_tx_octets: 251
  suggested_max_tx_time:   2120
DEBUG:bumble.controller:<<< [C2] HOST -> CONTROLLER: HCI_LE_WRITE_SUGGESTED_DEFAULT_DATA_LENGTH_COMMAND:
  suggested_max_tx_octets: 251
  suggested_max_tx_time:   2120
DEBUG:bumble.controller:>>> [C2] CONTROLLER -> HOST: HCI_COMMAND_COMPLETE_EVENT:
  num_hci_command_packets: 1
  command_opcode:          HCI_LE_WRITE_SUGGESTED_DEFAULT_DATA_LENGTH_COMMAND
  return_parameters:       HCI_SUCCESS
DEBUG:bumble.host:### CONTROLLER -> HOST: HCI_COMMAND_COMPLETE_EVENT:
  num_hci_command_packets: 1
  command_opcode:          HCI_LE_WRITE_SUGGESTED_DEFAULT_DATA_LENGTH_COMMAND
  return_parameters:       HCI_SUCCESS
DEBUG:bumble.host:### HOST -> CONTROLLER: HCI_READ_BD_ADDR_COMMAND
DEBUG:bumble.controller:<<< [C2] HOST -> CONTROLLER: HCI_READ_BD_ADDR_COMMAND
DEBUG:bumble.controller:>>> [C2] CONTROLLER -> HOST: HCI_COMMAND_COMPLETE_EVENT:
  num_hci_command_packets: 1
  command_opcode:          HCI_READ_BD_ADDR_COMMAND
  return_parameters:       00000000000000
DEBUG:bumble.host:### CONTROLLER -> HOST: HCI_COMMAND_COMPLETE_EVENT:
  num_hci_command_packets: 1
  command_opcode:          HCI_READ_BD_ADDR_COMMAND
  return_parameters:       
    status:  0
    bd_addr: 00:00:00:00:00:00/P
DEBUG:bumble.device:BD_ADDR: 00:00:00:00:00:00/P
DEBUG:bumble.host:### HOST -> CONTROLLER: HCI_LE_SET_RANDOM_ADDRESS_COMMAND:
  random_address: F0:F1:F2:F3:F4:F5
DEBUG:bumble.controller:<<< [C2] HOST -> CONTROLLER: HCI_LE_SET_RANDOM_ADDRESS_COMMAND:
  random_address: F0:F1:F2:F3:F4:F5
DEBUG:bumble.controller:new random address: F0:F1:F2:F3:F4:F5
DEBUG:bumble.controller:>>> [C2] CONTROLLER -> HOST: HCI_COMMAND_COMPLETE_EVENT:
  num_hci_command_packets: 1
  command_opcode:          HCI_LE_SET_RANDOM_ADDRESS_COMMAND
  return_parameters:       HCI_SUCCESS
DEBUG:bumble.host:### CONTROLLER -> HOST: HCI_COMMAND_COMPLETE_EVENT:
  num_hci_command_packets: 1
  command_opcode:          HCI_LE_SET_RANDOM_ADDRESS_COMMAND
  return_parameters:       HCI_SUCCESS
DEBUG:bumble.host:### HOST -> CONTROLLER: HCI_LE_CLEAR_RESOLVING_LIST_COMMAND
DEBUG:bumble.controller:<<< [C2] HOST -> CONTROLLER: HCI_LE_CLEAR_RESOLVING_LIST_COMMAND
DEBUG:bumble.controller:>>> [C2] CONTROLLER -> HOST: HCI_COMMAND_COMPLETE_EVENT:
  num_hci_command_packets: 1
  command_opcode:          HCI_LE_CLEAR_RESOLVING_LIST_COMMAND
  return_parameters:       HCI_SUCCESS
DEBUG:bumble.host:### CONTROLLER -> HOST: HCI_COMMAND_COMPLETE_EVENT:
  num_hci_command_packets: 1
  command_opcode:          HCI_LE_CLEAR_RESOLVING_LIST_COMMAND
  return_parameters:       HCI_SUCCESS
DEBUG:bumble.host:### HOST -> CONTROLLER: HCI_LE_SET_ADVERTISING_DATA_COMMAND:
  advertising_data: 070942756d626c65
DEBUG:bumble.controller:<<< [C2] HOST -> CONTROLLER: HCI_LE_SET_ADVERTISING_DATA_COMMAND:
  advertising_data: 070942756d626c65
DEBUG:bumble.controller:>>> [C2] CONTROLLER -> HOST: HCI_COMMAND_COMPLETE_EVENT:
  num_hci_command_packets: 1
  command_opcode:          HCI_LE_SET_ADVERTISING_DATA_COMMAND
  return_parameters:       HCI_SUCCESS
DEBUG:bumble.host:### CONTROLLER -> HOST: HCI_COMMAND_COMPLETE_EVENT:
  num_hci_command_packets: 1
  command_opcode:          HCI_LE_SET_ADVERTISING_DATA_COMMAND
  return_parameters:       HCI_SUCCESS
DEBUG:bumble.host:### HOST -> CONTROLLER: HCI_LE_SET_SCAN_RESPONSE_DATA_COMMAND:
  scan_response_data: 
DEBUG:bumble.controller:<<< [C2] HOST -> CONTROLLER: HCI_LE_SET_SCAN_RESPONSE_DATA_COMMAND:
  scan_response_data: 
DEBUG:bumble.controller:>>> [C2] CONTROLLER -> HOST: HCI_COMMAND_COMPLETE_EVENT:
  num_hci_command_packets: 1
  command_opcode:          HCI_LE_SET_SCAN_RESPONSE_DATA_COMMAND
  return_parameters:       HCI_SUCCESS
DEBUG:bumble.host:### CONTROLLER -> HOST: HCI_COMMAND_COMPLETE_EVENT:
  num_hci_command_packets: 1
  command_opcode:          HCI_LE_SET_SCAN_RESPONSE_DATA_COMMAND
  return_parameters:       HCI_SUCCESS
DEBUG:bumble.host:### HOST -> CONTROLLER: HCI_LE_SET_ADVERTISING_PARAMETERS_COMMAND:
  advertising_interval_min:  2000
  advertising_interval_max:  2000
  advertising_type:          0
  own_address_type:          1
  peer_address_type:         0
  peer_address:              00:00:00:00:00:00
  advertising_channel_map:   7
  advertising_filter_policy: 0
DEBUG:bumble.controller:<<< [C2] HOST -> CONTROLLER: HCI_LE_SET_ADVERTISING_PARAMETERS_COMMAND:
  advertising_interval_min:  2000
  advertising_interval_max:  2000
  advertising_type:          0
  own_address_type:          1
  peer_address_type:         0
  peer_address:              00:00:00:00:00:00/P
  advertising_channel_map:   7
  advertising_filter_policy: 0
DEBUG:bumble.controller:>>> [C2] CONTROLLER -> HOST: HCI_COMMAND_COMPLETE_EVENT:
  num_hci_command_packets: 1
  command_opcode:          HCI_LE_SET_ADVERTISING_PARAMETERS_COMMAND
  return_parameters:       HCI_SUCCESS
DEBUG:bumble.host:### CONTROLLER -> HOST: HCI_COMMAND_COMPLETE_EVENT:
  num_hci_command_packets: 1
  command_opcode:          HCI_LE_SET_ADVERTISING_PARAMETERS_COMMAND
  return_parameters:       HCI_SUCCESS
DEBUG:bumble.host:### HOST -> CONTROLLER: HCI_LE_SET_ADVERTISING_ENABLE_COMMAND:
  advertising_enable: 1
DEBUG:bumble.controller:<<< [C2] HOST -> CONTROLLER: HCI_LE_SET_ADVERTISING_ENABLE_COMMAND:
  advertising_enable: 1
DEBUG:bumble.controller:>>> [C2] CONTROLLER -> HOST: HCI_COMMAND_COMPLETE_EVENT:
  num_hci_command_packets: 1
  command_opcode:          HCI_LE_SET_ADVERTISING_ENABLE_COMMAND
  return_parameters:       HCI_SUCCESS
DEBUG:bumble.host:### CONTROLLER -> HOST: HCI_COMMAND_COMPLETE_EVENT:
  num_hci_command_packets: 1
  command_opcode:          HCI_LE_SET_ADVERTISING_ENABLE_COMMAND
  return_parameters:       HCI_SUCCESS
DEBUG:bumble.host:### HOST -> CONTROLLER: HCI_LE_SET_SCAN_PARAMETERS_COMMAND:
  le_scan_type:           1
  le_scan_interval:       96
  le_scan_window:         96
  own_address_type:       1
  scanning_filter_policy: 0
DEBUG:bumble.controller:<<< [C2] HOST -> CONTROLLER: HCI_LE_SET_SCAN_PARAMETERS_COMMAND:
  le_scan_type:           1
  le_scan_interval:       96
  le_scan_window:         96
  own_address_type:       1
  scanning_filter_policy: 0
DEBUG:bumble.controller:>>> [C2] CONTROLLER -> HOST: HCI_COMMAND_COMPLETE_EVENT:
  num_hci_command_packets: 1
  command_opcode:          HCI_LE_SET_SCAN_PARAMETERS_COMMAND
  return_parameters:       HCI_SUCCESS
DEBUG:bumble.host:### CONTROLLER -> HOST: HCI_COMMAND_COMPLETE_EVENT:
  num_hci_command_packets: 1
  command_opcode:          HCI_LE_SET_SCAN_PARAMETERS_COMMAND
  return_parameters:       HCI_SUCCESS
DEBUG:bumble.host:### HOST -> CONTROLLER: HCI_LE_SET_SCAN_ENABLE_COMMAND:
  le_scan_enable:    1
  filter_duplicates: 0
DEBUG:bumble.controller:<<< [C2] HOST -> CONTROLLER: HCI_LE_SET_SCAN_ENABLE_COMMAND:
  le_scan_enable:    1
  filter_duplicates: 0
DEBUG:bumble.controller:>>> [C2] CONTROLLER -> HOST: HCI_COMMAND_COMPLETE_EVENT:
  num_hci_command_packets: 1
  command_opcode:          HCI_LE_SET_SCAN_ENABLE_COMMAND
  return_parameters:       HCI_SUCCESS
DEBUG:bumble.host:### CONTROLLER -> HOST: HCI_COMMAND_COMPLETE_EVENT:
  num_hci_command_packets: 1
  command_opcode:          HCI_LE_SET_SCAN_ENABLE_COMMAND
  return_parameters:       HCI_SUCCESS
DEBUG:bumble.controller:<<< [C1] HOST -> CONTROLLER: HCI_RESET_COMMAND
DEBUG:bumble.controller:>>> [C1] CONTROLLER -> HOST: HCI_COMMAND_COMPLETE_EVENT:
  num_hci_command_packets: 1
  command_opcode:          HCI_RESET_COMMAND
  return_parameters:       HCI_SUCCESS
DEBUG:bumble.controller:<<< [C1] HOST -> CONTROLLER: HCI_READ_LOCAL_SUPPORTED_FEATURES_COMMAND
DEBUG:bumble.controller:>>> [C1] CONTROLLER -> HOST: HCI_COMMAND_COMPLETE_EVENT:
  num_hci_command_packets: 1
  command_opcode:          HCI_READ_LOCAL_SUPPORTED_FEATURES_COMMAND
  return_parameters:       000000000060000000
DEBUG:bumble.controller:<<< [C1] HOST -> CONTROLLER: HCI_READ_LOCAL_VERSION_INFORMATION_COMMAND
DEBUG:bumble.controller:>>> [C1] CONTROLLER -> HOST: HCI_COMMAND_COMPLETE_EVENT:
  num_hci_command_packets: 1
  command_opcode:          HCI_READ_LOCAL_VERSION_INFORMATION_COMMAND
  return_parameters:       0009000009ffff0000
DEBUG:bumble.controller:<<< [C1] HOST -> CONTROLLER: HCI_READ_BD_ADDR_COMMAND
DEBUG:bumble.controller:>>> [C1] CONTROLLER -> HOST: HCI_COMMAND_COMPLETE_EVENT:
  num_hci_command_packets: 1
  command_opcode:          HCI_READ_BD_ADDR_COMMAND
  return_parameters:       00000000000000
DEBUG:bumble.controller:<<< [C1] HOST -> CONTROLLER: HCI_LE_READ_BUFFER_SIZE_COMMAND
DEBUG:bumble.controller:>>> [C1] CONTROLLER -> HOST: HCI_COMMAND_COMPLETE_EVENT:
  num_hci_command_packets: 1
  command_opcode:          HCI_LE_READ_BUFFER_SIZE_COMMAND
  return_parameters:       001b0040
DEBUG:bumble.controller:<<< [C1] HOST -> CONTROLLER: HCI_LE_READ_LOCAL_SUPPORTED_FEATURES_COMMAND
DEBUG:bumble.controller:>>> [C1] CONTROLLER -> HOST: HCI_COMMAND_COMPLETE_EVENT:
  num_hci_command_packets: 1
  command_opcode:          HCI_LE_READ_LOCAL_SUPPORTED_FEATURES_COMMAND
  return_parameters:       00ff49010000000000
DEBUG:bumble.controller:<<< [C1] HOST -> CONTROLLER: HCI_LE_READ_SUPPORTED_STATES_COMMAND
DEBUG:bumble.controller:>>> [C1] CONTROLLER -> HOST: HCI_COMMAND_COMPLETE_EVENT:
  num_hci_command_packets: 1
  command_opcode:          HCI_LE_READ_SUPPORTED_STATES_COMMAND
  return_parameters:       00ffff3fffff030000
DEBUG:bumble.controller:<<< [C1] HOST -> CONTROLLER: HCI_READ_LOCAL_SUPPORTED_COMMANDS_COMMAND
DEBUG:bumble.controller:>>> [C1] CONTROLLER -> HOST: HCI_COMMAND_COMPLETE_EVENT:
  num_hci_command_packets: 1
  command_opcode:          HCI_READ_LOCAL_SUPPORTED_COMMANDS_COMMAND
  return_parameters:       002000800000c000000000e40000002822000000000000040000f7ffff7f00000030f0f9ff01008004000000000000000000000000000000000000000000000000
DEBUG:bumble.controller:<<< [C1] HOST -> CONTROLLER: HCI_SET_EVENT_MASK_COMMAND:
  event_mask: 90e8040200800020
DEBUG:bumble.controller:>>> [C1] CONTROLLER -> HOST: HCI_COMMAND_COMPLETE_EVENT:
  num_hci_command_packets: 1
  command_opcode:          HCI_SET_EVENT_MASK_COMMAND
  return_parameters:       HCI_SUCCESS
DEBUG:bumble.controller:<<< [C1] HOST -> CONTROLLER: HCI_LE_SET_EVENT_MASK_COMMAND:
  le_event_mask: 7f0e080000000000
DEBUG:bumble.controller:>>> [C1] CONTROLLER -> HOST: HCI_COMMAND_COMPLETE_EVENT:
  num_hci_command_packets: 1
  command_opcode:          HCI_LE_SET_EVENT_MASK_COMMAND
  return_parameters:       HCI_SUCCESS
DEBUG:bumble.controller:<<< [C1] HOST -> CONTROLLER: HCI_LE_READ_ADVERTISING_PHYSICAL_CHANNEL_TX_POWER_COMMAND
WARNING:bumble.controller:--- Unsupported command HCI_LE_READ_ADVERTISING_PHYSICAL_CHANNEL_TX_POWER_COMMAND
DEBUG:bumble.controller:>>> [C1] CONTROLLER -> HOST: HCI_COMMAND_COMPLETE_EVENT:
  num_hci_command_packets: 1
  command_opcode:          HCI_LE_READ_ADVERTISING_PHYSICAL_CHANNEL_TX_POWER_COMMAND
  return_parameters:       HCI_UNKNOWN_HCI_COMMAND_ERROR

hcitool terminal

hciconfig
hci1:	Type: Primary  Bus: UART
	BD Address: 00:00:00:00:00:00  ACL MTU: 27:64  SCO MTU: 0:0
	DOWN 
	RX bytes:182 acl:0 sco:0 events:11 errors:0
	TX bytes:60 acl:0 sco:0 commands:11 errors:0

hci0:	Type: Primary  Bus: UART
	BD Address: B8:27:EB:7B:B5:EE  ACL MTU: 1021:8  SCO MTU: 64:1
	UP RUNNING PSCAN 
	RX bytes:1568388 acl:0 sco:0 events:56495 errors:0
	TX bytes:98471 acl:0 sco:0 commands:11391 errors:0

What is the best way to move forward?

Add BR/EDR Scanning Capability to the Bumble

Currently the Bumble controller does not support Bluetooth "classic" (BR/EDR) scanning. It only has BLE scanning support.

This issue is to request that Bumble be updated to support Bluetooth classic scanning.

I'm still investigating the full impact of this, but at a minimum it will require adding the BR/EDR scanning HCI commands to the controller.

Regression: unable to import Device due to type annotation error for Python 3.8

Commit 78534b6 introduced a regression. A TypeError exception is raised when importing Device. I'm using Python 3.8 from the environment.yaml Conda environment. Python 3.8 is specified in setup.cfg as a supported Python version.

eg.

Python 3.8.12 (default, Oct 12 2021, 13:49:34) 
[GCC 7.5.0] :: Anaconda, Inc. on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from bumble.device import Device
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/mogenson/Work/bumble/bumble/device.py", line 617, in <module>
    class Device(CompositeEventEmitter):
  File "/home/mogenson/Work/bumble/bumble/device.py", line 1600, in Device
    async def request_remote_name(self, remote: Connection | Address):
TypeError: unsupported operand type(s) for |: 'type' and 'type'

@uael FYI

GATT Properties test failures and mypy errors in Python 3.11

In Python 3.11, tests fail on cases:

tests/gatt_test.py::test_char_property_to_string FAILED                                   [ 60%]
tests/gatt_test.py::test_server_string FAILED                                             [ 75%]

and mypy generates errors:

bumble/gatt.py:323: error: Incompatible return value type (got "object", expected "Properties")  [return-value]

This is probably because change in Python 3.11

Changed in version 3.11: str() is now int.str() to better support the replacement of existing constants use-case. format() was already int.format() for that same reason.

Ideal way to terminate gracefully

With the scripts running , what would be a graceful way to terminate and exit ?
Here is what i am doing -

  1. Upon Keyboard Interrupt - disconnect device and then device.power_off().
    Any thing else I may be missing. Didnt find a suitable example for the exit sequence yet , but any refrence to read up will help.

BTW irrespective of what reason i give for the disconnect device , the disconnect reason is always 22 .

Tests failing locally

on commit ae0b739e4a7784e5f8f5592597e51330c03d0de0

(Bumble) ➜  bumble git:(main) invoke test          
============================= test session starts ==============================
platform linux -- Python 3.8.15, pytest-7.2.0, pluggy-1.0.0
rootdir: /home/alanrosenthal/code/fitbit/bumble/tests, configfile: pytest.ini
plugins: metadata-2.0.4, asyncio-0.20.2, html-3.2.0
asyncio: mode=auto
collected 395 items

tests/a2dp_test.py ..                                                    [  0%]
tests/avdtp_test.py ..                                                   [  1%]
tests/core_test.py .                                                     [  1%]
tests/device_test.py F                                                   [  1%]
tests/gatt_test.py ..........                                            [  4%]
tests/hci_test.py ................................                       [ 12%]
tests/import_test.py .F.                                                 [ 12%]
tests/l2cap_test.py ....                                                 [ 13%]
tests/rfcomm_test.py .                                                   [ 14%]
tests/sdp_test.py .                                                      [ 14%]
tests/self_test.py ..................................................... [ 27%]
........................................................................ [ 46%]
........................................................................ [ 64%]
........................................................................ [ 82%]
........................................................                 [ 96%]
tests/smp_test.py ...........                                            [ 99%]
tests/transport_test.py ..                                               [100%]

=================================== FAILURES ===================================
_________________________ test_device_connect_parallel _________________________

    @pytest.mark.asyncio
    async def test_device_connect_parallel():
        d0 = Device(host=Host(None, None))
        d1 = Device(host=Host(None, None))
        d2 = Device(host=Host(None, None))
    
        # enable classic
        d0.classic_enabled = True
        d1.classic_enabled = True
        d2.classic_enabled = True
    
        # set public addresses
        d0.public_address = Address('F0:F1:F2:F3:F4:F5', address_type=Address.PUBLIC_DEVICE_ADDRESS)
        d1.public_address = Address('F5:F4:F3:F2:F1:F0', address_type=Address.PUBLIC_DEVICE_ADDRESS)
        d2.public_address = Address('F5:F4:F3:F3:F4:F5', address_type=Address.PUBLIC_DEVICE_ADDRESS)
    
        def d0_flow():
            packet = HCI_Packet.from_bytes((yield))
            assert packet.name == 'HCI_CREATE_CONNECTION_COMMAND'
            assert packet.bd_addr == d1.public_address
    
            d0.host.on_hci_packet(HCI_Command_Status_Event(
                status                  = HCI_COMMAND_STATUS_PENDING,
                num_hci_command_packets = 1,
                command_opcode          = HCI_CREATE_CONNECTION_COMMAND
            ))
    
            d1.host.on_hci_packet(HCI_Connection_Request_Event(
               bd_addr         = d0.public_address,
               class_of_device = 0,
               link_type       = HCI_Connection_Complete_Event.ACL_LINK_TYPE
            ))
    
            packet = HCI_Packet.from_bytes((yield))
            assert packet.name == 'HCI_CREATE_CONNECTION_COMMAND'
            assert packet.bd_addr == d2.public_address
    
            d0.host.on_hci_packet(HCI_Command_Status_Event(
                status                  = HCI_COMMAND_STATUS_PENDING,
                num_hci_command_packets = 1,
                command_opcode          = HCI_CREATE_CONNECTION_COMMAND
            ))
    
            d2.host.on_hci_packet(HCI_Connection_Request_Event(
               bd_addr         = d0.public_address,
               class_of_device = 0,
               link_type       = HCI_Connection_Complete_Event.ACL_LINK_TYPE
            ))
    
            assert (yield) == None
    
        def d1_flow():
            packet = HCI_Packet.from_bytes((yield))
            assert packet.name == 'HCI_ACCEPT_CONNECTION_REQUEST_COMMAND'
    
            d1.host.on_hci_packet(HCI_Command_Complete_Event(
                num_hci_command_packets = 1,
                command_opcode          = HCI_ACCEPT_CONNECTION_REQUEST_COMMAND,
                return_parameters       = b"\x00"
            ))
    
            d1.host.on_hci_packet(HCI_Connection_Complete_Event(
                status             = HCI_SUCCESS,
                connection_handle  = 0x100,
                bd_addr            = d0.public_address,
                link_type          = HCI_Connection_Complete_Event.ACL_LINK_TYPE,
                encryption_enabled = True,
            ))
    
            d0.host.on_hci_packet(HCI_Connection_Complete_Event(
                status             = HCI_SUCCESS,
                connection_handle  = 0x100,
                bd_addr            = d1.public_address,
                link_type          = HCI_Connection_Complete_Event.ACL_LINK_TYPE,
                encryption_enabled = True,
            ))
    
            assert (yield) == None
    
        def d2_flow():
            packet = HCI_Packet.from_bytes((yield))
            assert packet.name == 'HCI_ACCEPT_CONNECTION_REQUEST_COMMAND'
    
            d2.host.on_hci_packet(HCI_Command_Complete_Event(
                num_hci_command_packets = 1,
                command_opcode          = HCI_ACCEPT_CONNECTION_REQUEST_COMMAND,
                return_parameters       = b"\x00"
            ))
    
            d2.host.on_hci_packet(HCI_Connection_Complete_Event(
                status             = HCI_SUCCESS,
                connection_handle  = 0x101,
                bd_addr            = d0.public_address,
                link_type          = HCI_Connection_Complete_Event.ACL_LINK_TYPE,
                encryption_enabled = True,
            ))
    
            d0.host.on_hci_packet(HCI_Connection_Complete_Event(
                status             = HCI_SUCCESS,
                connection_handle  = 0x101,
                bd_addr            = d2.public_address,
                link_type          = HCI_Connection_Complete_Event.ACL_LINK_TYPE,
                encryption_enabled = True,
            ))
    
            assert (yield) == None
    
        d0.host.set_packet_sink(Sink(d0_flow()))
        d1.host.set_packet_sink(Sink(d1_flow()))
        d2.host.set_packet_sink(Sink(d2_flow()))
    
>       [c01, c02, a10, a20, a01] = await asyncio.gather(*[
            asyncio.create_task(d0.connect(d1.public_address, transport=BT_BR_EDR_TRANSPORT)),
            asyncio.create_task(d0.connect(d2.public_address, transport=BT_BR_EDR_TRANSPORT)),
            asyncio.create_task(d1.accept(peer_address=d0.public_address)),
            asyncio.create_task(d2.accept()),
            asyncio.create_task(d0.accept(peer_address=d1.public_address)),
        ])

tests/device_test.py:161: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
bumble/device.py:1387: in accept
    if peer_address == Address.NIL:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <bumble.hci.Address object at 0x7f4fa68f2e80>
other = <bound method ? of <class 'bumble.hci.Address'>>

    def __eq__(self, other):
>       return self.address_bytes == other.address_bytes and self.is_public == other.is_public
E       AttributeError: 'property' object has no attribute 'address_bytes'

bumble/hci.py:1750: AttributeError
_______________________________ test_app_imports _______________________________

    def test_app_imports():
>       from bumble.apps.console import main
E       ModuleNotFoundError: No module named 'bumble.apps'

tests/import_test.py:67: ModuleNotFoundError
=============================== warnings summary ===============================
self_test.py::test_self_smp_reject
self_test.py::test_self_smp_reject
self_test.py::test_self_smp_wrong_pin
self_test.py::test_self_smp_wrong_pin
self_test.py::test_self_smp_wrong_pin
self_test.py::test_self_smp_wrong_pin
  /home/alanrosenthal/code/fitbit/bumble/bumble/smp.py:1113: DeprecationWarning: The 'warn' method is deprecated, use 'warning' instead
    logger.warn(f'pairing failure ({error_name(reason)})')

-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
=========================== short test summary info ============================
FAILED tests/device_test.py::test_device_connect_parallel - AttributeError: '...
FAILED tests/import_test.py::test_app_imports - ModuleNotFoundError: No modul...
================== 2 failed, 393 passed, 6 warnings in 22.39s ==================

`Device.send_command` procedure catch `TimeoutError`

In device.py#L785 the send_command procedure actually catch the TimeoutError which does not make it to the caller.
I was running some test and it break at device.py#L1247 with result set to None:

Traceback (most recent call last):
  File "./bumble/examples/run_classic_connect.py", line 48, in connect
    connection = await device.connect(target_address, transport=BT_BR_EDR_TRANSPORT)
  File "./bumble/bumble/device.py", line 1259, in connect
    if result.status != HCI_Command_Status_Event.PENDING:
AttributeError: 'NoneType' object has no attribute 'status'

I may be wrong here but I'm not sure this is the desired behavior.

A command timeout usually mean that controller may be dead and may need to be reboot.
I suggest adding a new CommandTimeoutError error type, which can be catches at user level to properly act against the error.

What do you think ?

Regression: cannot set Device.host - Host object has no attribute add_listener

Since commit ce9004f setting the host member of a Device raises an AttributeError exception from l2cap.py.

Eg.

python -m asyncio
asyncio REPL 3.8.12 (default, Oct 12 2021, 13:49:34) 
[GCC 7.5.0] on linux
Use "await" directly instead of "asyncio.run()".
Type "help", "copyright", "credits" or "license" for more information.
>>> import asyncio
>>> from bumble.device import Device
>>> from bumble.host import Host
>>> from bumble.transport import open_transport
>>> device = Device()
>>> device.transport = await open_transport("usb:0")
>>> device.host = Host(device.transport.source, device.transport.sink)
Traceback (most recent call last):
  File "/home/mogenson/miniconda3/envs/bumble/lib/python3.8/concurrent/futures/_base.py", line 444, in result
    return self.__get_result()
  File "/home/mogenson/miniconda3/envs/bumble/lib/python3.8/concurrent/futures/_base.py", line 389, in __get_result
    raise self._exception
  File "/home/mogenson/miniconda3/envs/bumble/lib/python3.8/asyncio/__main__.py", line 34, in callback
    coro = func()
  File "<console>", line 1, in <module>
  File "/home/mogenson/Work/bumble/bumble/device.py", line 773, in host
    self.l2cap_channel_manager.host = host
  File "/home/mogenson/Work/bumble/bumble/l2cap.py", line 1227, in host
    host.add_listener('disconnection', self.on_disconnection)
AttributeError: 'Host' object has no attribute 'add_listener'
>>> 

Remove dependency on GPL-3 library

One of the dependencies for Bumble is aioconsole. This is distributed under GPL v3, which is an extremely aggressive copy-left license. Depending upon your interpretation of GPL v3, this might not be compatible with the project's Apache 2.0 license.

The Bumble project may want to consider migrating away from this dependency in order to maintain the current project license.

According to PyPi:

The python console exposed by aioconsole is quite limited compared to modern consoles such as IPython or ptpython. Luckily, those projects gained greater asyncio support over the years. In particular, the following use cases overlap with aioconsole capabilities:

Embedding a ptpython console in an asyncio program

Using the await syntax in an IPython console

The other dependencies appear compatible with the Apache 2.0 license. (But, I am not a lawyer....)

Integrating Pairing : Identifying if a device already is bonded

I am currently Integrating the pairing functionality into my custom app.

Basically the flow would be when on_connect check if the address is in the keystore , if it is continue else pairing mode.
However I have not been able to get this check correctly done as the address of the connecting device - Android keeps changing .
I looked at the unbond.py app and it seems to remove all bonds and isnt specific to an address or device to unbond from .

Any pointers here will help .

At present I always need to unpair and repair for me to get my app to continue execution of the rest of the code.

The only time a check for if connection.is_encrypted went through was when i paired via Windows and it used the same address as previous connections.

Issue is seen on pair.py as well where when i restart the app after first pairing it doesnt progress until I unpair and repair on an Android phone.

Connection between android-netsim and Zephyr BT Stack work arounds

As a follow upt to the issues I posted

#215
#217

I have managed to connect the Zephyr Bluetooth stack to the Android emulator with the bumble hci-bridge 👍

I open this issue to discuss the workarounds to make it work.

After some trial and error I figured out that the following commands from host to controller (zephyr tcp-client to android-netsim) should be filtered and not sent to the android-netsim.

  • HCI_SET_CONTROLLER_TO_HOST_FLOW_CONTROL_COMMAND
  • HCI_LE_CONNECTION_UPDATE_COMMAND
  • HCI_LE_SET_PHY_COMMAND
  • HCI_LE_READ_REMOTE_FEATURES_COMMAND
  • HCI_HOST_NUMBER_OF_COMPLETED_PACKETS_COMMAND

An example of how to make it work

  1. Make sure your Zephyr OS is updated to at least this commit

    zephyrproject-rtos/zephyr@33c922a

  2. Compile the BLE Hearbeat sensor sample from zephyr located here .

    For any other zephyr project be sure to set these configurations as follows

    • CONFIG_BT_HCI_ACL_FLOW_CONTROL=n
    • CONFIG_BT_SMP_SC_PAIR_ONLY=n
  3. Run the android emulator.

    emulator -avd Pixel_7_API_34 -packet-streamer-endpoint default

    Note: Only tested with API 34, other APIs <34 gave me some problems

  4. Create the hci-bridge with bumble and the filters for hci commands I mentioned above

    bumble-hci-bridge tcp-server:_:9000 android-netsim 0x03:0x0031,0x08:0x013,0x08:0x032,0x08:0x016,0x03:0x035

    update: If in zephyr you set CONFIG_BT_HCI_ACL_FLOW_CONTROL=n you do not need to filter the hci commands. I.e., you can run bumble-hci-bridge tcp-server:_:9000 android-netsim

  5. Run the zephyr binary

    zephyr.exe --bt-dev=127.0.0.1:9000

    Note: This instruction assume that zephyr binary has access to localhost. In case Android emulator and bumble are installed in Windows and using WSL to compile and execute the zephyr binary, then you need to get the proxy IP for localhost by running cat /etc/resolv.conf and obtaining the nameserver ip address.

And here a small video of the Proof of concept. Note I have not tested every option from the BT Zephyr stack or the APIs so this is still experimental.

Upper left screen is the HCI traffice between zephyr bluetooth stack (host) and the android-netsim (Controller). Lower window is the zephyr binary running the peripheral_hr example in the Windows Subsystem for Linux. And on the right is the Android emulator running with Android API 34 and the NRF Connect App to test the GATT notification of the peripheral_hr sample.

clideo_editor_bc0051d87a63439b9a0a10a32635c453.mp4

[Question] How to get the HF audio?

Hi,

I want to use the solution to emulate a HFP headset. I am using a USB BT Adpater(TP-Link). I am able to simulate a set of AT commands like Answer, handup, or voice recognization,. But when I answer the call I do not get the call audio. How do I achieve call audio?

'run_hfp_gateway.py' has no Attribute List

When running this script run_hfp_gateway.py I manage to connect to the HFP but get as far as searching for the attribute list which is it tells me is none existent (incorrect). I've narrowed down the error to line 471 of sdp.py 'cls = SDP_PDU.sdp_pdu_classes.get(pdu_id)' as this comes back with an SDP error and I cannot step into it look further.

Attribute Error:
Exception has occurred: AttributeError
'SDP_ErrorResponse' object has no attribute 'attribute_lists'
File "/home/anyone/Downloads/test/bumble/examples/run_hfp_gateway.py", line 50, in list_rfcomm_channels
search_result = await sdp_client.search_attributes(
File "/home/anyone/Downloads/test/bumble/examples/run_hfp_gateway.py", line 133, in main
channels = await list_rfcomm_channels(device, connection)
File "/home/anyone/Downloads/test/bumble/examples/run_hfp_gateway.py", line 209, in
asyncio.run(main())

Console Output:
anyone@User1:~/Downloads/bumble$ sudo python3.9 examples/run_hfp_gateway.py
DEBUG:asyncio:Using selector: EpollSelector
<<< connecting to HCI...
DEBUG:bumble.transport.usb:USB Device: Bus 001 Device 003: ID 0a12:0001
DEBUG:bumble.transport.usb:selected endpoints: configuration=1, interface=0, setting=0, acl_in=0x82, acl_out=0x02, events_in=0x81,
DEBUG:bumble.transport.usb:current configuration = 1
DEBUG:bumble.transport.usb:starting USB event loop
<<< connected
DEBUG:bumble.keys:JSON keystore: /root/.local/share/Bumble/Pairing/00-00-00-00-00-00.json
DEBUG:bumble.host:### HOST -> CONTROLLER: HCI_RESET_COMMAND
DEBUG:bumble.transport.usb:submit COMMAND
DEBUG:bumble.host:reset not done, ignoring packet from controller
DEBUG:bumble.host:### CONTROLLER -> HOST: HCI_COMMAND_COMPLETE_EVENT:
num_hci_command_packets: 1
command_opcode: HCI_RESET_COMMAND
return_parameters: HCI_SUCCESS
DEBUG:bumble.host:### HOST -> CONTROLLER: HCI_READ_LOCAL_SUPPORTED_COMMANDS_COMMAND
DEBUG:bumble.transport.usb:submit COMMAND
DEBUG:bumble.host:### CONTROLLER -> HOST: HCI_COMMAND_COMPLETE_EVENT:
num_hci_command_packets: 1
command_opcode: HCI_READ_LOCAL_SUPPORTED_COMMANDS_COMMAND
return_parameters:
status: HCI_SUCCESS
supported_commands: ffffff03fefffffffffffffff30fe8fe3ff783ff1c00000061f7ffff7f0000000000000000000000000000000000000000000000000000000000000000000000
DEBUG:bumble.host:### HOST -> CONTROLLER: HCI_LE_READ_LOCAL_SUPPORTED_FEATURES_COMMAND
DEBUG:bumble.transport.usb:submit COMMAND
DEBUG:bumble.host:### CONTROLLER -> HOST: HCI_COMMAND_COMPLETE_EVENT:
num_hci_command_packets: 1
command_opcode: HCI_LE_READ_LOCAL_SUPPORTED_FEATURES_COMMAND
return_parameters:
status: HCI_SUCCESS
le_features: 0100000000000000
DEBUG:bumble.host:### HOST -> CONTROLLER: HCI_READ_LOCAL_VERSION_INFORMATION_COMMAND
DEBUG:bumble.transport.usb:submit COMMAND
DEBUG:bumble.host:### CONTROLLER -> HOST: HCI_COMMAND_COMPLETE_EVENT:
num_hci_command_packets: 1
command_opcode: HCI_READ_LOCAL_VERSION_INFORMATION_COMMAND
return_parameters:
status: HCI_SUCCESS
hci_version: 6
hci_subversion: 8891
lmp_version: 6
company_identifier: 10
lmp_subversion: 8891
DEBUG:bumble.host:### HOST -> CONTROLLER: HCI_SET_EVENT_MASK_COMMAND:
event_mask: ffffffffffffff3f
DEBUG:bumble.transport.usb:submit COMMAND
DEBUG:bumble.host:### CONTROLLER -> HOST: HCI_COMMAND_COMPLETE_EVENT:
num_hci_command_packets: 1
command_opcode: HCI_SET_EVENT_MASK_COMMAND
return_parameters: HCI_SUCCESS
DEBUG:bumble.host:### HOST -> CONTROLLER: HCI_LE_SET_EVENT_MASK_COMMAND:
le_event_mask: 1f00000000000000
DEBUG:bumble.transport.usb:submit COMMAND
DEBUG:bumble.host:### CONTROLLER -> HOST: HCI_COMMAND_COMPLETE_EVENT:
num_hci_command_packets: 1
command_opcode: HCI_LE_SET_EVENT_MASK_COMMAND
return_parameters: HCI_SUCCESS
DEBUG:bumble.host:### HOST -> CONTROLLER: HCI_READ_BUFFER_SIZE_COMMAND
DEBUG:bumble.transport.usb:submit COMMAND
DEBUG:bumble.host:### CONTROLLER -> HOST: HCI_COMMAND_COMPLETE_EVENT:
num_hci_command_packets: 1
command_opcode: HCI_READ_BUFFER_SIZE_COMMAND
return_parameters:
status: HCI_SUCCESS
hc_acl_data_packet_length: 310
hc_synchronous_data_packet_length: 64
hc_total_num_acl_data_packets: 10
hc_total_num_synchronous_data_packets: 8
DEBUG:bumble.host:### HOST -> CONTROLLER: HCI_LE_READ_BUFFER_SIZE_COMMAND
DEBUG:bumble.transport.usb:submit COMMAND
DEBUG:bumble.host:### CONTROLLER -> HOST: HCI_COMMAND_COMPLETE_EVENT:
num_hci_command_packets: 1
command_opcode: HCI_LE_READ_BUFFER_SIZE_COMMAND
return_parameters:
status: HCI_SUCCESS
hc_le_acl_data_packet_length: 0
hc_total_num_le_acl_data_packets: 0
DEBUG:bumble.host:HCI ACL flow control: hc_acl_data_packet_length=310,hc_total_num_acl_data_packets=10
DEBUG:bumble.host:HCI LE ACL flow control: hc_le_acl_data_packet_length=310,hc_total_num_le_acl_data_packets=10
DEBUG:bumble.host:### HOST -> CONTROLLER: HCI_READ_BD_ADDR_COMMAND
DEBUG:bumble.transport.usb:submit COMMAND
DEBUG:bumble.host:### CONTROLLER -> HOST: HCI_COMMAND_COMPLETE_EVENT:
num_hci_command_packets: 1
command_opcode: HCI_READ_BD_ADDR_COMMAND
return_parameters:
status: HCI_SUCCESS
bd_addr: C0:FB:F9:60:7D:EC/P
DEBUG:bumble.device:BD_ADDR: C0:FB:F9:60:7D:EC/P
DEBUG:bumble.host:### HOST -> CONTROLLER: HCI_WRITE_LE_HOST_SUPPORT_COMMAND:
le_supported_host: 1
simultaneous_le_host: 1
DEBUG:bumble.transport.usb:submit COMMAND
DEBUG:bumble.host:### CONTROLLER -> HOST: HCI_COMMAND_COMPLETE_EVENT:
num_hci_command_packets: 1
command_opcode: HCI_WRITE_LE_HOST_SUPPORT_COMMAND
return_parameters: HCI_SUCCESS
DEBUG:bumble.host:### HOST -> CONTROLLER: HCI_LE_SET_RANDOM_ADDRESS_COMMAND:
random_address: 00:00:00:00:00:00
DEBUG:bumble.transport.usb:submit COMMAND
DEBUG:bumble.host:### CONTROLLER -> HOST: HCI_COMMAND_COMPLETE_EVENT:
num_hci_command_packets: 1
command_opcode: HCI_LE_SET_RANDOM_ADDRESS_COMMAND
return_parameters: HCI_SUCCESS
DEBUG:bumble.host:### HOST -> CONTROLLER: HCI_WRITE_LOCAL_NAME_COMMAND:
local_name: 42756d626c652050686f6e65
DEBUG:bumble.transport.usb:submit COMMAND
DEBUG:bumble.host:### CONTROLLER -> HOST: HCI_COMMAND_COMPLETE_EVENT:
num_hci_command_packets: 1
command_opcode: HCI_WRITE_LOCAL_NAME_COMMAND
return_parameters: HCI_SUCCESS
DEBUG:bumble.host:### HOST -> CONTROLLER: HCI_WRITE_CLASS_OF_DEVICE_COMMAND:
class_of_device: [60020C] Services(Audio,Telephony),Class(Phone|Smartphone)
DEBUG:bumble.transport.usb:submit COMMAND
DEBUG:bumble.host:### CONTROLLER -> HOST: HCI_COMMAND_COMPLETE_EVENT:
num_hci_command_packets: 1
command_opcode: HCI_WRITE_CLASS_OF_DEVICE_COMMAND
return_parameters: HCI_SUCCESS
DEBUG:bumble.host:### HOST -> CONTROLLER: HCI_WRITE_SIMPLE_PAIRING_MODE_COMMAND:
simple_pairing_mode: 1
DEBUG:bumble.transport.usb:submit COMMAND
DEBUG:bumble.host:### CONTROLLER -> HOST: HCI_COMMAND_COMPLETE_EVENT:
num_hci_command_packets: 1
command_opcode: HCI_WRITE_SIMPLE_PAIRING_MODE_COMMAND
return_parameters: HCI_SUCCESS
DEBUG:bumble.host:### HOST -> CONTROLLER: HCI_WRITE_SECURE_CONNECTIONS_HOST_SUPPORT_COMMAND:
secure_connections_host_support: 1
DEBUG:bumble.transport.usb:submit COMMAND
DEBUG:bumble.host:### CONTROLLER -> HOST: HCI_COMMAND_STATUS_EVENT:
status: HCI_UNKNOWN_HCI_COMMAND_ERROR
num_hci_command_packets: 1
command_opcode: HCI_WRITE_SECURE_CONNECTIONS_HOST_SUPPORT_COMMAND
DEBUG:bumble.host:### HOST -> CONTROLLER: HCI_WRITE_SCAN_ENABLE_COMMAND:
scan_enable: 3
DEBUG:bumble.transport.usb:submit COMMAND
DEBUG:bumble.host:### CONTROLLER -> HOST: HCI_COMMAND_COMPLETE_EVENT:
num_hci_command_packets: 1
command_opcode: HCI_WRITE_SCAN_ENABLE_COMMAND
return_parameters: HCI_SUCCESS
DEBUG:bumble.host:### HOST -> CONTROLLER: HCI_WRITE_EXTENDED_INQUIRY_RESPONSE_COMMAND:
fec_required: 0
extended_inquiry_response: 0d0942756d626c652050686f6e65
DEBUG:bumble.transport.usb:submit COMMAND
DEBUG:bumble.host:### CONTROLLER -> HOST: HCI_COMMAND_COMPLETE_EVENT:
num_hci_command_packets: 1
command_opcode: HCI_WRITE_EXTENDED_INQUIRY_RESPONSE_COMMAND
return_parameters: HCI_SUCCESS
DEBUG:bumble.host:### HOST -> CONTROLLER: HCI_WRITE_SCAN_ENABLE_COMMAND:
scan_enable: 3
DEBUG:bumble.transport.usb:submit COMMAND
DEBUG:bumble.host:### CONTROLLER -> HOST: HCI_COMMAND_COMPLETE_EVENT:
num_hci_command_packets: 1
command_opcode: HCI_WRITE_SCAN_ENABLE_COMMAND
return_parameters: HCI_SUCCESS
=== Connecting to XX:XX:XX:XX:XX:XX...
DEBUG:bumble.host:### HOST -> CONTROLLER: HCI_CREATE_CONNECTION_COMMAND:
bd_addr: XX:XX:XX:XX:XX:XX/P
packet_type: 52248
page_scan_repetition_mode: 2
reserved: 0
clock_offset: 0
allow_role_switch: 1
DEBUG:bumble.transport.usb:submit COMMAND
DEBUG:bumble.host:### CONTROLLER -> HOST: HCI_COMMAND_STATUS_EVENT:
status: PENDING
num_hci_command_packets: 1
command_opcode: HCI_CREATE_CONNECTION_COMMAND
DEBUG:bumble.host:### CONTROLLER -> HOST: HCI_CONNECTION_COMPLETE_EVENT:
status: HCI_SUCCESS
connection_handle: 72
bd_addr: XX:XX:XX:XX:XX:XX/P
link_type: ACL
encryption_enabled: 0
DEBUG:bumble.host:### BR/EDR CONNECTION: [0x0048] XX:XX:XX:XX:XX:XX/P
DEBUG:bumble.device:*** Connection: [0x0048] XX:XX:XX:XX:XX:XX/P as CENTRAL
=== Connected to XX:XX:XX:XX:XX:XX/P!
DEBUG:bumble.l2cap:creating client channel with cid=64 for psm 1
DEBUG:bumble.l2cap:Channel(64->0, PSM=1, MTU=48, state=CLOSED) state change -> WAIT_CONNECT_RSP
DEBUG:bumble.l2cap:>>> Sending L2CAP Signaling Control Frame on connection [0x0048] (CID=1) XX:XX:XX:XX:XX:XX/P:
L2CAP_CONNECTION_REQUEST [ID=1]:
psm: 1
source_cid: 64
DEBUG:bumble.host:### HOST -> CONTROLLER: (CID=1) ACL: handle=0x0048, pb=0, bc=0, data_total_length=12, data=080001000201040001004000
DEBUG:bumble.transport.usb:submit ACL
DEBUG:bumble.host:### CONTROLLER -> HOST: HCI_MAX_SLOTS_CHANGE_EVENT:
connection_handle: 72
lmp_max_slots: 5
DEBUG:bumble.host:### CONTROLLER -> HOST: ACL: handle=0x0048, pb=2, bc=0, data_total_length=10, data=060001000a3a02000200
DEBUG:bumble.hci:<<< ACL PDU: 060001000a3a02000200
DEBUG:bumble.l2cap:<<< Received L2CAP Signaling Control Frame on connection [0x0048] (CID=1) XX:XX:XX:XX:XX:XX/P:
L2CAP_INFORMATION_REQUEST [ID=58]:
info_type: EXTENDED_FEATURES_SUPPORTED
DEBUG:bumble.l2cap:>>> Sending L2CAP Signaling Control Frame on connection [0x0048] (CID=1) XX:XX:XX:XX:XX:XX/P:
L2CAP_INFORMATION_RESPONSE [ID=58]:
info_type: EXTENDED_FEATURES_SUPPORTED
result: SUCCESS
data: 80000000
DEBUG:bumble.host:### HOST -> CONTROLLER: (CID=1) ACL: handle=0x0048, pb=0, bc=0, data_total_length=16, data=0c0001000b3a08000200000080000000
DEBUG:bumble.transport.usb:submit ACL
DEBUG:bumble.host:### CONTROLLER -> HOST: HCI_NUMBER_OF_COMPLETED_PACKETS_EVENT:
number_of_handles: 1
connection_handle[0]: 72
num_completed_packets[0]: 1
DEBUG:bumble.host:### CONTROLLER -> HOST: ACL: handle=0x0048, pb=2, bc=0, data_total_length=16, data=0c000100030108004000400000000000
DEBUG:bumble.hci:<<< ACL PDU: 0c000100030108004000400000000000
DEBUG:bumble.l2cap:<<< Received L2CAP Signaling Control Frame on connection [0x0048] (CID=1) XX:XX:XX:XX:XX:XX/P:
L2CAP_CONNECTION_RESPONSE [ID=1]:
destination_cid: 64
source_cid: 64
result: CONNECTION_SUCCESSFUL
status: 0
DEBUG:bumble.l2cap:Channel(64->64, PSM=1, MTU=48, state=WAIT_CONNECT_RSP) state change -> WAIT_CONFIG
DEBUG:bumble.l2cap:>>> Sending L2CAP Signaling Control Frame on connection [0x0048] (CID=1) XX:XX:XX:XX:XX:XX/P:
L2CAP_CONFIGURE_REQUEST [ID=2]:
destination_cid: 64
flags: 0
options: 01020008
DEBUG:bumble.host:### HOST -> CONTROLLER: (CID=1) ACL: handle=0x0048, pb=0, bc=0, data_total_length=16, data=0c000100040208004000000001020008
DEBUG:bumble.transport.usb:submit ACL
DEBUG:bumble.l2cap:Channel(64->64, PSM=1, MTU=48, state=WAIT_CONFIG) state change -> WAIT_CONFIG_REQ_RSP
DEBUG:bumble.host:### CONTROLLER -> HOST: HCI_NUMBER_OF_COMPLETED_PACKETS_EVENT:
number_of_handles: 1
connection_handle[0]: 72
num_completed_packets[0]: 1
DEBUG:bumble.host:### CONTROLLER -> HOST: ACL: handle=0x0048, pb=2, bc=0, data_total_length=16, data=0c000100043b0800400000000102ed03
DEBUG:bumble.hci:<<< ACL PDU: 0c000100043b0800400000000102ed03
DEBUG:bumble.l2cap:<<< Received L2CAP Signaling Control Frame on connection [0x0048] (CID=1) XX:XX:XX:XX:XX:XX/P:
L2CAP_CONFIGURE_REQUEST [ID=59]:
destination_cid: 64
flags: 0
options: 0102ed03
DEBUG:bumble.l2cap:MTU = 1005
DEBUG:bumble.l2cap:>>> Sending L2CAP Signaling Control Frame on connection [0x0048] (CID=1) XX:XX:XX:XX:XX:XX/P:
L2CAP_CONFIGURE_RESPONSE [ID=59]:
source_cid: 64
flags: 0
result: SUCCESS
options: 0102ed03
DEBUG:bumble.host:### HOST -> CONTROLLER: (CID=1) ACL: handle=0x0048, pb=0, bc=0, data_total_length=18, data=0e000100053b0a004000000000000102ed03
DEBUG:bumble.transport.usb:submit ACL
DEBUG:bumble.l2cap:Channel(64->64, PSM=1, MTU=1005, state=WAIT_CONFIG_REQ_RSP) state change -> WAIT_CONFIG_RSP
DEBUG:bumble.host:### CONTROLLER -> HOST: ACL: handle=0x0048, pb=2, bc=0, data_total_length=10, data=060001000a3c02000300
DEBUG:bumble.hci:<<< ACL PDU: 060001000a3c02000300
DEBUG:bumble.l2cap:<<< Received L2CAP Signaling Control Frame on connection [0x0048] (CID=1) XX:XX:XX:XX:XX:XX/P:
L2CAP_INFORMATION_REQUEST [ID=60]:
info_type: FIXED_CHANNELS_SUPPORTED
DEBUG:bumble.l2cap:>>> Sending L2CAP Signaling Control Frame on connection [0x0048] (CID=1) XX:XX:XX:XX:XX:XX/P:
L2CAP_INFORMATION_RESPONSE [ID=60]:
info_type: FIXED_CHANNELS_SUPPORTED
result: SUCCESS
data: f200000000000000
DEBUG:bumble.host:### HOST -> CONTROLLER: (CID=1) ACL: handle=0x0048, pb=0, bc=0, data_total_length=20, data=100001000b3c0c0003000000f200000000000000
DEBUG:bumble.transport.usb:submit ACL
DEBUG:bumble.host:### CONTROLLER -> HOST: HCI_NUMBER_OF_COMPLETED_PACKETS_EVENT:
number_of_handles: 1
connection_handle[0]: 72
num_completed_packets[0]: 1
DEBUG:bumble.host:### CONTROLLER -> HOST: ACL: handle=0x0048, pb=2, bc=0, data_total_length=18, data=0e00010005020a0040000000000001020008
DEBUG:bumble.hci:<<< ACL PDU: 0e00010005020a0040000000000001020008
DEBUG:bumble.l2cap:<<< Received L2CAP Signaling Control Frame on connection [0x0048] (CID=1)XX:XX:XX:XX:XX:XX/P:
L2CAP_CONFIGURE_RESPONSE [ID=2]:
source_cid: 64
flags: 0
result: SUCCESS
options: 01020008
DEBUG:bumble.l2cap:Channel(64->64, PSM=1, MTU=1005, state=WAIT_CONFIG_RSP) state change -> OPEN
DEBUG:bumble.l2cap:>>> Sending L2CAP PDU on connection [0x0048] (CID=64) XX:XX:XX:XX:XX:XX/P: SDP_SERVICE_SEARCH_ATTRIBUTE_REQUEST [TID=0]:
service_search_pattern: SEQUENCE([UUID(UUID-16:111E (Handsfree))])
maximum_attribute_byte_count: 65535
attribute_id_list: SEQUENCE([UNSIGNED_INTEGER(4#2),UNSIGNED_INTEGER(9#2),UNSIGNED_INTEGER(1#2)])
continuation_state: 00
DEBUG:bumble.host:### HOST -> CONTROLLER: (CID=64) ACL: handle=0x0048, pb=0, bc=0, data_total_length=28, data=180040000600000013350319111effff350909000409000909000100
DEBUG:bumble.transport.usb:submit ACL
DEBUG:bumble.host:### CONTROLLER -> HOST: HCI_NUMBER_OF_COMPLETED_PACKETS_EVENT:
number_of_handles: 1
connection_handle[0]: 72
num_completed_packets[0]: 1
DEBUG:bumble.host:### CONTROLLER -> HOST: HCI_NUMBER_OF_COMPLETED_PACKETS_EVENT:
number_of_handles: 1
connection_handle[0]: 72
num_completed_packets[0]: 1
DEBUG:bumble.host:### CONTROLLER -> HOST: HCI_NUMBER_OF_COMPLETED_PACKETS_EVENT:
number_of_handles: 1
connection_handle[0]: 72
num_completed_packets[0]: 1
DEBUG:bumble.host:### CONTROLLER -> HOST: ACL: handle=0x0048, pb=2, bc=0, data_total_length=11, data=0700400001000000020003
DEBUG:bumble.hci:<<< ACL PDU: 0700400001000000020003
DEBUG:bumble.sdp:<<< Response: SDP_ERROR_RESPONSE [TID=0]:
error_code: [0x300]
DEBUG:bumble.transport.usb:waiting for IN[4] transfer cancellation to be done...
DEBUG:bumble.transport.usb:IN[4] transfer cancellation done
DEBUG:bumble.transport.usb:waiting for IN[2] transfer cancellation to be done...
DEBUG:bumble.transport.usb:USB event loop done
DEBUG:bumble.transport.usb:IN[2] transfer cancellation done
Traceback (most recent call last):
File "/home/anyone/Downloads/bumble/examples/run_hfp_gateway.py", line 210, in
asyncio.run(main())
File "/usr/lib/python3.9/asyncio/runners.py", line 44, in run
return loop.run_until_complete(main)
File "/usr/lib/python3.9/asyncio/base_events.py", line 642, in run_until_complete
return future.result()
File "/home/anyone/Downloads/bumble/examples/run_hfp_gateway.py", line 134, in main
channels = await list_rfcomm_channels(device, connection)
File "/home/anyone/Downloads/bumble/examples/run_hfp_gateway.py", line 50, in list_rfcomm_channels
search_result = await sdp_client.search_attributes(
File "/usr/local/lib/python3.9/dist-packages/bumble/sdp.py", line 718, in search_attributes
accumulator += response.attribute_lists
AttributeError: 'SDP_ErrorResponse' object has no attribute 'attribute_lists'

JUST_WORKS bonding

Hi,

I need help understanding how pairing works from apps/pair.py. While using this app, I got an error message when I try to connect with an iPhone: (except for first connection)

Failed to Connect: Peer removed pairing information

Furthermore, when I disconnect and reconnect an Android, I have to redo pairing.

I configure it as follows :

  • mode : "le"
  • request = True
  • device1.json

So my questions are: How to store and use pairing information with Bumble for a Just_Works pairing method ? And how can we use or are we supposed to use PairingDelegate to create a device with a just works bonding?

I really appreciate any help you can provide.

Subscribing to characteristic with both notify and indicate returns ATT error

Some BT stacks do not handle setting the notify and indicate CCCD bits simultaneously when subscribing to a characteristic.

For example, Cordio returns an ATT error of 0x80. In the Cordio stack this is defined as ATT_ERR_VALUE_RANGE, and in the Bumble stack this is defined as WRITE_REQUIRES_AUTHORIZATION.

https://github.com/packetcraft-inc/stacks/blob/3656312d6b73e2a2c1c8b33ee0385bc199dd97e6/ble-host/sources/stack/att/atts_ccc.c#L231

Should the GATT client subscribe() method prefer notify over indicate or indicate over notify? Should we let the caller choose with a separate subscribe with notify and subscribe with indicate method like the notify_subscribers()/indicate_subscribers() methods? Maybe we can have a keyword argument to subscribe that's an enum of values notify, indicate, or either.

Also, is it invalid to have a characteristic that supports both notify and indicate properties?

https://github.com/google/bumble/blob/main/bumble/gatt_client.py#L562

Android crash on shutdown

I think this bug is probably on the emulator side, but I havent found the correct team to contact for that so if you know please let me know, so I thought I would at least start here.

Scenario:
I can successfully create an hci bridge to the android emulator and everything works, awesome job bumble team!
The problem is I cant find anyway to close down the the emulator without it crashing once this link has been established, here are some things I have tried (they all end up with the emulator crashing):

  • Closing emulator
  • Sending a shutdown command to the emulator
  • Disabling bluetooth and then trying the two above methods of closing the emulator.
  • Closing the bumble process

I used to get a popup on my linux box with more crash details, but I must have checked some box to send them automatically or something because I dont get this box anymore (tried android reinstall but no change), the only output I have is from my console and it is not helpful just shows a small stack trace of hex addresses. I have tried multiple emulators and android versions, but they all crash.

Is there any way to send a command to the emulator to stop the vhci forward / root canal thing (in hopes that after that it wont crash on shutdown)?

Any suggestions are greatly appreciated, thanks.

support setting the page timeout for Classic connections

For BR/EDR, in the current implementation, Device.connect() will try to connect, using the current (default) page timeout (2 seconds?). An exception is thrown if the connection doesn't complete in time, and it is up to the caller to retry (and the timeout parameter is ignored). This should be improved: automatically retry when timeout is infinite, and set the page timeout correctly when a non-infinite timeout is specified.

vhci Adapter not visible to chromium on Raspberry Pi

I have been trying to access the example characteristics from the example device in the run_controller.py example. I have followed the vhci steps here (https://google.github.io/bumble/platforms/linux.html#using-vhci).

Although I can see the virtual controller in hciconfig, as well as connect to the virtual device attached to it using bluetoothctl and then list characteristics, I cannot do the same in chromium because chromium cannot see the bumble vhci adapter.

I have tried disabling the pi's built in adapter and re-starting chrome, but still it is not aware of the bumble vhci adapter.

I am not sure if this is a limitation of bumble or chrome, but I was hoping to use bumble to develop a heart rate monitoring application as a replacement for my physical device which works well with chrome when connected via the pi's real controller.

Steps to Reproduce

In terminal window A, start the bumble example:

cd $BUMBLE_ROOT
sudo chmod 766 /dev/vhci
python3 examples/run_controller.py F6:F7:F8:F9:FA:FB examples/device1.json vhci

In terminal window B, disable other adapter(s) , verify virtual characteristics

sudo hciconfig hci0 down
bluetoothctl
menu scan
transport le
back
scan on
pair F0:F1:F2:F3:F4:F5
trust F0:F1:F2:F3:F4:F5
scan off

virtualdeviceservices

Open a chromium window, verify bluetooth is enabled and verify that the bumble adapter (C7:C4:87:86:93:12) is not detected. In the search bar:

(enable bluetooth) chrome://flags/#enable-experimental-web-platform-features
(check detected adapter) chome://bluetooth-internals/

chromiumbluetooth

The virtual controller is not detected, so chrome is unable to read services from the virtual Bumble device provided by run_controller.py since scanning for it will always fail

Bumble stops working on Android when having (too?) many characteristics

Hi,

We are using this great project with a Nordic nRF52840 dongle.
However we notice that when having many characteristics, it stops working for (some) Android devices.

I have added about 170 (notifiable) characteristics and while I initially have no errors when starting the python code, bonding is never triggered and bluetooth connection times out.

Any idea what is going on, and if I could solve this?

Issue while using NRF dongle with HCI_USB firmware

Good afternoon!

I'm trying to run through some of the demo programs that come with Bumble. However, I'm getting an error and the commands hang indefinitely. Ex:

akuker@xxx:~/bumble/bumble_src$ bumble-controller-info usb:1
<<< connecting to HCI...
<<< connected
WARNING:bumble.transport.usb:!!! out transfer not completed: status=4
akuker@xxx:~/bumble/bumble_src$ bumble-scan usb:2fe3:000b
<<< connecting to HCI...
<<< connected
WARNING:bumble.transport.usb:!!! out transfer not completed: status=4

Using pyusb gives a slightly different error:

akuker@xxx:~/bumble/bumble_src$ bumble-scan pyusb:2FE3:000B
<<< connecting to HCI...
<<< connected
WARNING:bumble.transport.pyusb:USB write error: [Errno 32] Pipe error

My setup:

I'm running Ubuntu 20.04.05 on a reasonably high-end IBM Thinkpad.

I'm using the Nordic nRF52840 dongle with the Zephyr USB_HID demo loaded. I believe this is working properly, since lsusb gives reasonable output:

akuker@xxx:~$ lsusb
Bus 004 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
Bus 003 Device 028: ID 2fe3:000b ZEPHYR USB-DEV
......

Bumble usb probe shows two Bumble-compatible devices:

akuker@xxx:~/bumble/bumble_src$ bumble-usb-probe 
ID 8087:0032
  Bumble Transport Names: usb:0 or usb:8087:0032
  Bus/Device:             003/007
  Class:                  Wireless Controller
  Subclass/Protocol:      1/1 [Bluetooth]

ID 2FE3:000B
  Bumble Transport Names: usb:1 or usb:2FE3:000B or usb:2FE3:000B/912A41BB46CF0B92
  Bus/Device:             003/028
  Class:                  Device
  Subclass/Protocol:      0/0
  Serial:                 912A41BB46CF0B92
  Manufacturer:           ZEPHYR
  Product:                USB-DEV

I'm assuming the host BT stack (BlueZ) is using usb:0/hci0 and not using the Nordic dongle.

akuker@xxx:~$ hciconfig 
hci0:	Type: Primary  Bus: USB
	BD Address: xx:xx:xx:xx:xx  ACL MTU: 1021:4  SCO MTU: 96:6
	DOWN 
	RX bytes:16331 acl:0 sco:0 events:2614 errors:0
	TX bytes:641217 acl:0 sco:0 commands:2612 errors:0

Do you have any suggestions of things to try to troubleshoot/get to root cause?

Bumble inside a docker container without using bluetooth from host?

This might not be an issue but its more of a discussion (PS. It would be nice to have a discussions page to ask questions)

I have already seen the advantages of bumble for my use cases which is emulating BLE GATT interfaces from Iot Device firmware for integration tests (e.g. a Linux BLE Gateway or a Phone application). As I really like to automate everything I was interested in doing integration tests within Docker.

However, to use docker container (linux) with Bluez as far as I know you need access to the Bluetooth from host PC, otherwise you will get the error

Can't open HCI socket.: Address family not supported by protocol

Which happens when Bluez opens an HCI socket [1].

For a practical solution I have found out that I need to set up for my CI/CD a runner (either Gitlab or Github) that has access to the Bluetooth host even though I am not going to use real hardware rather bumble to create a virtual hci.

So I was wondering if there is a way to have a real virtual bluetooth bluez driver running on docker to use bumble. Its not that big of a problem but I rather run everything virtually with default runners (spawned by Github or Gitlab) rather than having a dedicated server to run my virtual bluetooth integration tests.

This came to my thought in the sense that if bumble already allows virtualization of bluetooth why not in Bluez in Docker, somehow (?).

Thanks for reading, I come more from an Embedded background so I might be ignorant on virtual solutions for bluetooth. Open for suggestions.

Unable to trigger notifications from two virtual devices connected to the same bumble controller

Hello, I have had good success melding the heart rate server example (heart_rate_server.py) with the run controller example (run_controller.py). I have used the vhci controller bus option and been able to observe virtual heart rate measurements on my raspberry pi from the chromium browser. Great!

But, I want to add a second virtual device, so I added another virtual heart rate server. Something like this:

C1<-VHCI->System
|
|->C2(Heart rate monitor 1)
|->C3(Heart rate monitor 2)

Now things have gone wrong. Only one of these virtual heart rate monitors, say C2, ever gets notifications from a subscriber sent to its HeartRateService() instance, although both C2 and C3 can be found and paired from bluetoothctl to the bluez stack.

I have been careful to check that I duplicated instances for all necessary classes, but I can't find what the cause for the second virtual device not firing messages might be and single stepping the code didn't provide any clues either.

I've attached my script below, which will reproduce the issue when dumped into the examples directory. But if anyone has a working example of more than one virtual device connected to the same controller that would also be ideal.

Cheers!

import logging
import asyncio
import os

import time
import math
import random
import struct

from bumble.gatt import (
    GATT_CHARACTERISTIC_USER_DESCRIPTION_DESCRIPTOR,
    GATT_DEVICE_INFORMATION_SERVICE,
    GATT_MANUFACTURER_NAME_STRING_CHARACTERISTIC,
    Characteristic,
    Descriptor,
    Service,
)
from bumble.device import Device
from bumble.host import Host
from bumble.controller import Controller
from bumble.link import LocalLink
from bumble.transport import open_transport_or_link

from bumble.core import AdvertisingData

from bumble.profiles.device_information_service import DeviceInformationService
from bumble.profiles.heart_rate_service import HeartRateService
from bumble.profiles.battery_service import BatteryService


# -----------------------------------------------------------------------------
async def main():

    print('>>> connecting to HCI...')
    async with await open_transport_or_link('vhci') as (hci_source, hci_sink):
        print('>>> connected')

        # Create a local link
        link = LocalLink()

        # Create a first controller using the packet source/sink as its host interface
        controller1 = Controller(
            'C1', host_source=hci_source, host_sink=hci_sink, link=link
        )
        controller1.random_address = "F0:F7:F8:F9:FA:FB"

        #========================================
        #       Virtual Heart Rate Device       #
        #========================================
        # Keep track of accumulated expended energy
        energy_start_time_hrm = time.time()

        def reset_energy_expended_hrm():
            nonlocal energy_start_time_hrm
            energy_start_time_hrm = time.time()

        # Create a second controller using the same link
        controller2 = Controller('C2', link=link)

        # Create a host for the second controller
        host = Host()
        host.controller = controller2

        # Create a device to manage the host
        hrm_device = Device.from_config_file("device2.json")
        hrm_device.host = host

        #MB also add a heart rate service
        heart_rate_service = HeartRateService(
            read_heart_rate_measurement=lambda _: HeartRateService.HeartRateMeasurement(
                heart_rate=100 + int(50 * math.sin(time.time() * math.pi / 60)),
                sensor_contact_detected=random.choice((True, False, None)),
                energy_expended=random.choice(
                    (int((time.time() - energy_start_time_hrm) * 100), None)
                ),
                rr_intervals=random.choice(
                    (
                        (
                            random.randint(900, 1100) / 1000,
                            random.randint(900, 1100) / 1000,
                        ),
                        None,
                    )
                ),
            ),
            body_sensor_location=HeartRateService.BodySensorLocation.WRIST,
            reset_energy_expended=lambda _: reset_energy_expended_hrm(),
        )

        hrm_device.add_services([heart_rate_service])

        # Debug print
        for attribute in hrm_device.gatt_server.attributes:
            print(attribute)

        #MB: set virtual device to advertize heart rate capability
        # Set the advertising data
        hrm_device.advertising_data = bytes(
            AdvertisingData(
                [
                    (
                        AdvertisingData.COMPLETE_LOCAL_NAME,
                        bytes('Bumble Heart', 'utf-8'),
                    ),
                    (
                        AdvertisingData.INCOMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS,
                        bytes(heart_rate_service.uuid),
                    ),
                    (AdvertisingData.APPEARANCE, struct.pack('<H', 0x0340)),
                ]
            )
        )

        #========================================
        #   Virtual HRM Device 2 'Controllable' #
        #========================================

        # Keep track of accumulated expended energy
        energy_start_time_ctl = time.time()

        def reset_energy_expended_ctl():
            nonlocal energy_start_time_ctl
            energy_start_time_ctl = time.time()

        # Create a third controller using the same link
        controller3 = Controller('C3', link=link)

        # Create a host for the third controller
        host2 = Host()
        host2.controller = controller3

        # Create a device to manage the host
        controllable_device = Device.from_config_file("device1.json")
        controllable_device.host = host2
        
        controllable_heart_rate_service = HeartRateService(
            read_heart_rate_measurement=lambda _: HeartRateService.HeartRateMeasurement(
                heart_rate=100 + int(50 * math.sin(time.time() * math.pi / 60)),
                sensor_contact_detected=random.choice((True, False, None)),
                energy_expended=random.choice(
                    (int((time.time() - energy_start_time_ctl) * 100), None)
                ),
                rr_intervals=random.choice(
                    (
                        (
                            random.randint(900, 1100) / 1000,
                            random.randint(900, 1100) / 1000,
                        ),
                        None,
                    )
                ),
            ),
            body_sensor_location=HeartRateService.BodySensorLocation.WRIST,
            reset_energy_expended=lambda _: reset_energy_expended_ctl(),
        )

        controllable_device.add_services([controllable_heart_rate_service])

        # Debug print
        for attribute in controllable_device.gatt_server.attributes:
            print(attribute)
        
        #MB: set virtual device to advertize heart rate capability
        # Set the advertising data
        controllable_device.advertising_data = bytes(
            AdvertisingData(
                [
                    (
                        AdvertisingData.COMPLETE_LOCAL_NAME,
                        bytes('Bumble Controllable', 'utf-8'),
                    ),
                    (
                        AdvertisingData.INCOMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS,
                        bytes(controllable_heart_rate_service.uuid),                    #uuid of service instance you defined earlier
                    ),
                    (AdvertisingData.APPEARANCE, struct.pack('<H', 0x0340)),
                ]
            )
        )

        #===== Virtual Device Management =======

        #device 1, HRM
        await hrm_device.power_on()
        await hrm_device.start_advertising(auto_restart=True)

        #device 2, 'Controllable'
        await controllable_device.power_on()
        await controllable_device.start_advertising(auto_restart=True)

        #TODO this below needs to be parallel somehow? can't be the isse.

        # Notify every 2 seconds
        while True:
            await asyncio.sleep(1.0)
            
            
            #HRM device notify
            await hrm_device.notify_subscribers(
                heart_rate_service.heart_rate_measurement_characteristic
            )
            
            await asyncio.sleep(1.0)

            #Controllable device notify
                       
            await controllable_device.notify_subscribers(
                controllable_heart_rate_service.heart_rate_measurement_characteristic
            )

        await hci_source.wait_for_termination()


# -----------------------------------------------------------------------------
logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper())
asyncio.run(main())

Add support for Enhanced L2CAP mode to support GOEB

For my use case, I would like to be able to use the Generic Object Exchange Profile (GOEP). According to the GOEP specification, the L2CAP channel must be configured for Enhanced Retransmission Mode:

7.1.2 Flow and Error Control Option
The L2CAP channel for OBEX connections shall be configured to use Enhanced Retransmission Mode

The current l2cap implementation in Bumble only supports Basic flow/control mode and does not support Enhanced Retransmission Mode. Therefore will not support GOEP functions. Please update the l2cap implementation (and test cases) to support ERTM.

Windows : Either during unbond or updating the keys , get FileExistsError

exception=FileExistsError(17, 'Cannot create a file when that file already exists')>
Traceback (most recent call last):

await keystore.delete(address)

File "\Python39\site-packages\bumble\keys.py", line 270, in delete
await self.save(db)
File "\Python39\site-packages\bumble\keys.py", line 260, in save
os.rename(temp_filename, self.filename)
FileExistsError: [WinError 183] Cannot create a file when that file already exists:

The error occurs when the bumble.keys tries to rename the temporary file to the original file, but the original file already exists. On Windows, the os.rename function will raise a FileExistsError if the destination file already exists. To fix this issue, you can use the shutil.move function, which will overwrite the destination file if it exists.

import shutil
shutil.move(temp_filename, self.filename)

Design question on sink classes and emits

Hi
First, this is a great project - I am trying to understand BLE and seeing code in Python is cool!
I am reading through the code to see how it works from USB through to Gatt Server.
I think the HCI layer uses sink classes then above that uses pyee Emitter classes. Is that right and is there a reason to change approaches across layers?
Just trying to understand the overall design.
Thanks

No data from Realtek Bluetooth 5.1 Radio

I got a bluetooth dongle with Realtek chip because it has external antenna. But for some reason or another, it doesn't appear to receive anything.

Probe finds it ok:

(vble) % bumble-usb-probe
ID 0BDA:8771
  Bumble Transport Names: usb:0 or usb:0BDA:8771 or usb:0BDA:8771/00E04C239987
  Bus/Device:             000/002
  Class:                  Wireless Controller
  Subclass/Protocol:      1/1 [Bluetooth]
  Serial:                 00E04C239987
  Manufacturer:           Realtek
  Product:                Bluetooth 5.1 Radio

But when starting scan, nothing is displayed.

(vble) % bumble-scan usb:0
<<< connecting to HCI...
<<< connected

I have an other dongle with Broadcom chipset at same room, it works ok with bumble - running scan prints a lot of advertisements. Is the Realtek chip just not supported ? Or is the dongle broken ?

(I'll have to say that this is great stuff. I'm using bumble to connect nrf52 based environment sensors I have developed. So much easier than c-language approach I had previously)

Windows : On Pairing get - WARNING:bumble.smp:pairing failure (SMP_INVALID_PARAMETERS_ERROR)

Bumble : v0.0.148
Windows : 11 22621.1635

The events leading to the failure:

A connection is established with another device (remote device address: 34:2E:B7:DF:76:26), and the connection handle is 0x0048.
The Secure Simple Pairing (SSP) process takes place, which involves exchanging pairing random values and checking Diffie-Hellman keys.
The encryption is enabled for the connection.
The Identity Resolving Key (IRK) and Identity Address are exchanged.
A GATT request for writing an attribute value is performed, followed by another request to read primary services.
An SMP pairing failure occurs due to invalid parameters (SMP_INVALID_PARAMETERS_ERROR), resulting in a warning message.
The connection is terminated by the remote user (HCI_REMOTE_USER_TERMINATED_CONNECTION_ERROR), and the device starts advertising again.

DEBUG:bumble.smp:>>> Sending SMP Command on connection [0x0048] 34:2E:B7:DF:76:26/P: SMP_IDENTITY_ADDRESS_INFORMATION_COMMAND:
  addr_type: RANDOM_DEVICE_ADDRESS
  bd_addr:   6F:6D:6E:79:6B:01
DEBUG:bumble.host:### HOST -> CONTROLLER: (CID=6) ACL: handle=0x0048pb=0, bc=0, data_total_length=12, data=080006000901016b796e6d6f
DEBUG:bumble.transport.usb:submit ACL
DEBUG:bumble.host:### CONTROLLER -> HOST: ACL: handle=0x0048pb=2, bc=0, data_total_length=9, data=050004001209000200
DEBUG:bumble.hci:<<< ACL PDU: 050004001209000200
DEBUG:bumble.gatt_server:GATT Request to server: [0x0048] ATT_WRITE_REQUEST:
  attribute_handle: 0x0009
  attribute_value:  0200
DEBUG:bumble.gatt_server:GATT Response from server: [0x0048] ATT_WRITE_RESPONSE
DEBUG:bumble.host:### HOST -> CONTROLLER: (CID=4) ACL: handle=0x0048pb=0, bc=0, data_total_length=5, data=0100040013
DEBUG:bumble.transport.usb:submit ACL
DEBUG:bumble.host:### CONTROLLER -> HOST: HCI_NUMBER_OF_COMPLETED_PACKETS_EVENT:
  number_of_handles:         1
  connection_handle[0]:     72
  num_completed_pack
ets[0]: 1
DEBUG:bumble.host:### CONTROLLER -> HOST: ACL: handle=0x0048pb=2, bc=0, data_total_length=6, data=02000600050a
DEBUG:bumble.hci:<<< ACL PDU: 02000600050a
DEBUG:bumble.smp:<<< Received SMP Command on connection [0x0048] 34:2E:B7:DF:76:26/P: SMP_PAIRING_FAILED_COMMAND:
  reason: SMP_INVALID_PARAMETERS_ERROR
WARNING:bumble.smp:pairing failure (SMP_INVALID_PARAMETERS_ERROR)

Full logs added below
SMP_PAIR_FAIL.txt

Device or resource busy running on Raspberry 4

We are running the run controller.py example on a Raspberry 4 but we get the following error

sudo python3 ble.py DC:A6:32:68:FA:17 device.json hci-socket:0
DEBUG:asyncio:Using selector: EpollSelector
>>> connecting to HCI...
Traceback (most recent call last):
  File "/home/kenko/Documents/Kabanta/Kabanta/Proy_graph/ble.py", line 251, in <module>
    asyncio.run(main())
  File "/usr/lib/python3.9/asyncio/runners.py", line 44, in run
    return loop.run_until_complete(main)
  File "/usr/lib/python3.9/asyncio/base_events.py", line 642, in run_until_complete
    return future.result()
  File "/home/kenko/Documents/Kabanta/Kabanta/Proy_graph/ble.py", line 197, in main
    async with await open_transport_or_link(sys.argv[3]) as (hci_source, hci_sink):
  File "/usr/local/lib/python3.9/dist-packages/bumble/transport/__init__.py", line 182, in open_transport_or_link
    return await open_transport(name)
  File "/usr/local/lib/python3.9/dist-packages/bumble/transport/__init__.py", line 74, in open_transport
    return _wrap_transport(await _open_transport(name))
  File "/usr/local/lib/python3.9/dist-packages/bumble/transport/__init__.py", line 131, in _open_transport
    return await open_hci_socket_transport(spec[0] if spec else None)
  File "/usr/local/lib/python3.9/dist-packages/bumble/transport/hci_socket.py", line 93, in open_hci_socket_transport
    raise IOError(ctypes.get_errno(), os.strerror(ctypes.get_errno()))
OSError: [Errno 16] Device or resource busy

I can see the controller using hciconfig

hci0:	Type: Primary  Bus: UART
	BD Address: DC:A6:32:68:FA:17  ACL MTU: 1021:8  SCO MTU: 64:1
	UP RUNNING PSCAN 
	RX bytes:2241 acl:0 sco:0 events:119 errors:0
	TX bytes:5095 acl:0 sco:0 commands:119 errors:0
	Features: 0xbf 0xfe 0xcf 0xfe 0xdb 0xff 0x7b 0x87
	Packet type: DM1 DM3 DM5 DH1 DH3 DH5 HV1 HV2 HV3 
	Link policy: RSWITCH SNIFF 
	Link mode:hci0:	Type: Primary  Bus: UART
	BD Address: DC:A6:32:68:FA:17  ACL MTU: 1021:8  SCO MTU: 64:1
	UP RUNNING PSCAN 
	RX bytes:2241 acl:0 sco:0 events:119 errors:0
	TX bytes:5095 acl:0 sco:0 commands:119 errors:0
	Features: 0xbf 0xfe 0xcf 0xfe 0xdb 0xff 0x7b 0x87
	Packet type: DM1 DM3 DM5 DH1 DH3 DH5 HV1 HV2 HV3 
	Link policy: RSWITCH SNIFF 
	Link mode: SLAVE ACCEPT 
	Name: 'raspberrypi'
	Class: 0x7c0000
	Service Classes: Rendering, Capturing, Object Transfer, Audio, Telephony
	Device Class: Miscellaneous, 
	HCI Version: 5.0 (0x9)  Revision: 0x17e
	LMP Version: 5.0 (0x9)  Subversion: 0x6119
	Manufacturer: Cypress Semiconductor (305) SLAVE ACCEPT 
	Name: 'raspberrypi'
	Class: 0x7c0000
	Service Classes: Rendering, Capturing, Object Transfer, Audio, Telephony
	Device Class: Miscellaneous, 
	HCI Version: 5.0 (0x9)  Revision: 0x17e
	LMP Version: 5.0 (0x9)  Subversion: 0x6119
	Manufacturer: Cypress Semiconductor (305)

Exception Handling - USB device disconnects.

Whats the best way to catch exceptions like these from the custom script.

!!! transfer not completed: status=4
!!! transfer not completed: status=4
!!! transfer not completed: status=5
!!! transfer not completed: status=5
CAUGHT
Exception ignored on calling ctypes callback function: <bound method USBTransfer.__callbackWrapper of <class 'usb1.USBTransfer'>>
Traceback (most recent call last):
  File "C:\Users\thisi\AppData\Roaming\Python\Python39\site-packages\usb1\__init__.py", line 327, in __callbackWrapper
    callback(self)
  File "D:\Anaconda3\lib\site-packages\bumble\transport\usb.py", line 284, in on_packet_received
    exit()
  File "D:\Anaconda3\lib\_sitebuiltins.py", line 26, in __call__
    raise SystemExit(code)
SystemExit: None
!!! transfer not completed: status=5
CAUGHT
Exception ignored on calling ctypes callback function: <bound method USBTransfer.__callbackWrapper of <class 'usb1.USBTransfer'>>
Traceback (most recent call last):
  File "C:\Users\thisi\AppData\Roaming\Python\Python39\site-packages\usb1\__init__.py", line 327, in __callbackWrapper
    callback(self)
  File "D:\Anaconda3\lib\site-packages\bumble\transport\usb.py", line 284, in on_packet_received
    exit()
  File "D:\Anaconda3\lib\_sitebuiltins.py", line 26, in __call__
    raise SystemExit(code)
SystemExit: None

Custom BT stack with Link Relay

Not sure this is really an issue, but I'm looking to do a modified version of Use Case 6. I have a custom Bluetooth stack, which uses a standard H4 HCI interface. I'm planning to use the Linux hci_vhci.ko module, but don't have to.

I want to connect this BT stack to a link relay, which will have several virtual devices connected.

I haven't able to find an example/application in the bumble repository that does what I'm trying to do. It looks like I need to create something like run_simulator.py which will create a RemoteLink to the link_relay, then connect that to a virtual controller, which uses vhci as its host sink/source. (also, without the devices, since those are all connected to the link relay)

                                     ┌───────────────┐
                                     │   Custom BT   │
                                     │     Stack     │
                                     ├───────────────┤
                                     │ HCI Interface │
                                     └───────────────┘
                                             ▲
                                             │
                                             │hci_vhci.ko
                                             │
                                             ▼ Custom python app (pseudocode follows)
                          ┌───────────────────────────────────────────┐
                          │rlink = RemoteLink(ws:127.0.0.1:10723)     │
                          │open_transport_or_link(vhci) as (vhci_host)│
                          │ctrl = Controller (vhci_host, rlink)       │
                          │wait_for_termination()                     │
                          └───────────────────────────────────────────┘
                                             ▲
                                             │
+--------++--------++------------+           │websock     +------------++--------++--------+
| Bumble || Bumble || Bumble     |           │            | Bumble     || Bumble || Bumble |
| Python || Host   || Controller |<--+       ▼        +-->| Controller || Host   || Python |
| App    ||        ||            |   |   +--------+   |   |            ||        || App    |
+--------++--------++------------+   +-->| Bumble |<--+   +------------++--------++--------+
                                         | Link   |
+--------++--------++------------+   +-->| Relay  |<--+   +------------++--------++--------+
| Bumble || Bumble || Bumble     |   |   +--------+   |   | Bumble     || Bumble || Bumble |
| Python || Host   || Controller |<--+                +-->| Controller || Host   || Python |
| App    ||        ||            |                        |            ||        || App    |
+--------++--------++------------+                        +------------++--------++--------+

Am I on the right track? Or is there a better / more straightforward way to do this?

Thank you for your help!!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.