henaxxx/a2854-siri-remote-linux

GitHub: henaxxx/a2854-siri-remote-linux

在 Linux 上逆向并桥接第三代 Apple Siri Remote,将其按键与语音功能集成到自定义智能家居系统的 Python 工具。

Stars: 2 | Forks: 0

# a2854-siri-remote-linux ![由本项目控制、处于吸顶射灯下的 Apple Siri Remote (A2854, 第三代)](https://static.pigsec.cn/wp-content/uploads/repos/cas/48/484d753cc31eb71a6a4a043664623e715de2ea326f5c28056aa2fdefa01e21d6.jpg) 一个 Python 桥接器,可在 Linux 上使用 **Apple Siri Remote(第三代, 型号 A2854)**——包括按钮、方向键 (d-pad)、电源按钮和 Siri 语音流——并将其接入一个小型智能家居系统(TP-Link Tapo 智能插座、通过 AirPlay 连接的 Apple HomePod、通过 `ntfy` 发送推送通知,以及可选的基于 Groq 的 LLM 语音助手)。 ## 前人工作(请先阅读此部分) 如果你想要的只是一个在 Linux 上运行且干净、设计良好的 A2854 驱动程序, **请使用 [azais-corentin/siri-remote](https://github.com/azais-corentin/siri-remote)**。 这是一个 Rust 实现的项目,截至 2026 年 5 月,它已经涵盖了: - A2854 的按钮, - 支持完整 X/Y/压力/多指/旋转/悬停的触摸板, - 将 Siri 麦克风作为 PipeWire 的 `Audio/Source`, - 包括处理 BLE RPA 地址轮换的配对。 我是在发布这个项目之后才知道它的。“让第三代 Siri Remote(包括语音功能)在 Linux 上工作”这一 核心成果应归功于它,而不是本代码库。 本项目还建立在以下早期工作的基础之上: - **[Yanndroid/SiriRemote-Linux](https://github.com/Yanndroid/SiriRemote-Linux)** — 确立了早期 Siri Remote 世代在 Linux 下的按钮/触摸板输入的基本形态, 并记录了当时阻碍音频传输的 BlueZ 20 字节 HIDP 限制。 - **[Jack-R1/SiriRemoteVoiceControl](https://github.com/Jack-R1/SiriRemoteVoiceControl)** — 通过 PacketLogger 在 **macOS** 上对**第二代 / 第四代**遥控器进行 语音解码;在那里就已经知道了“在固定长度的通知中包含带长度前缀的 Opus 数据”这种结构。 ## 本代码库的附加价值 鉴于上述情况,本项目中可能仍然有用的部分是: - **一种不同的读取路径。** 该桥接器在读取端完全没有使用 BlueZ 的 GATT D-Bus API;它直接通过 PTY 从 `btmon` 读取 ATT `Handle Value Notification`(句柄值通知)数据帧。这避开了 BlueZ HoG 插件独占 HID 服务的问题,而 无需进行 D-Bus 通信或编写内核模块。 - **一个 Python 参考实现**,以防 Rust 不是你的技术栈。 - **一套端到端的智能家居粘合剂**:通过 `tapo` SDK 控制 Tapo 插座,通过持久化的进程内 `pyatv` 客户端控制 HomePod,通过 Groq Whisper + LLM + ntfy 处理语音,所有这些均在 systemd 下运行,具备自动重连功能和一个 kiosk 信息仪表板。 - **一份详细记录的时间线**,包括所有失败的方法。 请参阅 [TIMELINE.md](TIMELINE.md)。 ## A2854 按钮十六进制映射 所有数值均在 GATT 句柄 `0x003a` 处观察得到,触发时发送 2 字节通知, 释放时后跟 `0000`。 | 按钮 | 按下时的数值 | |---|---| | 返回 | `4000` | | TV | `0100` | | 播放/暂停 | `0001` | | 音量加 | `0200` | | 音量减 | `0400` | | 静音 | `8000` | | 电源 | `1000` | | 方向键中心 | `0800` | | 方向键上 | `0002` | | 方向键下 | `0008` | | 方向键右 | `0004` | | 方向键左 | (未经测试 —— 所用设备上的该物理按键已损坏) | | Siri(长按) | 在句柄 `0x0036` 上发送音频帧 | ## 第三代音频封装格式 句柄 `0x0036` 上的每个通知恰好为 100 字节: ``` offset size meaning 0 2 stream marker / session id (e.g. 1a 5c, 95 04, 00 00) 2 2 sequence number, little-endian 4 1 opus packet length in bytes (N) 5 N opus packet, starting with TOC byte 0xB8 5+N ... zero padding up to 100 bytes ``` `0xB8` 解码(根据 RFC 6716,表 2)为: - 配置 23 → **仅限 CELT,宽带,20 ms** 帧 - 单声道 - 每个数据包 1 帧 Opus 的输出标准为 48 kHz;WB(宽带)带宽标志将 实际音频内容限制在约 8 kHz 左右。其中包含的 [`decode_siri_voice.py`](decode_siri_voice.py) 目前会要求 解码器以 16 kHz 的输出速率进行解码,这足以还原 WB 内容。如果你希望获得未经处理的音频,azais-corentin 的 `siri-remote` 会通过 PipeWire 暴露完整的 48 kHz 输出。 这与 Jack-R1 描述的第二代/第四代封装(以 `1B 23 00 00 10` 开头)不同,但在固定长度通知内包含带长度前缀的 Opus 这一*思路*是相同的。 请参阅 [`decode_siri_voice.py`](decode_siri_voice.py) 获取参考的 解码器(btmon snoop → WAV)。 ## 架构(当前设备配置) ``` A2854 Siri Remote │ BLE GATT notifications ▼ Linux mini-PC (Ubuntu 24.04, BlueZ 5.72) │ ├─ btmon ── pty.fork() ── notification parser ──┬─ button → Tapo P105 / HomePod │ └─ audio → Opus decode → WAV │ │ │ ▼ │ Groq Whisper → Groq LLM → ntfy │ └─ FastAPI dashboard on :8080 (kiosk display) ``` 为 `btmon` 围绕 `pty.fork()` 的存在是因为: 1. BlueZ HoG 插件会将自己附加到 A2854 的 HID 服务上, 并拒绝通过通用的 GATT D-Bus API 发布特征值。因此 `bleak`、`busctl` 及其相关工具无法 订阅按钮或音频通知。 2. 通过 `btmon` 读取 HCI 流量是有效的,但 `subprocess.Popen` 会 对其标准输出进行块缓冲。`stdbuf` 在这里不起作用,因为 `sudo` 会剥离 `LD_PRELOAD`。 3. 在 PTY 下启动 `btmon` 可以恢复流行式流传输,并保持 解析器的响应状态。 ## 已测试硬件 - Apple Siri Remote 第三代(型号 **A2854**,二手设备,左键 物理损坏) -配备 Intel 芯片组蓝牙 `8087:0a2a` 的 Mini-PC(BT 4.2,在调整 `MinConnectionInterval` 和 USB 自动挂起后**无需**接收器) - Apple HomePod mini,配对状态为 "NotNeeded" —— 通过 `pyatv` 控制 - TP-Link Tapo P105 (×2) —— 通过 `tapo` Python SDK 在本地控制 ## 快速开始 ``` sudo apt install -y bluez libopus0 ffmpeg python3-venv git clone https://github.com//a2854-siri-remote-linux.git cd a2854-siri-remote-linux python3 -m venv .venv . .venv/bin/activate pip install -e . cp .env.example .env $EDITOR .env ``` 配对遥控器(将其靠近适配器): ``` a2854-pair --pair --seconds 60 --min-rssi -50 ``` 允许免密码运行 `btmon`(桥接器需要原始 HCI 访问权限): ``` # /etc/sudoers.d/btmon-youruser youruser ALL=(ALL) NOPASSWD: /usr/bin/btmon ``` 运行桥接器: ``` a2854-bridge ``` 或者在修改路径后,从 `systemd/*.example` 安装 systemd 单元。 ## 文件布局 | 文件 | 用途 | |---|---| | `a2854_bridge.py` | 主桥接器:按钮解析、语音路径、控制器 | | `a2854_pair.py` | A2854 的 BLE 检测 + 配对助手 | | `decode_siri_voice.py` | 独立的 btmon snoop → WAV 解码器 | | `bt_observe.py` | 连接状态观察器 | | `capture_buttons.sh` | 用于新按钮识别的 btmon 捕获助手 | | `tapo_test.py` | Tapo CLI 健全性测试 | | `whisper_test.py`, `whisper_ntfy_test.py` | 语音 pipeline 检查 | | `dashboard.py` | 运行在 `:8080` 端口的 FastAPI 状态仪表板 | | `systemd/*.example` | service 单元模板 | | `udev/*.example` | Intel BT 芯片组的 USB 自动挂起规则 | ## 许可证 [MIT](LICENSE)。 ## 贡献 欢迎提交 Issue 和 PR —— 特别是如果你能在不同的 A2854 设备上证实或反驳 任何关于协议的发现的话。如果你恰好 知道本文作者遥控器上(不幸损坏的)左键的十六进制值,你可以通过 PR 提交它。
标签:Python, 云资产清单, 无后门, 智能家居, 蓝牙, 逆向工具, 逆向工程