skaka/sony-headphones-linux
GitHub: skaka/sony-headphones-linux
通过逆向工程的蓝牙协议,在 Linux 上实现对 Sony WH-CH720N 耳机的无官方应用控制,支持降噪、均衡器和媒体管理。
Stars: 0 | Forks: 0
# 在 Linux 上控制 Sony WH-CH720N 耳机
一个 Python 程序,旨在通过 Bluetooth 完全控制 Linux 上的 **Sony WH-CH720N** 耳机,
无需任何官方应用。该协议是直接在设备上进行 **Reverse Engineering** 得出的。
## 支持的功能
| 功能 | 状态 | 命令 |
|--------|--------|-------|
| 🔋 电池电量 | ✅ 已确认 | `battery` |
| ℹ️ 固件 + MAC + 设备信息 | ✅ 已确认 | `info` |
| 🎚️ 均衡器 Equalizer(6 频段) | ✅ 已确认 | `eq` / `eq-get` / `eq-flat` |
| 🔇 降噪 Noise Cancelling | ✅ 已确认 | `nc` |
| 🌳 环境声 Ambient Sound | ✅ 已确认 | `ambient` |
| 📊 支持的特性表 | ✅ 已确认 | (在 `info` 中) |
| ⏯️ 播放/暂停 Firefox & Chrome 媒体 | ✅ 已确认 | `media-*` |
| 🖥️ 图形界面 | ✅ | `python3 gui.py` |
| 🔗 将耳机按钮绑定到系统命令 | ✅ | `python3 agent.py` |
| ⏹️ 完全关闭 ANC (Off) | ⚠️ 协议未暴露 | 使用耳机按钮 |
| 🔀 可写入开关 `@0xe6` | ✅ 写入已确认 · 功能未确认 | `raw e8 00 00/01` |
| 🔊 DSEE / 自动关闭 (`0x78/0x74/0xe2`) | 🔬 只读(调整功能研究中) | `raw 78 00` |
## 安装
```
# 无需对耳机的系统要求(仅限 Python 标准库)
# 仅用于控制浏览器媒体:
sudo apt install python3-dbus
# 确保耳机已配对并连接
bluetoothctl devices
```
## 使用方法
```
python3 main.py discover # اكتشاف السماعة والقناة
python3 main.py info # كل المعلومات
python3 main.py battery # البطارية
python3 main.py nc # تفعيل إلغاء الضوضاء
python3 main.py ambient # الصوت المحيطي
python3 main.py status # الوضع الحالي
python3 main.py eq-get # قراءة المعادل
python3 main.py eq 14 12 10 10 8 6 # 6 باندات (0..20، 10=وسط)
python3 main.py eq-flat # تسطيح المعادل
python3 main.py media-pause # إيقاف ميديا المتصفحات
python3 main.py media-play # تشغيل
python3 main.py media-toggle # تبديل
python3 main.py media-list # عرض المشغّلات وحالتها
python3 main.py raw 22 00 # إرسال أمر خام (hex) للتجربة
python3 main.py listen # طباعة الإطارات الواردة (لعكس البروتوكول)
```
### 图形界面 (GUI)
```
python3 gui.py
```
使用纯 tkinter 构建的**现代化**界面(扁平暗色主题、卡片、彩色按钮),无需额外依赖:
- **默认英语**,带有切换到**阿拉伯语**的按钮(右上角)。
- 完整的阿拉伯语支持:通过 `arabic.py` 实现**字母连接(词形变化)+ RTL(从右向左)布局** —
因为 Tk 不支持这些功能(字母会断开显示),所以我们将文本转换为连写形式,
并在视觉上进行反转,同时使用 Noto Naskh Arabic 字体。
- 显示电池电量,带有当前模式高亮的声音模式按钮,均衡器推子(6 频段)附带
快速预设,以及浏览器媒体控制按钮。
- 在独立线程中保持持久连接 + **实时 ANC 指示器**,在按下耳机按钮时会更新。
### 将耳机按钮绑定到系统命令 (agent)
```
python3 agent.py
```
监控耳机通知 (NTFY) 并触发相关操作的服务器。默认行为:
- 按下耳机按钮切换到**环境声** → 暂停浏览器媒体播放。
- 切回**降噪**模式 → 恢复媒体播放。
修改 `agent.py` 顶部的 `BINDINGS` 字典,即可将任何事件绑定到任何系统 shell 命令,例如:
```
BINDINGS = {
"ncasm:ambient": run_shell("notify-send 'الصوت المحيطي'"),
"battery:low": run_shell("notify-send 'بطارية السماعة منخفضة'"),
}
```
### 系统托盘图标 + 自动启动
```
python3 tray.py # تشغيل الأيقونة يدويًا
bash install_autostart.sh --start # تثبيت (تعمل عند كل دخول) + تشغيل الآن
bash install_autostart.sh --uninstall # إزالة التشغيل التلقائي وإيقاف الأيقونة
```
时钟旁边的耳机图标,作为常驻服务运行:
- **耳机连接到 Bluetooth 时自动连接**(在尝试前检查 BlueZ 状态 — 避免导致适配器过载的重复尝试)。
- 恢复会话设置(均衡器/模式),并将耳机按钮绑定到浏览器的媒体播放/暂停控制。
- 菜单功能:状态与电池电量、NC/Ambient、媒体控制、**打开设置窗口**(会暂时释放
连接随后恢复)以及退出。菜单显示已保存的设置语言。
### 在会话之间保存设置
语言、均衡器和模式会自动保存在 `~/.config/sony-ch720n/config.json` 中,
并在下次连接耳机时恢复。
## 发现的协议(WH-CH720N,固件 1.0.8)
### 传输层
- **Bluetooth Classic RFCOMM**,通道 **18**。
- 通过官方的 Sony MDR 服务 UUID 进行识别:
`956c7b26-d49a-4ba8-b03f-b17d393cb6e2`
### 帧格式
```
[0x3E] [DATA_TYPE] [SEQ] [LEN: 4 bytes BE] [PAYLOAD...] [CHECKSUM] [0x3C]
```
- `0x3E` 帧头,`0x3C` 帧尾,`0x3D` 转义字符 (escape)。
- 命令的 `DATA_TYPE` = **`0x0C`**(已在 CH720N 上确认),ACK 的 = `0x01`。
- `CHECKSUM` = (DATA_TYPE + SEQ + LEN + PAYLOAD) 之和 mod 256。
- 特殊字节 `3E/3C/3D` → 替换为 `0x3D` + `(byte & 0xEF)`。
### ACK 处理(非常重要)
每个 DATA 帧都必须收到一个带有**反转**序列号的 ACK:`ack_seq = received_seq XOR 1`。
如果没有收到,耳机将会重传并随后断开连接。
### 命令代码映射(通过探测发现)
payload 中的第一个字节 = 命令代码。返回的响应通常 = 命令代码 + 1。
| GET | RET | 功能 | 响应示例 |
|-----|-----|--------|---------|
| `0x00` | `0x01` | 协议信息 | `01 00 03 00 10 02 00 00` |
| `0x02` | `0x03` | MAC 地址(文本) | `03 00 23 11 "AA:BB:CC:..."` |
| `0x04` | `0x05` | 固件版本 | `05 02 05 "1.0.8"` |
| `0x06` | `0x07` | 支持的特性表(23 项功能) | `07 00 17 ...` |
| `0x12` | `0x13` | 设备信息 (JSON) | `{"type":"CS",...}` |
| `0x22` | `0x23` | 电池电量 | `23 00 64 00` (=100%) |
| `0x50` | `0x51` | EQ 预设列表 | 12 preset |
| `0x52` | `0x53` | EQ 状态 | `53 00 00` |
| `0x56` | `0x57` | 当前 EQ 频段 | `57 00 00 06 0a×6` |
| `0x58` | `0x59` | **设置 EQ** | `58 00 a0 06 <6 bands>` |
| `0x5a` | `0x5b` | EQ 频率 (Hz) | 400/1k/2.5k/6.3k |
| `0x66` | `0x67` | NC/ASM 状态 | `67 17 01 00 01 00 14` |
| `0x68` | — | **设置 NC/ASM** | 见下文 |
| `0x78` | `0x79` | 开关 (DSEE?) 值为 1 — 只读 | `79 00 01` |
| `0x70/0x74` | — | 多个值(编解码器/设置?) | 🔬 |
| `0xe6` | `0xe7` | **可写入开关** (SET=`0xe8`, NTFY=`0xe9`) — 功能未确认(听觉效果微弱;通过对比 Sony 应用命名) | `e7 00 01` |
| `0xe2` | `0xe3` | 值为 0 的只读开关 | `e3 00 00` |
| `0x69` | — | **按下 NC/Ambient 按钮时的 NTFY** (agent 使用) | `69 17 ...` |
### NC/ASM (subtype 0x17 — 唯一支持的子类型)
```
Noise Cancelling : 68 17 01 01 00 00 14
Ambient Sound : 68 17 01 00 01 00 14
```
结构:`[effect, ncSel, asmSel, voice, level]`。第二个字节(选择器)最重要:
`01` = NC,`00` = Ambient。级别固定为 `0x14`(CH720N 没有环境声音量滑块)。
通过此子类型**无法完全关闭 (OFF)** — 耳机始终返回 `effect=01`。
### EQ
```
58 00 06
```
每个频段范围为 0..20(10=中性)。频段:Clear Bass, 400Hz, 1kHz, 2.5kHz, 6.3kHz, 16kHz。
专用的插槽 `0xA0`/`0xA1`/`0xA2` 可以存储任意值。
## 项目结构
```
main.py واجهة سطر الأوامر (CLI)
gui.py الواجهة الرسومية (tkinter) — ثنائية اللغة + حفظ الإعدادات
agent.py خادم ربط أزرار السماعة بأوامر النظام
tray.py أيقونة شريط المهام (AppIndicator) + اتصال تلقائي
install_autostart.sh تثبيت/إزالة تشغيل الأيقونة تلقائيًا عند الدخول
sony_headset/
protocol.py بناء/فك الإطارات + checksum + escape (مغطّى باختبارات)
transport.py اكتشاف + اتصال RFCOMM مع retry + فحص حالة البلوتوث
commands.py أكواد الأوامر + بناء/فك الـ payloads
client.py عميل عالي المستوى (handshake/ACK/الميزات)
media.py تحكم ميديا المتصفحات عبر MPRIS/D-Bus
arabic.py تشكيل عربي + RTL خالص بـ Python (لعرض العربية في tkinter)
config.py حفظ/تحميل إعدادات الجلسة (~/.config/sony-ch720n/config.json)
tools/
probe_features.py أداة جس أكواد الأوامر (لاكتشاف خصائص جديدة)
probe_toggles.py أداة اختبار كتابة المفاتيح (toggles)
tests/
test_protocol.py اختبارات طبقة البروتوكول (9 اختبارات)
```
## 扩展新功能
```
# 1) 发现返回的 codes:
python3 tools/probe_features.py
# 2) 发送 raw command 并读取响应:
python3 main.py raw 78 00
# 3) 监控耳机的 frames(例如按下其按钮时):
python3 main.py listen --seconds 20
```
## 注意事项与限制
- 耳机同一时间仅允许**一个** MDR 连接。如果提示 `Device or resource busy`
请等待一秒钟后重试(已内置自动重试机制)。
- 每次仅运行一个命令;过快地连续运行命令可能会与通道释放发生冲突。
- 这些代码仅在 CH720N 型号 / 固件 1.0.8 上验证过;其他型号可能会有所不同
(对于较新的型号,请尝试 `DATA_TYPE = 0x0E`)。
标签:Linux工具, Python, 云资产清单, 无后门, 硬件适配, 端口探测, 系统工具, 蓝牙控制, 逆向工具, 逆向工程, 音频控制