我把某东销量第一的智能马桶接入了HomeAssistant
起因
今年新房装修,为了全屋智能所以把大部分家电都买了米家或者支持接入米家生态的
开关和灯用的领普除了马桶
小米没有自己品牌的马桶,只有智米,但是要3k+,太贵了,于是在京东按销量找到了排名第一的马桶(SKJ),支持遥控器,支持微信小程序,微信小程序肯定是蓝牙协议
既然支持蓝牙,就可以接入HA。我想要实现的是座温加热功能,人在或者人回来的时候开启,离家关闭,避免浪费电。
逆向
在某鱼直接搜微信小程序源码,找商家然后发小程序名称,5块钱就可以买到源码,缺点是代码可能是混淆、压缩后的代码,人工几乎不可读。
但AI发展的现在,这些脏话累活完全就丢给大模型就可以了。
通过分析,小程序扫描发射设备名称为VCX-Knob 的名称,通过蓝牙BLE发射
通过 ble.js 的 startInit() 函数发送,所有指令都采用固定的16进制,在通过writeBLECharacteristicValue 发送指令
基础功能按钮
| 按钮名称 | 函数名 | 指令码 | 指令说明 | 数据类型 |
|---|---|---|---|---|
| 大冲 | dachong() |
07 |
触发大冲功能 | commandSwitch |
| 小冲 | xiaochong() |
17 |
触发小冲功能 | commandSwitch |
| 睡眠 | shuimian() |
18 |
睡眠模式开关 | commandSwitch |
| 停止 | tingzhi() |
09 |
停止所有操作 | commandSwitch |
模式选择按钮
| 按钮名称 | 函数名 | 指令码 | 指令说明 | 数据类型 |
|---|---|---|---|---|
| 臀洗 | tunxi() |
02 |
切换到臀洗模式 | commandSwitch |
| 妇洗 | fuxi() |
01 |
切换到妇洗模式 | commandSwitch |
| 烘干 | honggan() |
04 |
切换到烘干模式 | commandSwitch |
| 座温 | zuowen() |
30 |
切换到座温模式 | commandSwitch |
javascript
// 1. 获取写入特征值(UUID 以 0000FFA1 开头)
// 2. 将指令字符串转换为 ArrayBuffer
// 3. 写入特征值
wx.writeBLECharacteristicValue({
deviceId: deviceId,
serviceId: serviceId,
characteristicId: characteristicId,
value: arrayBuffer, // 指令转换后的二进制数据
success: function (res) {
console.log("指令发送成功");
},
fail: function (err) {
console.log("指令发送失败");
},
});
通过分析指令和方法,证明此路可通
发射端
当时考虑使用米家极客版或者小爱同学作为蓝牙发射端,但是经过调用并不原生支持,需要拆机刷机,所以放弃
而我刚好有一台零刻ME-mini,刷的飞牛os,板载支持蓝牙模块
所以我就无需买个树莓派或者其他蓝牙发射端
稍微更新一下驱动,可以扫描到局域网其他蓝牙设备,作为指令发送端
桥接
直接在nas搭建了用docker启动一个emqx服务,在HA配置mqtt
系统架构
整个系统的数据流大概是这样:
服务端
Home Assistant 自动发现配置
HA的MQTT Discovery其实挺方便的,服务端第一次连接时会自动发布设备配置,HA就会自动生成对应的实体。
设备信息配置:
json
{
"identifiers": ["vcx-knob-toilet"],
"name": "智能马桶",
"manufacturer": "VCX",
"model": "Knob"
}
实体类型大概有几种:
- 开关:大冲、小冲、睡眠、泡泡、杀菌、翻圈翻盖这些
- 传感器:水温、座温、风温、信号强度
- 选择器:工作模式(臀洗/妇洗/烘干/座温)
- 按钮:停止、自动按摩
- 数字输入:温度等级设置(0-3档)
所有状态都发布到 homeassistant/toilet/vcx-knob/state,指令则发送到 homeassistant/toilet/vcx-knob/command/{功能名}。
蓝牙通信细节
BLE这块有几个坑记录一下:
指令格式:所有指令都是16进制,格式固定为 AA0802{指令码}{d1}{d2}{d3}{校验和}
校验和计算方式:把前面所有字节加起来,取低8位。
比如大冲的指令就是 AA08020700000018,最后那个18就是校验和。
状态包:设备会主动上报状态,格式是 AA0888{类型}{数据1}{数据2}{数据3},一共有6种类型:
- 01:基础状态(冲水、翻圈、各种开关状态)
- 02:温度信息(水温风温座温等级和实际温度)
- 03:百分比(空气、进水量、雷达等级)
- 04:强度(翻圈翻盖的力度)
- 05:冲水参数(泡泡等级、大小冲时长)
- 06:传感器(脚感应距离、杀菌时长)
微信小程序BLE限制:
- 同时只能连接一个BLE设备
- MTU最大512字节,实际体验单次传输大概20字节左右比较稳
- 连接后需要手动调用notify才能收到设备主动上报的状态
- Android和iOS的BLE行为有点差异,iOS对连接超时更严格
- 扫描时获取到的设备名称不完整(特别是Android),需要连接后重新获取
频道占用问题:这个花了不少时间调试。发现如果手机小程序连着马桶,服务端就连不上去,反过来也一样。看起来这个设备不支持多连接。最后采用的方案是按需连接——平时只扫描,收到HA指令或者需要查询状态时才建立连接,用完就断开。
既然所有指令接口已清晰可见,除了用mqtt上报消息,在HA中自动添加设备、实体,我所幸就也写了(生成)了一个web端控制面板,能够看到扫描状态,连接状态。

希望有所启发