Search code examples
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

  • The device actually does not show up as an input in /dev/input/. When I connect it manually through the GUI, it shows up as /dev/input/event5.
  • After connecting, there is no input captured by my script.

My Questions

  • How does it connect according to hcitool con yet not be registered as an input device (and register any inputs)?
  • What do you suggest doing? I have looked everywhere and do not seem to get a proper solution. There is a workaround with implementing a bash script that uses bluetoothctl to connect to the Bluetooth remote control, but it just does not make sense to me why Python cannot make this connection and retrieve information.

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