I was running Windows 11 on a laptop and using it mainly to run iaxRPT so I could connect to my AllStar node 604010 and stay on the N9IAA repeater (146.685 MHz, node 46655). I also had an AnyTone BP-02 Bluetooth PTT button that I used with it. Press the button to transmit, release to stop. Simple.
Windows kept doing Windows things. Updates at bad times. Stuff breaking. I got tired of it and moved to KDE Plasma on Linux. Most things just worked. iaxRPT runs fine under Wine. The Bluetooth PTT button did not.
The original script for the BP-02 is at f4inu/anytone-ptt. It uses a library called BLE_GATT to talk to the button over Bluetooth Low Energy. The problem is that BLE_GATT does not install cleanly on modern Linux. The library is essentially unmaintained and the underlying D-Bus interface it expects has moved on.
I cloned the repo, tried to install it, and it failed. So I started looking for a replacement.
The modern library for BLE on Linux (and Windows and Mac) is bleak. It is actively maintained, async-native in Python, and works cleanly with BlueZ on Linux without any of the D-Bus pain that killed BLE_GATT.
I had Claude help me rewrite the script using bleak. We started simple -- just connect and print what the device was actually sending. That is where the first real discovery happened.
The original script was looking for raw byte values to detect button presses. But the BP-02 does not send raw bytes. It sends ASCII strings:
ELET1 -- button pressedELET2 -- button releasedBATT followed by a character -- battery statusThat last one took some work to figure out. The battery character turns out to be a raw ASCII value where the character's decimal code is roughly the battery percentage. So BATT( is ASCII 40, meaning about 40%. BATTd is ASCII 100, meaning full. We figured this out by watching the messages change while the button was charging from nearly dead up to full.
One thing worth knowing if you are trying to do this yourself: this device does not pair with your computer in the traditional Bluetooth sense. It just broadcasts BLE advertisements. You scan for it, connect, subscribe to notifications on the right characteristic UUID (0000ff02-0000-1000-8000-00805f9b34fb), and the press and release messages come through.
This caused a lot of confusion early on because the connection status was hard to determine. The button was working even when the software thought it was not connected, because the device does not maintain a persistent connection the way a headset or keyboard would. Once we understood that, the logic got a lot simpler.
The rewritten ptt_gatt.py does a few things the original did not:
bleak instead of BLE_GATTELET-PTT so you do not have to know the MAC address ahead of timeconfig.ini once it finds the device, so subsequent runs connect directlyCtrl while the button is pressed and releases it when the button is released -- this is what iaxRPT uses for PTT when running under WineThere is also a PyQt5 system tray version that shows connection status and battery level as an icon in your taskbar. The tray icon goes red when you are transmitting and returns to normal when you release. The battery percentage shows in the tooltip. That version got messy to debug on Wayland -- there were some quirks with QWindow::requestActivate() -- but the core functionality works.
I want to be upfront that I did not write this code myself. I described what I wanted, pasted in error messages and terminal output, and Claude wrote the code. I tested it, told it what was wrong, and we went back and forth until it worked. I have done this with a number of projects now and it has become how I build things.
I am not a software developer. I do not write code. I described what I wanted, and Claude wrote it. Being able to describe a problem and iterate toward a working solution like that has genuinely changed what I am able to build.
The battery decoding was a good example. I pasted a long stream of BATT messages from the console while the button was charging, and asked what they meant. Claude noticed that the trailing character's ASCII value was tracking roughly with the charging progress and suggested the percentage mapping. Once we confirmed it matched reality, we added it to the status display.
The setup I have now: KDE Plasma on a 2-in-1 laptop, iaxRPT running under Wine connected to node 604010, and the AnyTone BP-02 running in the system tray. Press and hold the BT PTT button to talk, release to stop.
It works better than it did on Windows because I never have to think about it. The script starts on login and sits in the tray. If the button goes to sleep and reconnects, the script picks it back up automatically.
The code is on GitHub at ki9ng/anytone-ptt. If you have an AnyTone BP-02 and you are running Linux, it should work for you. Put your MAC address in config.ini or let the script find it on its own.