To make a Linux machine act as a Bluetooth keyboard/mouse, we need to implement the Human Interface Device (HID) profile. The Bluetooth HID profile allows devices to advertise themselves as input devices and communicate using the HID protocol over Bluetooth Low Energy (BLE).
We'll be using these essential components:
- BlueZ (Linux Bluetooth stack)
- Python 3 with PyBluez and dbus-python
- Bluetooth controller with LE support
First, ensure your Bluetooth adapter supports BLE:
hciconfig
# Look for "LE" in features
sudo hciconfig hci0 up
sudo bluetoothctl
power on
discoverable on
pairable on
Create a Python script to advertise HID services:
import dbus
import dbus.service
import dbus.mainloop.glib
from gi.repository import GLib
class HIDService(dbus.service.Object):
def __init__(self):
bus = dbus.SystemBus()
path = "/org/bluez/example/hid"
bus_name = dbus.service.BusName("org.bluez", bus=bus)
dbus.service.Object.__init__(self, bus_name, path)
@dbus.service.method("org.bluez.HID1",
in_signature="", out_signature="")
def SendReport(self):
# Implement key/mouse report sending
pass
if __name__ == "__main__":
dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
service = HIDService()
GLib.MainLoop().run()
Create a D-Bus service configuration file at /etc/dbus-1/system.d/org.bluez.hid.conf:
<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
<busconfig>
<policy user="root">
<allow own="org.bluez"/>
<allow send_destination="org.bluez"/>
</policy>
</busconfig>
For keyboard functionality, we need to implement these GATT characteristics:
HID_SERVICE_UUID = "00001812-0000-1000-8000-00805f9b34fb"
HID_REPORT_MAP_UUID = "00002a4b-0000-1000-8000-00805f9b34fb"
HID_REPORT_UUID = "00002a4d-0000-1000-8000-00805f9b34fb"
HID_CONTROL_POINT_UUID = "00002a4c-0000-1000-8000-00805f9b34fb"
Here's how to send keyboard key presses:
def send_key(keycode):
report = [0xA1, 0x01, 0x00, 0x00, keycode, 0x00, 0x00, 0x00]
# Implement BLE report sending
pass
# Example: Send 'A' key
send_key(0x04) # HID keycode for 'A'
For systems where you want to bridge Bluetooth to local input events:
import uinput
device = uinput.Device([
uinput.KEY_A,
uinput.KEY_B,
# Add more keys as needed
])
# Then emulate key presses
device.emit_click(uinput.KEY_A)
1. Permission errors: Ensure your user is in the 'bluetooth' group
sudo usermod -aG bluetooth $USER
2. BLE not working: Check kernel modules
lsmod | grep bt
3. Connection drops: Adjust Bluetooth parameters
sudo hcitool cmd 0x08 0x0008 1e 00 1e 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Bluetooth Human Interface Device (HID) profile allows devices like keyboards and mice to communicate wirelessly. When we want to make a Linux machine emulate these peripherals, we're essentially implementing the HID host protocol stack.
First install these dependencies on Debian-based systems:
sudo apt update
sudo apt install -y bluez libbluetooth-dev libreadline-dev libdbus-1-dev \
libglib2.0-dev libudev-dev libical-dev libreadline-dev libdbus-glib-1-dev \
python3-dbus python3-gi python3-pip
We'll use uinput
kernel module to create virtual input devices:
sudo modprobe uinput
echo "uinput" | sudo tee -a /etc/modules
sudo usermod -aG input $(whoami)
Here's a complete Python implementation using PyBluez:
import bluetooth
from evdev import UInput, ecodes as e
# Create virtual devices
mouse_cap = {
e.EV_KEY: [e.BTN_LEFT, e.BTN_RIGHT],
e.EV_REL: [e.REL_X, e.REL_Y, e.REL_WHEEL]
}
keyboard_cap = {
e.EV_KEY: e.KEY_MAX,
e.EV_LED: [e.LED_NUML, e.LED_CAPSL, e.LED_SCROLLL]
}
ui_mouse = UInput(mouse_cap, name="Virtual Bluetooth Mouse")
ui_kbd = UInput(keyboard_cap, name="Virtual Bluetooth Keyboard")
# Bluetooth setup
server_sock = bluetooth.BluetoothSocket(bluetooth.L2CAP)
server_sock.bind(("", 0x11))
server_sock.listen(1)
We need to advertise our device as having HID capabilities:
from bluetooth.advertisement import Advertisement
from bluetooth.gatt import Service
class HIDAdvertisement(Advertisement):
def __init__(self, index):
Advertisement.__init__(self, index, "peripheral")
self.add_service_uuid("00001812-0000-1000-8000-00805f9b34fb") # HID
self.add_local_name("Linux HID Device")
self.include_tx_power = True
This code handles incoming connections and data parsing:
def handle_client(client_sock):
try:
while True:
data = client_sock.recv(1024)
if not data:
break
# Parse HID reports
if data[0] == 0xA1: # Input report
handle_input_report(data[1:])
except Exception as e:
print(f"Connection error: {e}")
finally:
client_sock.close()
To verify functionality:
- Pair your Linux machine with the target device
- Check that it appears as an input device
- Test basic keyboard/mouse functions
For specialized use cases, you might want to:
- Implement key mapping profiles
- Add gesture recognition for the mouse
- Create macros or automation scripts
The complete implementation would include additional error handling and potentially a configuration interface, but this provides the core functionality needed to emulate Bluetooth HID devices on Linux.