Skip to content

About

Mi Remote 2 Pro (RC003) → Windows keyboard/action mapper. Electron + Raw Input + LL hook. HID usage table verified by experiments.

Resources

Stars

1 star

Watchers

0 watching

Forks

Repository files navigation

Mi Remote Mapper(小米遥控器2 Pro → Windows 键盘映射)

把小米遥控器2 Pro(Mi Remote 2 Pro / RC003)变成 Windows 的“无线键盘 + 快捷动作板”:

  • 方向键 / OK 直接当键盘用(开箱即用)
  • 任意信号可达按键可重映射为:键盘快捷键(如 Win+D 回桌面、Ctrl+Shift+T)、系统动作(Alt+Tab 切窗口、开始菜单)、打开程序 / 文件 / 网址、执行命令、输入一段文本
  • 语音 (F5) 键可接管,避免原生 F5 刷新行为;返回 / 音量± / 电源 / TV 因 Windows 驱动丢弃其报文而暂不可达(详见「按键信号可达性」,规划走 WUDFHost 通道)
  • 实时报文侦测:遥控器发来的每个 HID 报文都能看到,未收录按键也能现场学习并分配动作

技术栈:Electron + React + TypeScript(electron-vite 构建,node-hid 读设备,koffi 调 Win32 API)。


快速开始(开发模式)

npm install          # 若 Electron 二进制下载失败见下方「镜像」一节
npm run dev          # 启动开发版(热更新)
  1. 先在 Windows 蓝牙设置里配对小米遥控器2 Pro
  2. 启动本应用,状态栏应显示「已连接」(自动识别名称含 Mi Remote / 小米遥控器的设备)
  3. 到「按键映射」页给各按键分配动作;「测试」按钮可立刻试效果

打包

npm run dist:dir     # 免安装目录版 → release/win-unpacked/
npm run dist         # NSIS 安装包

验证 / 测试

npm run test         # 单元测试(HID 报文解码)
npm run typecheck    # TS 类型检查
npm run smoke        # 无头冒烟:枚举/解码/钩子/注入/渲染进程挂载 16 项检查

它是怎么工作的

遥控器 ──蓝牙HID──► Windows HID 栈
                     ├──► node-hid 直接读集合(可打开的集合)
                     └──► 键盘集合被系统独占 → Raw Input (WM_INPUT) 兜底
                                  │
                                  ▼
                       ReportDecoder / vkToButtonId
                       (统一产出 buttonId 事件流)
                                  │
                                  ▼
                       MappingEngine 查映射表
                                  │
                    ┌─────────────┼─────────────┐
                    ▼             ▼             ▼
                透传(放行)    执行动作(SendInput)   禁用(吞掉)
                             快捷键/媒体键/开程序/命令/文本

RC003 实测要点(Windows 11 蓝牙配对后)

项目 实测值
HID 层名称 RC003 / manufacturer MIOM(不是蓝牙显示名「小米蓝牙语音遥控器」)
VID/PID 0x2717 / 0x32b8(node-hid 视角)
暴露的集合 仅 1 个:usagePage 0x01 + usage 0x06(Generic Desktop/Keyboard),input 报文长 121 字节(含厂商 usage 位域)
直接读取 不可行 —— 路径结尾 \KBD 被系统键盘驱动独占,ReadFile 返回 ACCESS_DENIED(5)(HidD_GetPreparsedData 等 IOCTL 查询可用,见 tools/ri-descriptor-probe.mjs)
Raw Input 设备名 \\?\HID#{00001812-...}_Dev_VID&012717_PID&32b8_...(注意其中 VID 是蓝牙 DID 值 0x12717,与 HID 层 0x2717 不同!按 PID/序列号匹配更可靠)

因此键盘事件走 Raw Input:注册键盘 + 消费者两个 usage(RIDEV_INPUTSINK,后台可收), 用 RAWINPUTHEADER.hDevice 只放行目标遥控器(hDevice=0 是注入事件,天然防回环), VK 经反向表映射回按钮 ID(如 0x74→语音键 F5、0x24→主页)。 注意 Raw Input 只能收到 kbdhid 翻译成功的 VK:方向/OK/主页/菜单/语音 8 个键可达; 返回/音量±/电源/TV 是键盘页厂商 usage,在驱动层就被丢弃(见下方「按键信号可达性」)。 可打开的集合(消费者/厂商,其它设备)仍走 node-hid 报文解码,两条路径产出一致的 buttonId。

踩坑记录:RIDI_DEVICENAME 的正确值是 0x20000007(见 SDK WinUser.h), 网上常传的 0x20000000 会一直返回 ERROR_INVALID_PARAMETER。

按键与 HID usage 对照(实测来源:RC003 + Windows 实测项目 remote-bridge-hub 交叉验证)

RC003 全部按键都在唯一的键盘集合里、走 report ID 1,报文格式: 01 00 00 + 3 个 16-bit 小端键盘页 (0x07) usage 槽位(如音量+ 按下 = 01 00 00 80 00 00 00 00 00)。

遥控器按键 usage(键盘页 0x07) Windows 原生行为
方向 ▲▼◀▶ 0x52 / 0x51 / 0x50 / 0x4F 方向键 ✅
OK 0x28 Enter ✅
返回 (‹) 0xF1(厂商 usage) 无——kbdhid 不翻译,直接丢弃
主页 (Home) 0x4A Home 键 ✅
菜单 (Menu) 0x65 应用菜单键 ✅
语音键 0x3E F5 ✅(会刷新浏览器,建议改映射或禁用)
音量 +/− 0x80 / 0x81(键盘页保留位,厂商挪用) 无——kbdhid 丢弃
静音(固件保留) 0x7F(键盘页保留位) 无(遥控器无独立静音实体键)
电源 0x66(厂商 usage) 无
TV 0x35(厂商 usage) 无

历史版本曾把音量±记为消费者页 0xE9/0xEA、返回记为键盘 0x29(Esc)——都是错的: RC003 没有 0x29 这个键,音量也不走消费者页。以上 usage 表与 remote-bridge-hub(Windows 实测)一致。

固件差异可能引入别的 usage:打开「报文侦测」页按下按键即可看到原始报文, 未收录按键会自动出现在映射页的「发现的其它按键」,一样可以分配动作。

原生键拦截与「替身转发」(防双触发 + 保住映射)

被改映射的按键若在 Windows 上有原生效果(如语音键=F5、主页=Home),用低级键盘钩子拦下。 关键机制:LL 钩子返回 1 会把事件从整条输入流(含 Raw Input)中抹掉——直接拦截会让引擎 「看不到」按键,映射动作永远不触发(v2 版曾因此回归)。因此钩子吞掉原键后,立即把原 VK 编码到 F13-F24 私有通道并用第二魔数重新注入:

遥控器 Home ──► LL 钩子(吞) ──► 替身 F13+魔数2 ──► Raw Input ──► 翻译回 Home ──► 映射引擎 ──► Win+D
  • 替身带魔数可穿过钩子(防递归),F13-F24 无实体键、无原生效果
  • 引擎自己的动作注入(Win+D 等)带第一魔数,Raw Input 忽略,天然防回环
  • 代价:遥控器连接期间,被改映射的键在物理键盘上同样被接管(如物理 F5 暂时无效)。 按设备区分需要 WUDFHost 通道(在驱动宿主内按设备过滤,规划中)

按键信号可达性(RC003 实测,布局自上而下)

实体布局:电源 / 语音麦克风(顶部)· 五维导航盘(方向×4 + OK)· 返回 + 音量+/− · 主页 · 菜单 + TV

键 到达 Windows 途径
方向×4 / OK / 主页 / 菜单 / 语音(F5) ✅(8 个可达键) 键盘集合标准 usage → kbdhid 翻译 → Raw Input
返回(‹)、音量 +/−、电源、TV ❌ 键盘页厂商 usage(0xF1/0x80/0x81/0x66/0x35),kbdhid 不翻译、直接丢弃

