Metadata-Version: 2.4
Name: airctl
Version: 0.1.6
Summary: AI-friendly CLI tool for device control via Bluetooth and more
Author: AirCtl Team
License-Expression: MIT
License-File: LICENSE
Classifier: Development Status :: 3 - Alpha
Classifier: Framework :: AsyncIO
Classifier: Operating System :: MacOS :: MacOS X
Classifier: Operating System :: Microsoft :: Windows :: Windows 10
Classifier: Operating System :: POSIX :: Linux
Classifier: Topic :: System :: Hardware :: Hardware Drivers
Classifier: Topic :: Terminals
Requires-Python: >=3.10
Requires-Dist: bleak>=0.21.0
Requires-Dist: orjson>=3.9.0
Requires-Dist: pydantic>=2.0.0
Requires-Dist: pywin32>=306; platform_system == 'Windows'
Requires-Dist: pyyaml>=6.0
Requires-Dist: rich>=13.0.0
Requires-Dist: typer>=0.12.0
Requires-Dist: typing-extensions>=4.7.0
Provides-Extra: dev
Requires-Dist: black>=24.0.0; extra == 'dev'
Requires-Dist: isort>=5.13.0; extra == 'dev'
Requires-Dist: mypy>=1.8.0; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.23.0; extra == 'dev'
Requires-Dist: pytest-cov>=4.1.0; extra == 'dev'
Requires-Dist: pytest>=8.0.0; extra == 'dev'
Requires-Dist: ruff>=0.2.0; extra == 'dev'
Description-Content-Type: text/markdown

# 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, `-h` for human-readable
- **Auto-Connect**: Device commands automatically connect if needed
- **Daemon Architecture**: Background daemon maintains BLE connections for fast operations
- **LLM-Friendly Snapshots**: `--last N` returns 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

```bash
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

```bash
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)

```bash
# 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

```bash
# 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.

```bash
# 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).

```bash
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:

```bash
# 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:**
1. `airctl device quick-scan <addr>` — One command to discover everything
2. `airctl device heart-rate <addr>` — One command for heart rate monitoring
3. If error: check `hint` field and follow the recovery steps

### AI Hint Errors

All errors include an actionable `hint` field for autonomous recovery:

```json
{
  "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

```bash
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:

```json
{
  "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

```bash
pip install airctl
```

Or from source:

```bash
git clone https://github.com/skinapi2025/AirCtl.git
cd AirCtl
pip install -e ".[dev]"
```

## Testing

```bash
pytest tests/ -v
```

## License

MIT License
