通信协议
绿米局域网通信协议API有两个互不兼容的版本:
绿米局域网通信协议1.X,最新版本为1.1.1(2017.12.21)。
绿米局域网通信协议2.X,最新版本为2.0.1(2018.05.18)。
绿米1.X协议设备类型及对应model:
小米多功能网关:gateway
米家多功能网关升级版:gateway.v3
米家窗磁传感器:magnet
米家人体传感器:motion
米家无线开关传感器:switch
米家智能插座:plug
Aqara 86单火开关单键:ctrl_neutral1
Aqara 86单火开关双键:ctrl_neutral2
Aqara 86无线开关单键:86sw1
Aqara 86无线开关双键:86sw2
米家温湿度传感器:sensor_ht
米家魔方传感器:cube
Aqara窗帘:curtain
Aqara 86零火墙壁开关单键:ctrl_ln1、ctrl_ln1.aq1
Aqara 86零火墙壁开关双键:ctrl_ln2、ctrl_ln2.aq1
Aqara 墙壁插座:86plug、ctrl_86plug.aq1
米家天然气报警器:natgas
米家烟雾报警器:smoke
Aqara门磁传感器:sensor_magnet.aq2
Aqara人体照度传感器:sensor_motion.aq2
Aqara无线开关传感器:sensor_switch.aq2
Aqara温湿度气压传感器:weather.v1
Aqara水浸传感器:sensor_wleak.aq1
Aqara门锁:lock.aq1
绿米2.X协议设备类型及对应model:
Aqara空调伴侣升级版:acpartner.v3
智能插座:plug
墙壁插座:ctrl_86plug、ctrl_86plug.aq1
墙壁开关(零火单键):ctrl_ln1、ctrl_ln1.aq1
墙壁开关(零火双键):ctrl_ln2
墙壁开关(单火单键):ctrl_neutral1
墙壁开关(单火双键):ctrl_neutral2
窗帘电机:curtain
双路控制器:lumi.ctrl_dualchn
门窗传感器:sensor_magnet.aq2
人体传感器:sensor_motion.aq2
温湿度传感器:weather
水浸传感器:sensor_wleak.aq1
无线开关:sensor_switch.aq2
无线开关(升级版):sensor_switch.aq3
86无线开关单键:sensor_86sw1.aq1
86无线开关双键:sensor_86sw2.aq1
魔方传感器:sensor_cube.aqgl01
不支持任何局域网通信协议的设备对应model:
Aqara空调伴侣:acpartner.v1
米家空调伴侣:acpartner.v2
注意事项:
固件决定了是否支持哪个协议。
开启局域网通信协议并拥有该网关的KEY才能与该网关进行局域网完全通信。
有些设备3个表中都没有给出,一般属于支持1.X的设备,也可能不支持任何协议。
唯一支持2.X协议的网关是Aqara空调伴侣升级版(方形),小米多功能网关/米家多功能网关升级版均为1.X协议。
Aqara魔方传感器仅能接入Aqara空调伴侣升级版才能使用局域网协议控制。
发现设备
服务器发现网关(全文默认以2.X协议为例):
服务器向局域网发送UDP组播报文(IP:224.0.0.50,端口:4321),内容为
{"cmd":"whois"}
即【服务器>>组播(224.0.0.50:4321){"cmd":"whois"}】下写法同理。
网关收到信息后通过UDP单播报文回复服务器:
【网关>>单播($Host:4321){"cmd":"report",...}】
report的具体内容举例:
{
"cmd":"iam",
"ip":"192.168.0.42", //网关IP地址
"protocal":"UDP",
"port":"9898",
"model":"gateway.aq1", //网关设备类型
......
}
服务器查询网关设备
【服务器>>单播($Hub:9898){"cmd":"discovery"}】
【网关>>单播($Host:9898){"cmd":"discovery_rsp",...}】
discovery_rsp具体内容举例:
{
"cmd":"discovery_rsp",
"sid":"158d323123c9d9", //sid为网关did
"token":"TahkC7dalbIhXG22", //网关生成的随机字符串
"dev_list":[{"sid":"158d0000f1a750","model":"plug"},
{"sid":"158d00010fd645","model":"sensor_switch.aq2"}] //sid为子设备did
}
注意:“token”为网关生成的随机字符串,每10s刷新一次,在未收到设备心跳上报的token前,用户可用此token来生成写设备时的“key”。
设备查询上报
当设备状态发生变化时,【网关>>组播(224.0.0.50:9898){"cmd":"report",...}】
report具体内容举例:
{
"cmd":"report",
"model":"sensor_magnet.aq2",
"sid":"158d0000123456",
"params":[{"window_status":"open"}]
}
设备心跳上报
网关心跳上报
网关每10秒钟发送一次心跳报文,用来告诉服务器网关正常工作。若间隔65s以上未收到心跳包即表示网关处于离线状态。网关设备心跳格式如下:
【网关>>组播(224.0.0.50:9898){"cmd":"heartbeat",...}】
heartbeat具体内容举例:
{
"cmd":"heartbeat",
"model":"gateway.v3",
"sid":"f0b429b3c9d965",
"token":"1234567890abcdef", //网关生成的随机字符串
"params":[{"ip":"172.22.4.130"}] //网关IP地址
}
子设备心跳上报
子设备通过心跳告诉PC:子设备正常工作(心跳上报频率:睡眠设备是每60分钟一次,插电设备是每10分钟一次)。子设备心跳格式如下:
【网关>>组播(224.0.0.50:9898){"cmd":"heartbeat",...}】
heartbeat具体内容举例:
{
"cmd":"heartbeat",
"model":"sensor_magnet.aq2",
"sid":"158d000065a271",
"params":[{"window_status":"open"}]
}
AES-CBC 128加密
开启局域网通信协议后,服务器可以获取网关信息(接收网关上报、回应信息,对网关进行读操作),但无法控制网关(对网关进行写操作)。
要对网关及其下属设备进行管理和控制,必须拥有该网关的KEY,我把它叫做网关KEY。网关KEY可在APP上取得,为16个字节长度的字符串。
网关KEY并非直接用于控制网关,而是作为加密的密钥。服务器收到网关心跳“heartbeat”里的16个字节的“token”字符串之后,使用网关KEY对token的字符串进行AES-CBC 128加密,生成16个字节的密文,再转换为32字节的ASCII码,这才是通讯时用到的工作KEY。
AES-CBC 128初始向量定义为:unsigned char const AES_KEY_IV[16] = {0x17, 0x99, 0x6d, 0x09, 0x3d, 0x28, 0xdd, 0xb3, 0xba, 0x69, 0x5a, 0x2e, 0x6f, 0x58, 0x56, 0x2e}。
1.6 设备读写操作
读设备
服务器向网关发送读命令,网关向服务器回复读取结果。
【服务器>>单播($Hub:9898){"cmd":"read",...}】
read具体内容举例:
{
"cmd":"read",
"sid":"158d0000123456" //墙壁开关did
}
【网关>>单播($Host:9898){"cmd":"read_rsp",...}】
read_rsp具体内容举例:
{
"cmd":"read",
"sid":"158d0000123456" //墙壁开关did
}
{
"cmd":"write_rsp",
"model":"ctrl_neutral1",
"sid":"158d0000123456",
"params":[{"channel_0":"on"}]
}
写设备
服务器向网关发送写命令,网关向服务器发送回复,表示知悉。
【服务器>>单播($Hub:9898){"cmd":"write",...}】
write具体内容举例:
{
"cmd":"write",
"model":"ctrl_neutral1",
"sid":"158d0000123456",
"key":"3EB43E37C20AFF4C5872CC0D04D81314",
"params":[{"channel_0":"off"}]
}
【网关>>单播($Host:9898){"cmd":"write_rsp",...}】
write_rsp具体内容举例:
{
"cmd":"write_rsp",
"model":"ctrl_neutral1",
"sid":"158d0000123456",
"params":[{"channel_0":"on"}]
}
该“write_rsp”只代表网关收到了write命令,params里的属性状态为当前的设备最新状态,不是write之后的最终设备状态。最终的设备状态靠report报文进行上报。