从微信小程序到 Home Assistant:VCX-Knob 智能马桶蓝牙桥接服务开发实践
从微信小程序到 Home Assistant:VCX-Knob 智能马桶蓝牙桥接服务开发实践
项目背景
最近在整理一个微信小程序项目,这个项目用于控制 VCX-Knob 智能马桶。小程序通过蓝牙低功耗(BLE)与设备通信,实现了水温调节、工作模式切换、自动按摩等丰富的功能。
但作为一个 Home Assistant 用户,我更希望能够将这些设备直接集成到智能家居系统中。微信小程序虽然功能完善,但无法与 Home Assistant 直接通信。于是,我决定开发一个 Node.js 桥接服务,将蓝牙设备的功能暴露给 Home Assistant。
技术挑战
1. 蓝牙协议逆向
首先要做的是理解微信小程序中的蓝牙通信协议。通过分析小程序源码,我发现了以下关键信息:
- 设备名称:
VCX-Knob - 服务 UUID:
0000FFA0-0000-1000-8000-00805F9B34FB - 特征值 UUID:
- 写入:
0000FFA1-0000-1000-8000-00805F9B34FB - 通知:
0000FFA2-0000-1000-8000-00805F9B34FB
- 写入:
2. 指令格式解析
所有指令都采用固定的十六进制格式:
AA 08 02 [指令码] [数据1] [数据2] [数据3] [校验和]
AA 08 02:固定头部[指令码]:功能操作码(如大冲07、小冲17、睡眠18等)[数据1-3]:参数数据(温度等级、模式等)[校验和]:所有字节的十六进制和
3. 状态数据解析
设备返回的状态数据也采用类似的格式:
AA 08 88 [类型码] [数据1] [数据2] [数据3]
根据不同的类型码(01-06),数据包含不同的信息:
- 类型 01:基础状态(冲水、座座、泡泡、空气、雷达等开关状态)
- 类型 02:温度和参数(水温、风温、座温等级,水压、氛围灯亮度等)
- 类型 03:百分比和档位(空气百分比、进水百分比、雷达档位等)
- 类型 04-06:力度、冲水参数、传感器参数等
架构设计
核心模块
项目采用模块化设计,包含以下几个核心模块:
-
BLE 客户端 (
ble-client.js)- 负责蓝牙设备的连接、扫描、指令发送
- 处理状态数据的接收和解析
- 自动重连机制
-
指令管理 (
commands.js)- 定义所有指令码常量
- 提供指令生成函数(简单开关、温度设置等)
- 指令格式验证和转换
-
Home Assistant 客户端 (
ha-client.js)- REST API 调用(更新实体状态)
- WebSocket 连接(实时监听状态变化)
- 实体管理和状态同步
-
MQTT 客户端 (
mqtt-client.js)- MQTT 消息发布/订阅
- Home Assistant 自动发现
- 作为备选方案支持
两种连接模式
为了满足不同用户的需求,我设计了两种连接模式:
直接连接模式(推荐)
javascript
CONNECTION_MODE=direct
HA_API_URL=http://homeassistant:8123
HA_ACCESS_TOKEN=your_token
优势:
- 无需独立的 MQTT Broker
- 直接使用 Home Assistant 的 API
- 配置更简单
- WebSocket 实时通信
实现:
- 使用 REST API 更新实体状态
- 使用 WebSocket API 监听用户操作
- 状态变化立即同步
MQTT 模式
javascript
CONNECTION_MODE=mqtt
MQTT_BROKER_URL=mqtt://localhost:1883
优势:
- 支持 Home Assistant 自动发现
- 无需手动配置实体
- 更符合 MQTT 生态
关键技术实现
1. 蓝牙连接管理
使用 @abandonware/noble 库实现蓝牙通信:
javascript
import noble from '@abandonware/noble';
class BLEClient {
async connect(peripheral) {
await peripheral.connectAsync();
const services = await peripheral.discoverServicesAsync([serviceUUID]);
const characteristics = await service.discoverCharacteristicsAsync([
writeUUID, notifyUUID
]);
// 订阅通知
await notifyCharacteristic.subscribeAsync();
notifyCharacteristic.on('data', this.onCharacteristicData);
}
}
2. 状态数据解析
状态数据需要按位解析,例如基础状态:
javascript
parseBasicStatus(byte1, byte2, byte3) {
const byte1Bits = parseInt(byte1, 16).toString(2).padStart(8, '0');
return {
flush: byte1Bits[7] === '1', // bit7: 冲水状态
seat: byte1Bits[6] === '1', // bit6: 座座状态
bubble: byte1Bits[5] === '1', // bit5: 泡泡状态
air: byte1Bits[4] === '1', // bit4: 空气状态
// ...
};
}
3. Home Assistant 集成
直接连接模式通过 REST API 和 WebSocket 实现:
javascript
// 更新实体状态
async updateEntityState(entityId, state, attributes) {
const response = await fetch(`${apiUrl}/api/states/${entityId}`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({ state, attributes })
});
}
// WebSocket 监听状态变化
ws.send(JSON.stringify({
type: 'subscribe_events',
event_type: 'state_changed'
}));
实体设计
为了在 Home Assistant 中更好地控制设备,我设计了以下实体类型:
- 开关:大冲、小冲、睡眠、泡泡、空气净化、氛围灯等
- 传感器:水温、风温、座温、空气百分比等
- 选择器:工作模式(臀洗/妇洗/烘干/座温)
- 按钮:停止、自动按摩、泡泡复位
- 数字输入:温度等级设置(0-3)
开发过程中的坑
1. 原生模块编译问题
@abandonware/noble 需要编译原生 C++ 代码,在使用 pnpm 时遇到了构建脚本被忽略的问题。
解决方案:
bash
# 手动构建
cd node_modules/.pnpm/@abandonware+noble@*/node_modules/@abandonware/noble
npm run install
2. 状态数据包不完整
蓝牙数据传输可能出现分包情况,需要实现数据缓冲和重组。
解决方案:
javascript
onCharacteristicData(data) {
this.statusBuffer += hexString;
// 检查完整数据包
while (this.statusBuffer.includes('AA0888')) {
const packet = extractPacket(this.statusBuffer);
this.parseStatusData(packet);
}
}
3. Home Assistant 实体创建
直接连接模式无法通过 API 自动创建实体,需要手动配置。
解决方案:
- 提供配置模板文件 (
homeassistant-config.yaml) - 创建详细的配置指南 (
HA_SETUP.md) - 启动时检查实体是否存在并提示用户
项目亮点
- 完整的协议实现:支持所有功能指令和状态解析
- 灵活的连接方式:支持直接连接和 MQTT 两种模式
- 完善的错误处理:自动重连、状态恢复、错误提示
- 详细的文档:README、安装指南、配置说明一应俱全
- 代码质量:模块化设计、清晰的注释、错误处理
使用体验
启动服务后,Home Assistant 中会自动出现智能马桶的所有控制选项。可以通过 UI 切换工作模式、调节温度、控制各种功能,实现了真正的智能家居集成。
总结
这个项目展示了如何:
- 逆向分析蓝牙通信协议
- 使用 Node.js 实现蓝牙桥接服务
- 集成 Home Assistant 智能家居系统
- 处理实际开发中的各种技术挑战
通过这个项目,不仅实现了功能需求,还深入理解了蓝牙通信、智能家居集成等技术细节。对于想要将非标准设备集成到 Home Assistant 的开发者来说,这是一个很好的参考案例。
相关资源
- 项目文档完整,包含详细的安装和配置指南
- 支持 macOS、Linux 平台
- 代码开源,可扩展性强
希望这个项目能够帮助到有类似需求的开发者!