Cheap BlueTooth Buttons and Linux – Terence Eden’s Blog
Selfie sticks – like most modern inventions – are utter tosh. But they’ve rapidly brought down the price of Bluetooth buttons. So who am I to complain?
Let’s take the venerable AB Shutter 3 – You can find it on Amazon for around £2 including postage – or around $2 on AliExpress. Frankly, that’s stupidly cheap.
OK, let’s put this to work as something other than a vanity clicker! There are no instructions which come with this, it’s delivered in a little plastic bag and that’s it. Time to get hacking!
Once paired to a server, like the Raspberry Pi, pressing the button should run a program to turn on my Lifx bulbs.
With the battery panel slipped off and the cell removed, it’s fairly easy to open the case. Fingernails are sufficient – no screws or glue!
It’s an AIROHA AB1126A.
AB1126A is an optimized single-chip solution which integrates baseband and radio for wireless human input device applications especially for remote smartphone camera control. It complies with Bluetooth system version 3.0.
But what happens when we ZOOM! ENHANCE!?
The 24C16N is a fairly generic EEPROM.
But what’s this?!?!
The chip is listed as an AB1127A. A chip which, seemingly, doesn’t exit. Onwards!
When switched into the “on” position, the dongle is ready to pair.
From the Ubuntu command line:
$ hcitool scan
Scanning ...
80:00:00:00:EE:E0 AB Shutter 3
Aha! We’ve found it. What sort of device is it?
$ hcitool inq
Inquiring ...
80:00:00:00:EE:E0 clock offset: 0x0acd class: 0x002540
It shows up a a keyboard. Let’s connect to it and trust it.
$ bluez-simple-agent hci0 80:00:00:00:EE:E0
Release
New device (/org/bluez/794/hci0/dev_80_00_00_00_EE_E0)
$ bluez-test-device trusted 80:00:00:00:EE:E0
$ bluez-test-input connect 80:00:00:00:EE:E0
To check that it is seen and connected properly:
$ xinput
↳ AB Shutter 3 id=13 [slave keyboard (3)]
Nice! Running xinput query-state "AB Shutter 3"
allows us to see which keyboard keys are activated when the buttons are pressed.
It turns out that the iOS buttons sends Volume Up (key 123) whereas the Android button sends Enter (key 36).
Pressing the selfie-button instantly sends the command to my computer! Well… until the button goes to sleep. The device is powered by a CR2032 battery which, despite the power efficiencies of Bluetooth, isn’t magical.
After a few minutes of idleness, the device goes to sleep. Pressing any button wakes it up and repairs the connection – but then another button press is required to send a key press. The pairing process only takes a couple of seconds, so it’s not quite instant.
Having an external button which can increase the volume or send an enter command isn’t very useful. I want to press the button and have a program run which will (for example) turn on my lights.
Because the device goes to sleep after a few minutes of inactivity, we need a way to listen for a connection. So, when a button is pressed for the first time, the device connects and a program is run.
I’ve half-inched the instructions from this InOut Board tutorial.
First of all, make sure Python has the ability to work with Bluetooth:
sudo apt-get install python-bluez
#!/usr/bin/python
import bluetooth
import time
while True:
print "Checking " + time.strftime("%a, %d %b %Y %H:%M:%S", time.gmtime())
result = bluetooth.lookup_name('80:00:00:00:EE:E0', timeout=5)
if (result != None):
print "Device detected"
# Do Something
else:
print "Device NOT detected"
time.sleep(6)
With that running constantly in the background, you can perform an action whenever the device connects.
Right, this is where it gets tricky! Ubuntu doesn’t seem to differentiate between different keyboards attached to a device. This means you can’t use loadkeys to swap keys, nor xkb.
You can, however, use xkbcomp to remap the buttons on a specific device (thanks to Stephen Wing for that tip).
This will convert the Volume Up to XF86Launch1
and Enter to XF86Launch2
– those are multimedia keycodes which shouldn’t be assigned to anything by default.
remote_id=$(
xinput list |
sed -n 's/.*AB Shutter 3.*id=\([0-9]*\).*keyboard.*/\1/p'
)
[ "$remote_id" ] || exit
mkdir -p /tmp/xkb/symbols
cat >/tmp/xkb/symbols/custom <<\EOF
xkb_symbols "remote" {
key { [ XF86Launch1 ] };
key { [ XF86Launch2 ] };
};
EOF
setxkbmap -device $remote_id -print | sed 's/\(xkb_symbols.*\)"/\1+custom(remote)"/' | xkbcomp -I/tmp/xkb -i $remote_id -synch - $DISPLAY 2>/dev/null
The script needs to be re-run every time the Bluetooth connection is re-established. Probably best to run it on reconnect as part of the Python code above.
So, that remaps the inputs for that Bluetooth button. Ok, but how do we get XF86Launch1
to launch a program?
It’s pretty easy to set keyboard shortcuts in the GUI- but how do we do it on the command line? Well, you can’t. There’s no way to tell a shell to run a program when a specific key has been pressed.
So, it’s back to Python and listening for the key to be pressed. Which I have no idea how to do!
If you know how to detect multimedia keys, please leave a comment or answer this StackOverflow question.
Or – let me know a better, more obvious way that I’m missing!
BlueTooth buttons are available on AliExpress and Amazon for UK customers.