Engwe N1 Air MCP Server
by SafaAslan
README.md
# š² Engwe N1 Air ā BLE Reverse Engineering + Claude MCP Server
> Reverse engineered the Engwe N1 Air e-bike's Bluetooth protocol and built a Claude AI integration to control it from your Mac.



## What This Does
- š **Unlock speed limit** via Bluetooth (up to 40 km/h)
- š **Lock/unlock** the bike remotely via IoT API
- š **Find your bike** (sound + light alarm)
- š **Battery, mileage, cadence** real-time stats
- š **Geo-fence** management
- šŖ **PAS level** control via Bluetooth
- š¤ **Claude AI integration** ā control your bike by talking to Claude
- š„ļø **Mac menubar app** ā one-click access to all features
---
## How It Was Built
### Step 1 ā API Discovery (Charles Proxy)
Captured network traffic from the Engwe iOS app using Charles Proxy. Found the main API endpoint at `app-api.engweapp.com` with JWT authentication.
### Step 2 ā APK Decompile (JADX)
Decompiled the Android APK using JADX to find all internal API endpoints and BLE command codes.
```bash
jadx -d engwe-source ENGWE_2.7.2_APKPure.xapk
grep -r "iot/device" engwe-source --include="*.java" | grep -o '"[^"]*iot/device[^"]*"' | sort -u
```
### Step 3 ā BLE Protocol Reverse Engineering
Found the complete BLE protocol in `ProtocolUtil.java` and `BLEBikeCommand.java`.
**V2 Protocol Format:**
```
456E + CMD(4) + LEN(4) + DATA + CRC16(4) + 7765
```
**BLE UUIDs (from BluetoothRegulate.java):**
```
Service: 00001820-0000-1000-8000-00805f9b34fb
Write: 00001822-0000-1000-8000-00805f9b34fb
Notify: 00001821-0000-1000-8000-00805f9b34fb
```
---
## BLE Command Reference
All commands use V2 protocol format: `456E + CMD + LEN + DATA + CRC16 + 7765`
| Command | Hex | Description |
|---------|-----|-------------|
| Speed Limit | `2A65` | 0x00=0, 0x10=10, 0x20=20, 0x30=30, 0x40=40 km/h |
| PAS Level | `010A` | 0x00ā0x05 |
| Motor Current Limit | `2A66` | Under investigation |
| Cruise Control Set | `0128` | Under investigation |
| Wheel Diameter | `08C3` | Under investigation |
| Factory Reset | `8049` | ā ļø Irreversible |
| Lock State | `0108` | Query lock |
| Power On/Off | `0107` | 01=on, 02=off |
| Auto Shutdown Time | `1000` | Minutes |
**Speed Limit Example (40 km/h):**
```python
# Index 4 * 16 = 0x40
command = "456E2A650001406A237765"
```
**CRC16 Calculation (ModBus):**
```python
def crc16(hex_str):
data = bytes.fromhex(hex_str)
crc = 0xFFFF
for byte in data:
crc ^= byte
for _ in range(8):
crc = (crc >> 1) ^ 0xA001 if crc & 1 else crc >> 1
return format(crc, "04X")
# CRC input = CMD + LEN + DATA
# Example: crc16("2A65" + "0001" + "40") = "6A23"
```
---
## IoT API Endpoints
Base URL: `https://app-api.engweapp.com`
| Endpoint | Description |
|----------|-------------|
| `/api/app/iot/device/lockSwitch` | Lock/unlock bike |
| `/api/app/iot/device/findVehicle` | Sound + light alarm |
| `/api/app/iot/device/vibrationAlarmSwitch` | Vibration alarm + signal |
| `/api/app/iot/device/fenceSwitch` | Geo-fence on/off |
| `/api/app/iot/device/getFenceList` | List geo-fences |
| `/api/app/iot/device/bleSenselessSwitch` | BLE auto-unlock |
| `/api/app/iot/device/headlightSwitch` | Headlight + realtime PAS/charge |
| `/api/app/iot/device/attr/newest` | Battery SOC/SOH, mileage, cadence |
| `/api/app/iot/device/version` | IoT/BT/4G firmware versions |
| `/api/app/iot/device/attr/newest` | Real-time attributes |
| `/api/app/paypal/syDetail` | IoT subscription info |
| `/api/app/ride-data/index` | Weekly ride statistics |
---
## Installation
### Requirements
- Node.js 18+
- Python 3.9+
- Mac (for menubar app)
```bash
git clone https://github.com/SafaAslan/engwe-n1air-mcp
cd engwe-n1air-mcp
npm install
pip3 install bleak
```
### Get Your Token
Use Charles Proxy to capture traffic from the Engwe iOS/Android app. Find the `Authorization` header in any request to `app-api.engweapp.com`.
### Configure Claude Desktop
`~/Library/Application Support/Claude/claude_desktop_config.json`:
```json
{
"mcpServers": {
"engwe-n1air": {
"command": "node",
"args": ["/path/to/engwe-n1air-mcp/index.js"],
"env": {
"ENGWE_IMEI": "YOUR_IMEI",
"ENGWE_BLE_MAC": "YOUR_BLE_MAC",
"ENGWE_TOKEN": "YOUR_JWT_TOKEN"
}
}
}
}
```
### Find Your IMEI & BLE MAC
```bash
# IMEI ā visible in Engwe app under bike settings
# BLE MAC ā run while bike is on:
python3 -c "
import asyncio
from bleak import BleakScanner
async def scan():
devices = await BleakScanner.discover(timeout=10)
for d in devices:
if d.name and 'ENGWE' in d.name.upper():
print(d.address, d.name)
asyncio.run(scan())
"
```
---
## Usage
### Claude AI (via MCP)
Once configured, just talk to Claude:
- *"Lock my bike"*
- *"What's my battery level?"*
- *"Set speed limit to 35 km/h"*
- *"Show my ride stats for this week"*
### Menubar App (Mac)
```bash
npm install electron
./node_modules/.bin/electron tray.js
```
### Auto-start on Boot
```bash
chmod +x setup_autostart.sh
bash setup_autostart.sh
```
### Speed Limit Script (standalone)
```bash
python3 ble_speed3.py
```
---
## Project Structure
```
engwe-n1air-mcp/
āāā index.js # Claude MCP server (18 tools)
āāā tray.js # Mac menubar app
āāā ble_speed3.py # Standalone speed limit script
āāā ble_scan2.py # BLE device scanner
āāā setup_autostart.sh # Mac autostart setup
āāā README.md
```
---
## ā ļø Disclaimer
Unlocking the speed limit above 25 km/h is **illegal on public roads in the EU**. This project is for educational and off-road/private use only. Use at your own risk. This project is not affiliated with Engwe.
---
## Contributing
Pull requests welcome! Especially interested in:
- Testing on other Engwe models (P275 SE, M20, etc.)
- Cruise control command (`0128`)
- Motor current limit (`2A66`)
- Android version of menubar app
---
## License
MIT
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessSyncing