趁着有点闲钱,打算继续改改家中的电器。
首先记录两个重要步骤:
获取设备的token
所有米家智能产品通信都在54321/udp上,通过token认证交互,而这个token目前无法直接获取了,不过可以通过以下方法获得:
数据库法
推荐使用安卓模拟器,因为今后的调试中涉及抓包,方便起见。
使用的米家版本需要是5.0.19
左右的版本,在此之后的版本已经不会把token存到本地了。下载:ApkMirror
下载,安装,登陆账号,等待米家同步所有设备。
这时可以使用文件管理器打开/data/data/com.xiaomi.smarthome/databases/
将miio2.db
移动到计算机中,使用网上的工具,例如:https://miio.loli.ren/index.php 来读取,也可以直接使用sqlite工具打开。

我们需要的是这个ip和token。
建议在路由器端为智能产品固定IP地址
好了,经过测试,其他方法失效(哭
抓包解包
首先你需要在服务器端安装miio,npm i miio -g
,然后启动米家APP进行抓包,你需要知道需要抓包的智能产品的IP地址,开始抓包后在米家APP对智能产品进行操作,尽可能的覆盖每一个操作。为避免影响,比如空调伴侣,可以把空调插头先拔下来。
我在此使用WireShark
进行操作,涉及的操作大同小异。
使用过滤条件:ip.addr == 智能产品的IP
如果有必要可以过滤udp包出来,然后File
->Export Packet Dissections
->As JSON
,直接命名保存就可以。

然后我们得到了这样的JSON:
[
{
"_index": "packets-2020-03-25",
"_type": "pcap_file",
"_score": null,
"_source": {
"layers": {
"frame": {
"frame.interface_id": "0",
"frame.interface_id_tree": {
"frame.interface_name": "\\Device\\NPF_{2333333333333}"
},
"frame.encap_type": "1",
"frame.time": "Mar 25, 2020 17:46:40.839379000 \344230b4",
"frame.offset_shift": "0.000000000",
"frame.time_epoch": "1585129600.839379000",
"frame.time_delta": "0.051667000",
"frame.time_delta_displayed": "0.000000000",
"frame.time_relative": "5.302513000",
"frame.number": "189",
"frame.len": "74",
"frame.cap_len": "74",
"frame.marked": "0",
"frame.ignored": "0",
"frame.protocols": "eth:ethertype:ip:udp:data",
"frame.coloring_rule.name": "UDP",
"frame.coloring_rule.string": "udp"
},
"eth": {
"eth.dst": "502343444",
"eth.dst_tree": {
"eth.dst_resolved": "50:e23424:72333:43",
"eth.addr": "50:ec:23333:02343:43",
"eth.addr_resolved": "502434243",
"eth.lg": "0",
"eth.ig": "0"
},
然后执行 miio protocol json-dump JSON文件 --token 设备的TOKEN
就可以自动解包,我们就会获得如下信息:
-> 192.168.31.145 data= N/A
<- 192.168.31.68 data= N/A
-> 192.168.31.145 data= {"id":9552,"method":"get_ac_model","params":[]}
<- 192.168.31.68 data= {"id":9552,"result":[192,9377,1],"exe_time":0}
-> 192.168.31.145 data= N/A
<- 192.168.31.68 data= N/A
-> 192.168.31.145 data= {"id":9553,"method":"get_prop","params":["ac_mode","ac_state","load_power","en_nnlight","quick_cool_state","sleep_state","list_crc32"]}
<- 192.168.31.68 data= {"id":9553,"result":[0,"P1_M0_T22_S3_D0",0.00,0,0,0,187445932],"exe_time":0}
-> 192.168.31.145 data= {"id":9555,"method":"get_prop","params":["ac_mode","ac_state","load_power","en_nnlight","quick_cool_state","sleep_state","list_crc32"]}
<- 192.168.31.68 data= {"id":9555,"result":[0,"P1_M0_T22_S3_D0",0.00,0,0,0,187445932],"exe_time":0}
像这样就是米家产品的交互过程,我们只需要知道它发送什么,返回什么就行,miio能让我们直接使用。
空调
空调我也不记得是什么时候买的了,不过好在小米杂货铺牛逼!
花65买了一个空调伴侣2,东西真的很小,大概长这样:

设计的很聪明,那一圈黑色的是全向红外发射器,然后自带功率计,这个价格很不错,详细的功能官网有,我不多做赘述。

米家APP张这个样子,功能很足,主要是码库是真的全,点赞!
然后自然是接入到HomeBridge让我等穷人和Siri多多接触。但是很遗憾,在npm搜了一下,只有三个是空调伴侣的插件,并且基本是3年前左右的代码,经过测试,完全不可用。
看了一下,原来是空调伴侣的控制码变了,原来是编码组合,现在是单个命令,感兴趣可以看看之前
解码
部分中的示例,那个是最新版本空调伴侣的抓包。我也找来了一份旧版的抓包:
-> 192.168.199.154 data= {"id":9460,"method":"get_model_and_state","params":[] }
<- 192.168.199.240 data= {"result":["010500978022222102","0111201E0280222221", "1148"],"id":9460}
-> 192.168.199.154 data= {"id":9461,"method":"get_model_and_state","params":[] }
<- 192.168.199.240 data= {"result":["010500978022222102","0111201E0280222221", "1148"],"id":9461}
-> 192.168.199.154 data= {"id":9462,"method":"send_cmd","params":["01802222210 1201e02",1]}
<- 192.168.199.240 data= {"result":["ok"],"id":9462}
-> 192.168.199.154 data= {"id":9463,"method":"get_model_and_state","params":[] }
<- 192.168.199.240 data= {"result":["010500978022222102","0101201E0280222221", "1148"],"id":9463}
-> 192.168.199.154 data= N/A
<- 192.168.199.240 data= N/A
-> 192.168.199.154 data= {"id":9464,"method":"get_model_and_state","params":[] }
<- 192.168.199.240 data= {"result":["010500978022222102","0101201E0280222221", "1148"],"id":9464}
-> 192.168.199.154 data= {"id":9465,"method":"send_cmd","params":["01802222211 1201e02",1]}
<- 192.168.199.240 data= {"result":["ok"],"id":9465}
那怎么能放弃呢哈哈哈哈
故研究一下新的编码,和目前的程序,经过测试,新版编码中ac_state
参数会直接返回空调目前的信息:
- P 电源状态,0 开 1关
- S 风速 0 自动 1 低 2 中 3 高
- D 扫风 1 关 0 开
- M 模式 0 制冷 1 制热 2 自动 3 通风 4 除湿
- T 温度 跟值
这些是可以通过操作米家APP整出来的,有了这些信息就可以修改代码了。
我使用的模块是homebridge-mi-acpartner
,Github在 https://github.com/LASER-Yi/homebridge-mi-acpartner
我修改可用的代码没有公开,毕竟是为了用而糊的,没有从0开发也就很多地方考虑不到,怕出事...
修改
首先是获取信息的逻辑,原来的代码会请求get_model_and_state
这里会直接返回所有信息,不过没有人类可读性,程序里面使用substr
分割,不过我目前解读了ac_state
的信息就直接替换即可,不过需要注意的是,ac_state
里面0是开,1是关,需要修改相关代码,并且模式的数字也不一样,制冷和制热的代码有调换。
解决完信息获取,就是发送命令了。因为目前发送命令不像之前是一次性给状态,而是挨个设置,我推测是新版固件有计时器,会等待一段时间接收指令再一次发送,这个工作之前应该是交给APP的,现在给伴侣自己了。
所以为了发送命令,我在每次获取信息时创建一次信息备份,在发送命令时与备份信息比对,有变动就发送。(原来是更新状态后一次性执行函数,由函数组合状态成控制码发送)这样就解决了发送命令的问题。
现在已经完全可在家庭APP
中使用,顺带带上玄学聪明的Siri:

效果很满意哈哈哈哈
温度计
买了一个温湿度计,挺便宜

不过它本身不支持Wifi连接,需要网关。正巧,我家之前在餐厅安装了米家吸顶灯,他是支持作为蓝牙网关使用的,中间隔了一堵墙和衣柜,居然还有信号,也是很厉害了。

不过我想弄到homebridge上就有点麻烦,npm上我搜索是只搜索到一个通过蓝牙连接的,可..我机器哪来的蓝牙,想了想应该还是得靠网关。
抓包无果..不太清楚客户端是怎么向网关要数据的,不过我还是尝试翻找了前人的代码,是这么写的:
//Update CurrentTemperature
const p1 = this.outerSensor && this.platform.devices[this.deviceIndex].call('get_device_prop_exp', [
[this.outerSensor, "temperature", "humidity"]
])
这个this.outerSensor
是蓝牙设备的id,写了一个js试一下:
const miio = require('miio位置');
miio.device({ address: '网关ip', token: "网关token" })
.then((retDevice) => {
console.log('Connected to', retDevice);
retDevice.call('get_device_prop_exp', [
["传感器ID", "temperature", "humidity"]
]).then((sendRet) => {
console.log(sendRet);
retDevice.destroy();
})
})
.catch(err => console.log('Error to', err));
不过我家吸顶灯的网络位置真的是差到离谱啊啊啊!
不知道是不是应为贴天花板的原因,wifi奇差,丢包率能高到10%。当然在app里也不好使,我估计网关应该是自动找传感器要数据然后定时上报,不然不可能每次这么快就能读数据...(我说的)
基本上就是只有这两种结果:
Connected to MiioDevice {
model=yeelink.light.ceiling22,
types=miio,
capabilities=
}
(node:29924) UnhandledPromiseRejectionWarning: Error: Call to device timed out
at Timeout.retry [as _onTimeout] (/www/server/nodejs/lib/node_modules/miio/lib/network.js:487:18)
at listOnTimeout (timers.js:327:15)
at processTimers (timers.js:271:5)
(node:29924) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1)
(node:29924) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
和
Error to { Error: Could not connect to device, handshake timeout
at Timeout.handshakeTimeout.setTimeout [as _onTimeout] (/www/server/nodejs/lib/node_modules/miio/lib/network.js:427:17)
at listOnTimeout (timers.js:327:15)
at processTimers (timers.js:271:5) code: 'timeout', device: null }
告辞,尝试无果,阶段性放弃。
等大佬反馈结果...
门禁
淘宝的wifi继电器,不到20块钱:

功能很强,支持接入米家,天猫精灵等智能家具产品。
找线
拆开家里的门禁

一共四根线,喊好友来当苦力在楼下试线hhhh
结果,左1红色线是在线信号线,黄色是门禁状态线,绿色和黑色搞不清楚,反正经过测试,少了哪一根线都不能完成开门(日
并且我家门禁需要:有人按我家门铃,摘下电话机,按下开锁按钮,才能操作开门...
开始是想最坏的设计也就是长通电话线,然后操作开门..
现在一来,估计只能换别的办法了,因为要同时保活电话机的响应能力,不然有没有人按门铃都不知道。
所以我打算找时间把板子卸下来,看能不能焊一下2333
好了,这个东西先鸽...我顺便想想继电器还能用在哪儿(哭)
https://bbs.iobroker.cn/t/topic/2876 抓token看看这个
jiabccc 2020-06-08 13:59
恩,这个应该是通过模拟登陆米家下载米家服务器存储用户的token表。
现在走虚拟机也不是太麻烦(
主要是用的少
时光 2020-06-12 21:47
这种wifi继电器很容易坏,我用过的没半年就乱来了,时不时给你闪断一下,很烦人
拜仁慕尼黑 2020-05-26 12:21
哈哈哈,我都还没找到地方用
现在都还是现成的方案舒服~
时光 2020-06-12 21:45