b00mhauercode/magrgb-controller
GitHub: b00mhauercode/magrgb-controller
为 Secretlab Magnus XL RGB 灯带逆向实现了 HAP-BLE 与 Thread 双协议控制器,并提供 WLED 桥接以兼容 SignalRGB。
Stars: 0 | Forks: 0
# Secretlab MAGRGB 控制器
包含两个网桥 — 选择适合您设置的一个:
| 网桥 | 传输方式 | 速度 | 要求 |
|---|---|---|---|
| `magnus_wled_bridge_thread.py` | Thread/UDP over IPv6 | ~20 Hz | Nanoleaf Desktop app(启动时运行一次) |
| `magnus_wled_bridge_hapble.py` | HAP-BLE (encrypted GATT) | ~10 Hz | HAP 配对 (`pair.py`) |
**推荐:Thread/UDP** — 更快,无需 BLE 配对,且 Nanoleaf Desktop app 只需运行一次即可启用流模式。
## 目录
1. [设备识别](#device-identification)
2. [逆向工程之旅](#the-reverse-engineering-journey)
3. [协议规范](#protocol-specification)
4. [初始设置 — Thread/UDP(推荐)](#initial-setup--threadudp-recommended)
5. [初始设置 — HAP-BLE(备选)](#initial-setup--hap-ble-fallback)
6. [脚本参考](#script-reference)
7. [SignalRGB 集成](#signalrgb-integration)
8. [架构](#architecture)
9. [本仓库中的文件](#files-in-this-repo)
10. [未来工作](#future-work)
11. [故障排除](#troubleshooting)
12. [法律声明](#legal)
## 设备识别
| 字段 | 值 |
|---|---|
| 产品 | Secretlab Magnus XL RGB Desk Strip |
| OEM | Nanoleaf (作为 MAGRGB 销售) |
| BLE 广播名称 | `Secretlab MAGRGB XXBJ` |
| 协议 | HAP-BLE (Apple HomeKit Accessory Protocol over Bluetooth LE) |
| 电源 | USB |
### BLE 广播
恢复出厂设置后,设备在**两个同时存在的地址**上进行广播 — 每个协议一个:
| 地址 | 制造商 ID | 协议 | 用途 |
|---|---|---|---|
| `XX:XX:XX:XX:XX:XX` | `76` (Apple) | HAP-BLE | HomeKit 配对 + 控制 |
| `YY:YY:YY:YY:YY:YY` | `2059` (Nanoleaf) | LTPDU | Nanoleaf app + Thread 控制 |
### HAP-BLE GATT 服务
| 服务 UUID | 用途 |
|---|---|
| `00001800-0000-1000-8000-00805f9b34fb` | Generic Access (设备名称) |
| `0000003e-0000-1000-8000-0026bb765291` | HAP Accessory Information |
| `00000055-0000-1000-8000-0026bb765291` | HAP Pairing (Pair-Setup `0x4C`, Pair-Verify `0x4E`) |
| `00000043-0000-1000-8000-0026bb765291` | **HAP Lightbulb** — 颜色控制位于此 |
| `00000701-0000-1000-8000-0026bb765291` | Nanoleaf 场景/效果服务 |
| `6d2ae1c4-9aea-11ea-bb37-0242ac130002` | Nanoleaf LTPDU 传输 (加密) |
### HAP Lightbulb 特征 IID
| 特征 | HAP UUID | IID |
|---|---|---|
| 开 / 关 | `00000025-...-0026bb765291` | 51 |
| 亮度 (0–100) | `00000008-...-0026bb765291` | 52 |
| 色调 (0–360°) | `00000013-...-0026bb765291` | 53 |
| 饱和度 (0–100%) | `0000002f-...-0026bb765291` | 54 |
## 逆向工程之旅
### 阶段 1 — 识别协议
灯带通过 Android/iOS 上的 **Nanoleaf app** 控制,这意味着 BLE 流量会告诉我们协议。第一步是捕获 Android HCI snoop 日志。
**snoop 日志显示的内容:**
日志捕获了发送到句柄 `0x008e` 和 `0x0090` 的**加密变长数据包** — 这是发送到 MAGRGB 的实际 Nanoleaf app 流量,使用 X25519 + AES-CTR(Nanoleaf 的 LTPDU 协议)或 HAP-BLE ChaCha20-Poly1305 加密。由于密钥是每次会话临时的,因此无法仅从捕获中解密这些数据包。
### 阶段 2 — 理解双协议架构
GATT 服务枚举(参见 `discover_services.py`)显示设备同时暴露了 HAP-BLE 和 Nanoleaf LTPDU 服务树:
- HAP 服务(`0026bb765291` UUID 命名空间)→ Apple HomeKit 协议
- LTPDU 服务(`6d2ae1c4-9aea-11ea-bb37-0242ac130002`)→ Nanoleaf 基于 Thread/CoAP 的专有协议
设备还使用**两个制造商 ID** 进行广播 — Apple 的 (76) 用于 HomeKit 发现,Nanoleaf 的 (2059) 用于 Nanoleaf app — 在两个独立的轮换 BLE 地址上。
这解释了为什么 aiohomekit 的 BLE 扫描器(过滤 Apple 制造商 ID 76)在正常运行期间找不到设备:它已配对并广播加密的通知广播,而不是可配对的广播。
### 阶段 3 — HAP-BLE 配对
恢复设备出厂设置后,HAP 地址广播标准的未加密 HomeKit 广播(制造商数据以 `0x06` 开头)。aiohomekit 的正常发现仍然找不到它,因为广播使用的是 **Nanoleaf 的广播地址**,而不是 Apple 的。
**解决方案:** 完全绕过 aiohomekit 基于扫描器的发现。使用 `BleakScanner.find_device_by_address()` 直接获取 BLEDevice,然后直接调用 aiohomekit 的底层 `drive_pairing_state_machine()` 与 HAP Pair-Setup 特征(`0x4C`)。
SRP 配对使用设备上打印的 **8 位 HomeKit 设置代码**,格式为 `XXX-XX-XXX`。格式很重要 — aiohomekit 的 `check_pin_format` 会拒绝任何其他格式。
**配对流程:**
```
1. BleakScanner.find_device_by_address(HAP_MAC)
2. AIOHomeKitBleakClient.connect()
3. drive_pairing_state_machine(PAIR_SETUP, perform_pair_setup_part1())
→ device returns SRP salt + public key
4. drive_pairing_state_machine(PAIR_SETUP, perform_pair_setup_part2(pin, uuid, salt, pubkey))
→ device returns long-term key pair (AccessoryLTPK, iOSDeviceLTSK, etc.)
5. Save pairing_data to pairing.json
```
### 阶段 4 — 特征控制
有了有效的配对,HAP-BLE 控制通过 aiohomekit 的 `BlePairing.put_characteristics()` 进行。一个兼容性问题:aiohomekit 的 `BlePairing` 类期望由完整的控制器/扫描器基础设施驱动,如果广播回调触发时 `_accessories_state` 为 `None`,则会崩溃。
**修复:** 在加载配对后立即使用空的 `AccessoriesState(Accessories(), 0, None, 0)` 预初始化 `_accessories_state`。
颜色以 **HSV 空间**(而非 RGB)通信,因为 HAP 的 Lightbulb 服务使用色调 + 饱和度 + 亮度作为独立的特征。来自 SignalRGB 的 WLED DRGB 流的 RGB 值使用 Python 的 `colorsys.rgb_to_hsv()` 进行转换。
## 协议规范
### HAP-BLE 会话
HAP-BLE 在配对后使用标准的 Pair-Verify 握手(X25519 + Ed25519)建立 ChaCha20-Poly1305 加密会话。aiohomekit 透明地处理此过程。
### 特征写入格式
控制数据包通过 aiohomekit 的 `put_characteristics([(aid, iid, value)])` 发送:
| 参数 | 值 |
|---|---|
| AID | `1` (BLE 外设始终为 1) |
| IID_ON | `51` — 布尔值 |
| IID_BRI | `52` — 整数 0–100 |
| IID_HUE | `53` — 整数 0–360 |
| IID_SAT | `54` — 整数 0–100 |
### 颜色转换
```
import colorsys
def rgb_to_hapsv(r, g, b):
h, s, v = colorsys.rgb_to_hsv(r / 255, g / 255, b / 255)
return round(h * 360), round(s * 100), round(v * 100)
```
### 关闭 vs 开启
HAP 有一个独立的开关特征 (IID 51)。发送 RGB `(0,0,0)` **不会**关闭灯 — 您必须向 IID_ON 写入 `False`。网桥会自动处理此问题。
## 初始设置 — Thread/UDP(推荐)
### 要求
- Python 3.9+
- Windows 10/11
- 已安装并运行 **Nanoleaf Desktop app**(启用流模式时需要一次)
- 设备可通过 Thread IPv6 访问(它通过 Apple TV 边界路由器连接)
```
pip install -r requirements.txt
```
### 步骤 1 — 首先通过 Nanoleaf 移动应用配对
1. 将灯带恢复出厂设置(按住重置约 10 秒直到灯闪烁)
2. 在开机后的 15 分钟内,在 iOS 或 Android 上打开 **Nanoleaf 移动应用** → 添加设备 → 按照蓝牙配对流程操作
3. 移动应用通过您的 Apple TV(或其他 Thread 边界路由器)将设备配置到 Thread
4. 一旦移动应用显示设备已连接,Desktop 应用和网桥就能访问它
### 步骤 2 — 获取您的设备信息
打开 Wireshark 并在 Nanoleaf Desktop 应用打开时捕获回环适配器(`127.0.0.1`)。过滤器:
```
tcp.port == 15765 and http
```
查找 `POST /essentials/state` 请求 — JSON 主体包含您设备的 `id`、`ip` (Thread IPv6)、`token`、`port` 和 `eui64`。
或者 — 如果这些值在之前的会话中已设置且设备此后未恢复出厂设置,则它们已存在于 `config_local.py` 中。
### 步骤 3 — 更新 config_local.py
```
DEVICE_MAC = "XX:XX:XX:XX:XX:XX" # BLE MAC (only needed for HAP bridge)
DEVICE_INFO = {
"controlVersion": 2,
"defaultName": "Secretlab MAGRGB XXBJ",
"id": "NXXXXXXXXX",
"ip": "fdXX:XXXX:XXXX:0:XXXX:XXXX:XXXX:XXXX", # Thread IPv6
"model": "NL62",
"port": 5683,
"token": "XXXXXXXXXXXXXXXX",
"eui64": "XXXXXXXXXXXXXXXX",
}
```
### 步骤 4 — 验证连接性
```
ping -6
```
应通过 Apple TV 边界路由器在约 1–30ms 内回复。
### 步骤 5 — 运行网桥
首先打开 Nanoleaf Desktop 应用(仅在首次启动时需要),然后:
```
python magnus_wled_bridge_thread.py
```
输出:
```
WLED UDP on 127.0.0.2:21325
WLED HTTP on :80
Enabling stream control via Nanoleaf Desktop...
OK: b''
Streaming to [fdc5:...]:60222
Ready.
```
首次成功运行后,设备将保持流模式 — 您可以关闭 Nanoleaf Desktop 应用,它将继续工作,直到设备断电。
## 初始设置 — HAP-BLE(备选)
如果 Thread 路径不可用(无 Apple TV,或设备未在 Thread 上),请使用此方法。
### 要求
- Python 3.9+
- Windows 10/11 配备 **Bluetooth LE 适配器**
- 设备恢复出厂设置(按住重置按钮约 10 秒直到灯闪烁)
### 步骤 1 — 查找 HAP MAC
```
python scan.py
python scan_adv.py
```
查找 `Secretlab MAGRGB XXBJ`。HAP 地址在广播数据中包含 `Manufacturer: {76: '06...'}`。
在 `config_local.py` 中设置它:
```
DEVICE_MAC = "XX:XX:XX:XX:XX:XX" # Apple manufacturer ID address
```
### 步骤 2 — 配对(一次性)
```
python pair.py XXX-XX-XXX
```
输入设备标签上的 8 位 HomeKit 设置代码。这将创建 `pairing.json` — 切勿提交它。
### 步骤 3 — 测试
```
python test.py
```
灯带应循环:红 → 绿 → 蓝 → 白 50% → 关。
### 步骤 4 — 运行网桥
```
python magnus_wled_bridge_hapble.py
```
## 脚本参考
| 脚本 | 用途 | 使用方法 |
|---|---|---|
| `magnus_wled_bridge_thread.py` | **推荐网桥** — Thread/UDP, ~20 Hz | `python magnus_wled_bridge_thread.py` |
| `magnus_wled_bridge_hapble.py` | 备选网桥 — HAP-BLE, ~10 Hz, 需配对 | `python magnus_wled_bridge_hapble.py` |
| `pair.py` | 一次性 HAP-BLE 配对,保存 `pairing.json` | `python pair.py XXX-XX-XXX` |
| `test.py` | HAP-BLE 颜色循环测试 — 红/绿/蓝/白/关 | `python test.py` |
| `scan.py` | 列出所有附近的 BLE 设备 | `python scan.py` |
| `scan_adv.py` | 显示 MAGRGB 地址的原始广播数据 | `python scan_adv.py` |
| `discover_services.py` | 枚举 GATT 服务和特征 | `python discover_services.py` |
## SignalRGB 集成
灯带作为 **WLED 设备** 暴露给 SignalRGB — 无需自定义插件。
### 设置
**步骤 1 — 启动网桥(以管理员身份运行以使用端口 80)**
Thread/UDP(推荐):
```
python magnus_wled_bridge_thread.py
```
HAP-BLE(备选,需先配对):
```
python magnus_wled_bridge_hapble.py
```
**步骤 2 在 SignalRGB 中添加**
1. 打开 SignalRGB → **Home → Lighting Services → WLED**
2. 在 "Discover WLED device by IP" 中输入 `127.0.0.2` 并按回车
3. SignalRGB 调用端口 80 上的 `/json/info`,获取返回的 `"brand": "WLED"` 并添加设备
4. 点击 **Link** — **"Magnus RGB Strip"** 现已位于您的画布上
**步骤 3 — 分配效果**
拖动画布上的 Magnus RGB Strip 区块并分配任意效果。
### Windows 自动启动
1. 打开 **任务计划程序** → **创建任务**
2. **常规:** 名称 `Magnus RGB Bridge`,勾选 **使用最高权限运行**
3. **触发器:** 启动时,延迟 30 秒
4. **操作:** 启动 `python`,参数 `C:\path\to\MAGRGB-controller\magnus_wled_bridge_thread.py`,起始于 `C:\path\to\MAGRGB-controller`
5. 保存
## 架构
### Thread/UDP(推荐)
```
╔══════════════════════════════════════════════════════════════════╗
║ YOUR PC (127.0.0.2) ║
║ ║
║ ┌──────────┐ ┌───────────────────────────────────────────┐ ║
║ │SignalRGB │ │ magnus_wled_bridge_thread.py │ ║
║ │ │──▶│ HTTP :80 (WLED discovery) │ ║
║ │ │ │ UDP :21325 (DRGB color stream) │ ║
║ └──────────┘ │ averages zones → single RGB │ ║
║ │ UDP socket → [Thread IPv6]:60222 ~20 Hz│ ║
║ └───────────────────┬───────────────────────┘ ║
╚══════════════════════════════════════╪═══════════════════════════╝
│ IPv6/UDP :60222
┌───────▼────────┐
│ Apple TV │
│ Thread Border │
│ Router │
└───────┬────────┘
│ Thread radio
┌───────▼────────┐
│ Secretlab │
│ MAGRGB (NL62) │
└────────────────┘
```
### HAP-BLE(备选)
```
╔══════════════════════════════════════════════════════════════════╗
║ YOUR PC (127.0.0.2) ║
║ ║
║ ┌──────────┐ ┌───────────────────────────────────────────┐ ║
║ │SignalRGB │ │ magnus_wled_bridge_hapble.py │ ║
║ │ │──▶│ HTTP :80 / UDP :21325 │ ║
║ └──────────┘ │ RGB→HSV conversion │ ║
║ │ aiohomekit put_characteristics ~10 Hz │ ║
║ └───────────────────┬───────────────────────┘ ║
╚══════════════════════════════════════╪═══════════════════════════╝
│ BLE (ChaCha20-Poly1305)
┌───────▼────────┐
│ Secretlab │
│ MAGRGB │
│ HAP-BLE GATT │
└────────────────┘
```
## 本仓库中的文件
| 文件 | 用途 |
|---|---|
| `magnus_wled_bridge_thread.py` | **推荐网桥** — Thread/UDP 到设备, ~20 Hz, 无需 BLE |
| `magnus_wled_bridge_hapble.py` | **备选网桥** — HAP-BLE via aiohomekit, ~10 Hz, 需配对 |
| `pair.py` | 一次性 HAP-BLE SRP 配对,保存 `pairing.json` |
| `test.py` | HAP-BLE 颜色循环测试 — 红/绿/蓝/白/关 |
| `scan.py` | BLE 扫描器 — 列出所有附近的设备及其名称和 MAC |
| `scan_adv.py` | 广播扫描器 — 显示 MAGRGB 地址的原始制造商数据 |
| `discover_services.py` | GATT 服务枚举器 — 列出所有服务和特征 |
| `compat.py` | HAP-BLE 脚本使用的 Bleak 2.x 兼容性垫片 |
| `config.py` | 配置模板 — 复制到 `config_local.py` |
| `config_local.py` | 您的设备配置 (MAC + Thread DEVICE_INFO) — gitignored |
| `requirements.txt` | Python 依赖项 |
| `pairing.json` | 您的长期 HAP 密钥对 — 由 `pair.py` 生成,gitignored |
## 未来工作
- [x] Thread/UDP 流 — 20 Hz 单色通过 Nanoleaf 外部控制协议
- [x] HAP-BLE 单色控制通过灯泡特征 (IID 51–54)
- [ ] 分区颜色控制 — 设备在 Thread/Nanoleaf 生态系统中仅暴露 panelID=0;HAP-BLE STRIPES (IID 60) 支持分区但产生动画(非静态)颜色,且设备忽略静态写入
- [ ] 直接 CoAP 流传输到设备 IPv6:5683 — 绕过 Nanoleaf Desktop app,消除“运行应用一次”的启动要求
- [ ] 启动时自动检测 HAP MAC 地址(处理重置后的地址轮换)
## 故障排除
### 网桥有流但灯不亮 — 恢复出厂设置后
**症状:** 网桥打印 `OK: b'{"...":{"isSuccess":true}}'` 并发送 `Thread -> rgb(...)` 行,但灯带不亮。
**原因:** 恢复出厂设置会分配新的 Thread IPv6 地址和 token。`config_local.py` 仍有旧地址,因此 UDP 数据包无处可去。
**修复:**
1. 首先通过 **Nanoleaf 移动应用** 配对(参见 [初始设置 — Thread/UDP](#initial-setup--threadudp-recommended) 步骤 1)。Desktop 应用无法配置 Thread 设备 — 只有移动应用可以。
2. 从 Wireshark 获取新地址。在回环适配器上捕获,过滤器:
tcp.port == 15765 and http
查找 `POST /essentials/state` — 请求 JSON 包含当前的 `ip` 和 `token`。
3. 更新 `config_local.py`:
DEVICE_INFO = {
...
"ip": "fdXX:XXXX:XXXX:0:XXXX:XXXX:XXXX:XXXX",
"token": "XXXXXXXXXXXXXXXX",
...
}
4. 重启网桥。
### Nanoleaf Desktop 应用找不到设备
**原因:** NL62 (MAGRGB) 仅支持 Thread。Desktop 应用要求:
- 您的网络上有 Thread 边界路由器(Apple TV 4K, HomePod mini, Google Nest Hub 2nd gen 等)
- 设备已配置到 Thread — **只有 Nanoleaf 移动应用可以做到这一点**
**修复:** 首先通过移动应用配对,然后重新打开 Desktop 应用。一旦设备在 Thread 网络上,它将自动出现。
## 法律声明
本项目是为作者拥有的硬件进行个人互操作性使用而开发的。出于互操作性目的的逆向工程根据 DMCA §1201(f)(美国)和其他司法管辖区的同等条款是被允许的。
未隶属于、未受认可或未连接到 Secretlab、Nanoleaf 或 Apple。所有商标均为其各自所有者的财产。
使用风险自负。
标签:GATT, HAP-BLE, HomeKit, Nanoleaf, Python, RGB灯控, Secretlab, SignalRGB, Thread协议, UDP通信, WLED, 云资产清单, 协议分析, 无后门, 智能家居, 权限提升, 桌子灯带, 桥接器, 物联网, 硬件黑客, 蓝牙低功耗, 跨平台集成, 逆向工具, 逆向工程