Bluetooth Remote Control Library
A robust Python library for handling Bluetooth remote controls that go to sleep, combining udev device monitoring with evdev event handling for seamless reconnection and event processing.
Features
- 🔄 Automatic Reconnection: Handles Bluetooth remotes that go to sleep and wake up
- 📱 Multiple Device Support: Manage multiple remotes simultaneously with individual configurations
- ⚡ Real-time Detection: Uses udev for instant device discovery (no polling)
- 🎯 Flexible Filtering: Filter devices by name, vendor ID, product ID, or bus type
- 🔧 Per-Device Handlers: Different event handlers and settings for each remote type
- 🏗️ Async/Await: Built with modern Python async patterns
- 🧵 Thread-Safe: Proper threading and cleanup
- 🔍 Device Discovery: Built-in tools to find your remote's details
Installation
Using uv (recommended)
uv add bluetooth-remote-lib
Using pip
pip install bluetooth-remote-lib
Quick Start
import asyncio
from bluetooth_remote_lib import RemoteManager, RemoteEventHandler, KeyEvent
class MyHandler(RemoteEventHandler):
async def on_key_event(self, event: KeyEvent):
print(f"Key pressed: {event.keycode}")
async def main():
handler = MyHandler()
manager = RemoteManager(
event_handler=handler,
device_filters={'name': 'remote'} # Match devices with 'remote' in name
)
async with manager.managed_context():
await asyncio.sleep(60) # Run for 60 seconds
asyncio.run(main())
Advanced Usage - Multiple Remotes
import asyncio
from bluetooth_remote_lib import (
RemoteManager, RemoteEventHandler, RemoteConfig,
KeyEvent, KeyEventType, DeviceInfo
)
# Define handlers for specific remotes
async def handle_fire_tv(event: KeyEvent):
if event.event_type == KeyEventType.KEY_DOWN:
print(f"🔥 Fire TV: {event.keycode}")
async def handle_roku(event: KeyEvent):
if event.event_type == KeyEventType.KEY_DOWN:
print(f"📺 Roku: {event.keycode}")
class MainHandler(RemoteEventHandler):
async def on_key_event(self, event: KeyEvent):
print(f"Generic remote: {event.keycode}")
async def on_device_connected(self, device_info: DeviceInfo):
print(f"Connected: {device_info.name}")
# Configure multiple remotes with individual settings
remote_configs = [
RemoteConfig(
name="Fire TV Remote",
filters={
'name': 'fire tv',
'vendor_id': 0x1949, # Amazon
},
handler=handle_fire_tv,
reconnect_delay=1.5, # Custom timing
scan_interval=3.0
),
RemoteConfig(
name="Roku Remote",
filters={
'name': 'roku',
'vendor_id': 0x0B05, # Roku
},
handler=handle_roku,
reconnect_delay=2.0
)
]
async def main():
manager = RemoteManager(
event_handler=MainHandler(),
remote_configs=remote_configs
)
async with manager.managed_context():
while True:
await asyncio.sleep(1)
asyncio.run(main())
Device Discovery
Find your remote's vendor/product IDs:
# Discover all input devices
bt-remote-discover
# Or use the module directly
python -m bluetooth_remote_lib discover
# Find specific devices
bt-remote find --name "fire tv"
bt-remote find --vendor-id 0x1949
Example output:
Device: Amazon Fire TV Remote
Path: /dev/input/event5
Vendor ID: 0x1949 (6473)
Product ID: 0x0404 (1028)
Key capabilities: 15 keys
Keys: KEY_UP, KEY_DOWN, KEY_LEFT, KEY_RIGHT, KEY_ENTER...
Configuration Options
RemoteConfig Parameters
- name: Friendly name for the remote configuration
- filters: Dictionary of device matching criteria:
name: Substring to match in device name (case insensitive)vendor_id: Vendor ID (integer)product_id: Product ID (integer)bus_type: Bus type (integer)
- handler: Optional async function to handle events from this remote
- reconnect_delay: Custom delay before reconnection attempts (seconds)
- scan_interval: Custom interval between reconnection scans (seconds)
Manager Parameters
- event_handler: Main event handler (RemoteEventHandler subclass)
- remote_configs: List of RemoteConfig objects
- device_filters: Legacy single filter dict (use remote_configs instead)
- reconnect_delay: Default reconnection delay (2.0 seconds)
- scan_interval: Default scan interval (5.0 seconds)
- log_level: Logging level (logging.INFO)
Event Handling
Key Events
class MyHandler(RemoteEventHandler):
async def on_key_event(self, event: KeyEvent):
# event.keycode: 'KEY_UP', 'KEY_HOME', etc.
# event.event_type: KEY_DOWN, KEY_UP, KEY_HOLD
# event.timestamp: Event timestamp
# event.device_name: Name of the device
# event.raw_event: Original evdev event
if event.event_type == KeyEventType.KEY_DOWN:
if event.keycode == 'KEY_HOME':
print("Home button pressed!")
Device Events
class MyHandler(RemoteEventHandler):
async def on_device_connected(self, device_info: DeviceInfo):
print(f"Device connected: {device_info.name}")
print(f"Vendor: 0x{device_info.vendor_id:04x}")
async def on_device_disconnected(self, device_info: DeviceInfo):
print(f"Device disconnected: {device_info.name}")
async def on_device_reconnecting(self, device_info: DeviceInfo):
print(f"Reconnecting: {device_info.name}")
Requirements
- Linux: This library uses Linux-specific evdev and udev systems
- Python 3.8+: Modern async/await support
- Permissions: May require user to be in
inputgroup or run with appropriate permissions
System Setup
# Add user to input group (recommended)
sudo usermod -a -G input $USER
# Then log out and back in
# Or install udev rule for your specific devices
echo 'SUBSYSTEM=="input", GROUP="input", MODE="0664"' | sudo tee /etc/udev/rules.d/99-input.rules
sudo udevadm control --reload-rules
Examples
The examples/ directory contains:
basic_usage.py: Simple single remote exampleadvanced_usage.py: Multiple remotes with individual configurations
Run examples:
python examples/basic_usage.py
python examples/advanced_usage.py
Contributing
Contributions welcome! Please feel free to submit a Pull Request.
- Fork the repository
- Create a feature branch
- Make your changes
- Add tests if applicable
- Submit a pull request
License
This project is licensed under the MIT License - see the LICENSE file for details.
Changelog
See CHANGELOG.md for version history and changes.
Support
- Issues: GitHub Issues
- Discussions: GitHub Discussions
Related Projects
Release files for ev-remote-lib 0.8.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| ev_remote_lib-0.8.0.tar.gz | 6.8 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| ev_remote_lib-0.8.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size:14.5 kB
Release files / ev_remote_lib-0.8.0.tar.gz
| Download URL | ev_remote_lib-0.8.0.tar.gz |
|---|---|
| Size | 6.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
31140c5562612ff57a8bc30ca85a52352cc18acbf964c1574d7fa48308a977f6
|
|
BLAKE2b-256 checksum How to use checksums |
cae9594c712cce09b3c28276c871cc161ed787d8b505e455a0b75adbf092c2c8
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.9.7
|
Release files / ev_remote_lib-0.8.0-py3-none-any.whl
| Download URL | ev_remote_lib-0.8.0-py3-none-any.whl |
|---|---|
| Size | 7.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
8fa610f5734246db120a6142365e563e1addf36b8d8a72180a9669159b74879c
|
|
BLAKE2b-256 checksum How to use checksums |
a253b335e1a23fdb14942200ea9c50f5f638b2830a94038ccee2ec56e297be12
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.9.7
|