pythonsocketsbluetoothpybluezl2cap

How do I connect a L2CAP-based Bluetooth Camera Shutter (AB Shutter 3) to Linux and get the key event in Python?


Goal

I have a HITSLAM Camera Shutter Bluetooth button (which is a AB Shutter 3 device, a common Bluetooth camera remote control) which I want to connect to my NVIDIA Jetson Nano using Bluetooth, so that I can use the button's input for some task.

What I Have Done

I am using the PyBluez library for connecting. I use the following to find out which port and protocol the AB Shutter 3 uses (where target_device_address is the AB Shutter 3's device address):

service_matches = bt.find_service(name=None,uuid=None,address=target_device_address)
first_match = service_matches[0]
print("Port {}, Name {}, Host {}, Protocol {}".format(first_match['port'], first_match['name'], first_match['host'], first_match['protocol']))

This is how I get the port (17) which to connect to and the protocol (L2CAP) it uses.

Now, I try to connect to it using the following:

client_sock = bt.BluetoothSocket(bt.L2CAP)
client_sock.connect((target_device_address,port))

I have also used Python's native socket library (which have yielded me the same results):

client_sock = socket.socket(socket.AF_BLUETOOTH, socket.SOCK_SEQPACKET, socket.BTPROTO_L2CAP)
client_sock.connect((target_device_address,port))

Which it successfully connects according to hcitool, after which I wait for user input:

if target_device_address in (subprocess.getoutput("hcitool con")).split():
    print('connected')
    while True:
        data = client_sock.recv(1024)
        print(str(data))

Issues

My Questions


Solution

  • My recommendation would be not to use hcitool as it was deprecated back in 2017.

    I prefer to use the BlueZ D-Bus API directly which is documented at: https://git.kernel.org/pub/scm/bluetooth/bluez.git/tree/doc/device-api.txt

    This can be accessed in Python using the pydbus library

    I am going to assume your Bluetooth adapter is on hci0 on the Jetson but you can check this with:

    $ busctl tree org.bluez
    └─/org
      └─/org/bluez
        └─/org/bluez/hci0
    

    This would make the code something like:

    import pydbus
    
    DEVICE_ADDR = '11:22:22:E7:CE:BE'
    
    # DBus object paths
    BLUEZ_SERVICE = 'org.bluez'
    ADAPTER_PATH = '/org/bluez/hci0'
    device_path = f"{ADAPTER_PATH}/dev_{DEVICE_ADDR.upper().replace(':', '_')}"
    
    # setup dbus
    bus = pydbus.SystemBus()
    mngr = bus.get(BLUEZ_SERVICE, '/')
    adapter = bus.get(BLUEZ_SERVICE, ADAPTER_PATH) 
    device = bus.get(BLUEZ_SERVICE, device_path)
    
    device.Connect()
    

    This should create the event at /dev/input/ and I would use the python library evdev to get the inputs as was done in the following question: https://stackoverflow.com/a/54765300/7721752