不可达键的通道调研结论(2026-08 两轮实测,证据见 tools/gatt-*.mjs、tools/gatt-vendor-koffi.mjs):

① BLE GATT 订阅 —— 对按键报文不可行(双保险实测):

  • HID 服务(0x1812)接口 CreateFile 返回 GLE=31,被 Windows HID 栈独占; 厂商服务 ab5e0001 也独占(GLE=32)
  • 对照实验:电池/设备信息/厂商服务(0x180F/0x180A/0xFE59/0x1BF/0x8a7a0001)全部可正常打开—— 证明调用方式正确,是系统策略专门锁 HID 服务
  • 厂商服务也不转发按键:订阅了全部 8 个可打开的 notifiable 特征值 (0x2A19、0x2BED、8a7a0102/0301/1201、8ec90001、0x0003、0xFF03), 实测按 音量±/返回/电源/TV → 0 条通知。按键只走 HID 服务内的报告

② Raw Input —— 同样收不到:注册键盘+消费者+系统控制三类 usage 实测, 这些键按下时遥控器句柄的 WM_INPUT 为 0 条(tools/remote-listen.log)—— kbdhid 在驱动层就把非标准 usage 丢了,Raw Input 拿到的是翻译后的 VK,自然也是空。

因此剩余路线只有一条:注入 WUDFHost(系统蓝牙 HID 驱动宿主,remote-bridge-hub 的做法: 附加只读探针、截取 HidOverGatt 的 IOCTL 输出缓冲 01 00 00 + 3×16bit usage, 需要 UAC 管理员确认),或接受 8 个可达键的现状(方向×4/OK/主页/菜单/语音)。

目录结构

shared/            主/渲染进程共享契约(类型、VK 表、按键目录、默认映射)
src/main/          主进程
  hid/decode.ts      HID 报文 → 按键事件(纯逻辑,可测)
  hid/watcher.ts     设备发现/连接/重连 + Raw Input 兜底切换
  input/rawinput.ts  Raw Input (WM_INPUT):窗口 + INPUTSINK 注册 + 设备过滤
  input/inject.ts    SendInput 注入 + 打开程序/命令/文本
  input/suppress.ts  低级键盘钩子
  input/engine.ts    映射引擎
src/preload/       contextBridge 安全桥
src/renderer/      React 界面(映射 / 设备 / 报文侦测 / 设置)
tests/             vitest 单元测试
tools/             排障脚本(HID 枚举、Raw Input 链路自测等)

配置与日志

  • 配置:%APPDATA%\mi-remote-mapper\config.json(界面里可打开目录)
  • 日志:%APPDATA%\mi-remote-mapper\logs\(按天滚动)

常见问题

Electron 二进制下载失败(fetch failed / 证书不匹配): 本机若把 electron-mirror 指到已失效的 npm.taobao.org,会出现证书报错。用环境变量覆盖为 现行镜像(不必改全局 .npmrc):

$env:npm_config_electron_mirror = "https://npmmirror.com/mirrors/electron/"
$env:ELECTRON_MIRROR             = "https://npmmirror.com/mirrors/electron/"
npx install-electron

仍失败时手动放置(见上文版本号对应):

https://npmmirror.com/mirrors/electron/<版本>/electron-v<版本>-win32-x64.zip
 → 解压到 node_modules\electron\dist\
 → node_modules\electron\path.txt 写入 electron.exe

打包(electron-builder)同理加:

$env:npm_config_electron_builder_binaries_mirror = "https://npmmirror.com/mirrors/electron-builder-binaries/"

识别不到遥控器:确认已在系统蓝牙里配对;到「设备」页手动指定 VID/PID。

语音键按了没反应:语音键原生是 F5 且默认「禁用」,编辑映射改成想要的动作即可。 (本工具不做语音听写,只做按键映射。)

占用冲突:不要同时运行其它接管同一遥控器的工具(如 Voice VibeCoding 等)。

许可

MIT

About

Mi Remote 2 Pro (RC003) → Windows keyboard/action mapper. Electron + Raw Input + LL hook. HID usage table verified by experiments.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages