AI-friendly CLI tool for device control via Bluetooth and more
Project description
AirCtl
AI-friendly CLI tool for Bluetooth device control.
AirCtl enables AI agents to control devices through a command-line interface, supporting both BLE (Bluetooth Low Energy) and BT Classic (BR/EDR) protocols.
Features
- AI-First Design: JSON output by default,
-hfor human-readable - Auto-Connect: Device commands automatically connect if needed
- Daemon Architecture: Background daemon maintains BLE connections for fast operations
- LLM-Friendly Snapshots:
--last Nreturns immediately instead of streaming - AI Hint Errors: All errors include actionable recovery hints for autonomous recovery
- Standard BLE Profiles: Built-in Heart Rate, Battery, Device Information
- Device Discovery: One-stop capabilities and info commands
- UUID Name Mapping: Human-readable names for standard BLE UUIDs
- BT Classic (BR/EDR): RFCOMM connection for Serial Port Profile devices
- Cross-Platform BLE: Windows, Linux, macOS | BT Classic: Windows, Linux
Quick Start
pip install airctl
# BLE: Scan and discover device
airctl ble scan -t 10
# Device info (one command, auto-connect)
airctl device quick-scan <address>
# Returns: name, manufacturer, battery, services, capabilities
# Read battery
airctl device battery <address>
# Monitor heart rate (auto-connect + subscribe + parse)
airctl device heart-rate <address>
# BT Classic: Serial port devices
airctl bt scan -t 10
airctl bt connect <address> --channel 1
Architecture
┌─────────────────────────────────────────────────────────────────┐
│ CLI Layer │
│ (airctl ble/bt/device/daemon/config commands) │
└───────────────────────────┬─────────────────────────────────────┘
│ JSON-RPC over IPC (BLE only)
▼
┌─────────────────────────────────────────────────────────────────┐
│ BLE Daemon (Background) │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────────┐ │
│ │ Connection │ │ Event │ │ Device Manager │ │
│ │ Manager │ │ Stream │ │ (multi-device support) │ │
│ └─────────────┘ └─────────────┘ └─────────────────────────┘ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ Task Manager │ │
│ │ (periodic read/write/scan operations) │ │
│ └─────────────────────────────────────────────────────────────┘ │
├─────────────────────────────────────────────────────────────────┤
│ Bleak Library (BLE) │
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ BT Classic (Direct System Calls) │
│ - Windows: WSA + BluetoothFind APIs │
│ - Linux: BlueZ via D-Bus / hctltool │
└─────────────────────────────────────────────────────────────────┘
| Category | Commands | Description |
|---|---|---|
| BLE Protocol | ble |
Scan, connect, GATT read/write/notify |
| Device Discovery | ble info, ble capabilities |
Structured device info and capabilities |
| BT Classic Protocol | bt |
Scan, SDP discovery, RFCOMM connect |
| Device Profiles | device |
Heart rate, battery, device info, quick-scan |
Commands Reference
Daemon Management
airctl daemon status # Check daemon status
airctl daemon start # Start daemon manually
airctl daemon start --log-level DEBUG
airctl daemon stop
airctl daemon restart
BLE Discovery (High-Level)
# One-stop device discovery: auto-connect + identity + battery + capabilities
airctl device quick-scan <address>
# Structured device info: identity, battery, subscriptions, MTU
airctl ble info <address>
# Full capabilities: all services/characteristics with names and operation flags
airctl ble capabilities <address>
BLE Operations
# Scanning
airctl ble scan [-t 10] [--service-uuids UUID1,UUID2]
airctl ble scan -n "Heart Rate"
# Connection
airctl ble connect <address> [-t 30] [-a alias]
airctl ble disconnect <address>
airctl ble list
# GATT Operations
airctl ble services <address>
airctl ble characteristics <address> [-s UUID]
airctl ble read <address> -u UUID [-f hex|base64|text]
airctl ble read <address> -H HANDLE
airctl ble write <address> -u UUID -d "hex:..."
airctl ble write <address> -H HANDLE -d "hex:..."
# Notifications
airctl ble notify subscribe <address> -u UUID
airctl ble notify unsubscribe <address> -u UUID
# Event Snapshots (LLM-friendly, returns immediately)
airctl ble events --last 5
airctl ble events --last 10 --address <addr>
airctl ble events --last 5 --type notification
# Background Tasks
airctl ble task start-read <address> -u UUID -i 5
airctl ble task start-write <address> -u UUID -d "hex:01" -i 10
airctl ble task start-scan -i 30 --timeout 5
airctl ble task list
airctl ble task stop <task_id>
airctl ble task result <task_id> --last 5
Device Profiles (High-Level, Auto-Connect)
All device commands automatically connect if not connected and disconnect when done.
# Battery: auto-connect + read + status
airctl device battery <address>
# {"battery_level": 85, "status": "normal"}
# Device info: identity (manufacturer, model, firmware)
airctl device device-info <address>
# {"identity": {"manufacturer_name": "Huawei", "model_number": "WATCH GT 4"}}
# Heart rate: auto-connect + subscribe + parse
airctl device heart-rate <address> [-d 30]
# {"latest_heart_rate": 72, "measurement_count": 5, "measurements": [...]}
# Full monitor: subscribe all notifications + periodic reads
airctl device monitor <address> [-d 30] [-i 5]
# {"subscribed_count": 3, "read_count": 12, "notifications": [...]}
# Identify device coordinator
airctl device identify <address>
# List coordinators and features
airctl device list-coordinators
airctl device features
airctl device kinds
BT Classic Operations
BT Classic uses direct system calls (not the daemon).
airctl bt scan [-t 10] # Scan for devices
airctl bt scan --timeout 10 -h # Human-readable output
airctl bt services <address> # List RFCOMM services
airctl bt connect <address> --channel 1 # Connect via RFCOMM
airctl bt connect <address> --service-uuid <UUID> # Auto-detect channel
airctl bt list # List connected devices
airctl bt disconnect <address>
When to Use BT Classic vs BLE
| Use Case | Protocol | Example |
|---|---|---|
| Device discovery | BLE | airctl device quick-scan <addr> |
| Standard profiles (heart rate, battery) | BLE | airctl device heart-rate <addr> |
| Raw GATT read/write/notify | BLE | airctl ble read/notify <addr> |
| RFCOMM serial communication | BT Classic | airctl bt connect <addr> |
| Device with SPP (Serial Port Profile) | BT Classic | HC-05/06 modules, GPS devices |
LLM-Friendly Patterns
Use snapshot queries (--last N) instead of streaming for AI agents:
# Snapshot: returns immediately
airctl ble events --last 5
# {"events": [...], "count": 5}
# Task results snapshot
airctl ble task start-read <addr> -u 2A19 -i 5
# {"task_id": "abc123", "type": "periodic_read"}
airctl ble task result abc123 --last 5
# {"task_id": "abc123", "results": [...], "count": 5}
Workflow for AI Agents:
airctl device quick-scan <addr>— One command to discover everythingairctl device heart-rate <addr>— One command for heart rate monitoring- If error: check
hintfield and follow the recovery steps
AI Hint Errors
All errors include an actionable hint field for autonomous recovery:
{
"error": true,
"message": "Device 24:81:C7:1B:3B:46 not connected",
"hint": "Run 'airctl ble connect 24:81:C7:1B:3B:46' first, then retry this command."
}
| Error | Hint |
|---|---|
| Device not connected | Run 'airctl ble connect <addr>' first |
| Device not found | Run 'airctl ble scan -t 10' to discover |
| Characteristic not found | Run 'airctl ble characteristics <addr>' |
| Insufficient authentication | Pair the device with your system first |
| Operation timed out | Restart daemon: airctl daemon stop && airctl daemon start |
| Lost connection to daemon | Run 'airctl daemon start' |
Configuration
airctl config list
airctl config alias set <address> <name>
airctl config alias list
airctl config alias remove <name>
airctl config preset list
airctl config preset set <name> --service UUID --char UUID
airctl config preset remove <name>
Data Formats
Input Formats
The -d / --data parameter supports:
| Format | Example | Description |
|---|---|---|
hex: |
hex:010203 |
Hexadecimal bytes |
text: |
text:hello |
UTF-8 text |
base64: |
base64:AQID |
Base64 encoded |
| (default) | 010203 |
Hexadecimal |
UUID Name Mapping
Standard BLE UUIDs are automatically mapped to human-readable names:
{
"services": [
{
"uuid": "0000180f-0000-1000-8000-00805f9b34fb",
"name": "Battery",
"characteristics": [
{"uuid": "00002a19-...", "name": "Battery Level", "readable": true}
]
}
]
}
Output Format
The -f / --format parameter controls output format:
| Format | Example | Description |
|---|---|---|
hex |
010203 |
Hexadecimal string |
base64 |
AQID |
Base64 encoded |
text |
Hello |
UTF-8 text |
Common GATT UUIDs
| UUID | Name | Description |
|---|---|---|
| 1800 | Generic Access | Device name and appearance |
| 180A | Device Information | Manufacturer, model, firmware |
| 180D | Heart Rate | Heart Rate service |
| 180F | Battery | Battery service |
| 2A00 | Device Name | BLE device name (read/write) |
| 2A19 | Battery Level | Battery percentage (read) |
| 2A24 | Model Number | Device model (read) |
| 2A26 | Firmware Revision | Firmware version (read) |
| 2A29 | Manufacturer Name | Device manufacturer (read) |
| 2A37 | Heart Rate Measurement | Heart rate data (notify) |
| 2A38 | Body Sensor Location | Sensor position (read) |
Known Issues
- Alias Naming: Use underscores (
my_sensor) not hyphens (my-sensor) in aliases - BLE Authentication: Some characteristics require pairing — pair the device with your system first
- BLE Connection Stability: Some devices (e.g., smartwatches) disconnect after ~12s idle. Device commands handle this automatically with auto-reconnect
- Short UUID Support: Both short (
2A19) and full (00002a19-...) UUID formats are supported - Heart Rate Notifications: Only sent when the device is actively measuring (e.g., during exercise)
Requirements
- Python 3.10+
- Bluetooth adapter
- Windows: Windows 10 v16299+ | Linux: BlueZ 5.55+ | macOS: macOS 10.15+
Installation
pip install airctl
Or from source:
git clone https://github.com/skinapi2025/AirCtl.git
cd AirCtl
pip install -e ".[dev]"
Testing
pytest tests/ -v
License
MIT License
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file airctl-0.1.6.tar.gz.
File metadata
- Download URL: airctl-0.1.6.tar.gz
- Upload date:
- Size: 72.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0698b355f4ecfcc6a7998bf73a1d12e0222619d2de5c8aa7869b21bbeada60fa
|
|
| MD5 |
5c1916dd57096158d10ecfa9b53201a1
|
|
| BLAKE2b-256 |
80d5cdb0d26ebb436086d4be7ad33017fa4823e063fbe1d341de2ff68337f3d7
|
File details
Details for the file airctl-0.1.6-py3-none-any.whl.
File metadata
- Download URL: airctl-0.1.6-py3-none-any.whl
- Upload date:
- Size: 89.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2dcf2f12758fbf2d127b1632947d6d2572fe342a00b5f5cbca75bcd5d9642da4
|
|
| MD5 |
cc35a84ab2537fbe56dac7be1728d589
|
|
| BLAKE2b-256 |
0201b340a18bd23b454e0a0727a2c2b391cfb8fa783247f600163948005a3837
|