『瀚思彼岸』» 智能家居技术论坛

 找回密码
 立即注册
查看: 43589|回复: 47

[技术探讨] 【学习过程】小米网关、ZIGBEE设备、HA、Homekit通讯原理

  [复制链接]

26

主题

553

帖子

2726

积分

金牌会员

Rank: 6Rank: 6

积分
2726
金钱
2148
HASS币
100

教程狂人

发表于 2018-7-17 17:09:14 | 显示全部楼层 |阅读模式
本帖最后由 Mirukuteii 于 2018-8-14 11:09 编辑


【学习过程】小米网关、ZIGBEE设备、HA、Homekit通讯原理(非学习教程)



-----------帖子还在编辑中--------------

【从入门到放弃】

绿米局域网通信协议、PyXiaomiGateway库、

HA原生xiaomi_aqara组件、Homekit组件自学过程

Lumi Zigbee PyXiaomiGateway xiaomi_aqara homekit 局域网通信协议

前言

作为论坛里面一枚小萌新,其实我的小米设备并不多。前期呢,我也和大家一样,跟着论坛里的大佬们搞定了很多小米设备接入和使用。但是随着接触的东西越来越多,战线越拉越长,这些小米设备也被我忽视掉了,再也没有好好研究过小米网关局域网通信协议PyXiaomiGateway库xiaomi_aqara组件xiaomi_miiohomekit组件这些东西。其实,想必很多人都是和我一样,抱着如何将自家的小米设备接入homekit,被那句用siri控制一切带进Hassbian这个论坛开始学习的吧,这就是所谓的“初心”吧。所以我决定沉下心来,挤点时间,不忘初心,拾起学习。同时,楼主我也感觉把学习过程记录下来是一件有意义的事,对刚入坑的坛友来讲,或许是帮助和借鉴;对自己则是一种任务牵引,则是总结和梳理,积累经验展示成果,更重要的是还可以向论坛里各位大佬学习体会、交流经验。故而立帖(挖坑,没错就是挖坑哈)。
楼主我本着边搬运边学习的想法,一开始可能自己的理解比较少,搬运的内容比较多,请理解。后期慢慢加入自己的理解体会,把学习过程转变为学习心得,最后变成教程,谢谢大家。

储备知识

  • 网络基础知识,TCP/IP协议架构,UDP组播,Socket。
  • HA的基本架构,组件结构,物联网组件的结构。
  • Python编程基础,懂基本的数据结构和代码含义。
  • (待添加)

总原理图

(待添加)

1. 绿米局域网通信协议

绿米局域网通信协议就是一套API,用于对网关及其下属ZigBee子设备(传感器、控制器等)进行管理。绿米局域网通信协议有两个互不兼容的版本:

  • 绿米局域网通信协议1.X,最新版本为1.1.1(2017.12.21)。
  • 绿米局域网通信协议2.X,最新版本为2.0.1(2018.05.18)。

按照一般理解,1.X版本的协议,主要用于米家品牌的设备;而2.X版本的协议,主要用于Aqara品牌的设备。目前来说,这两个协议,其实差别并不大,所以PyXiaomiGateway同时提供了对这两个协议的支持。

PS:绿米(lumi)即深圳绿米联创科技有限公司,其自有产品的品牌为Aqara,给小米代工产品的品牌为米家。2015年1月发布小米多功能网关,2016年3月发布小米多功能网关2代(升级版),2016年11月29日,由小米正式更名为米家。

【表1】绿米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】绿米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

【表3】不支持任何局域网通信协议的设备对应模型名(model)

Aqara空调伴侣:acpartner.v1
米家空调伴侣:acpartner.v2

注意:开启局域网通信协议并拥有该网关的KEY后,才能与该网关进行局域网完全通信。
注意:有些设备3个表中都没有给出,一般属于支持1.X的设备,也可能不支持任何协议。
注意:唯一支持2.X协议的网关是Aqara空调伴侣升级版(方形那个),小米多功能网关/米家多功能网关升级版均为1.X协议。
注意:Aqara魔方传感器仅能接入Aqara空调伴侣升级版才能使用局域网协议控制。
疑问:米家空调伴侣由于硬件性能关系,存在但没有公开局域网通信协议。
猜测:如果你的设备在表2而不在表1中,说明很可能只支持2.x协议,必须接入Aqara空调伴侣升级版;如果你的设备在表1而不在表2中,则说明支持1.X协议,并且按绿米的说法,也能接入Aqara空调伴侣升级版(这货可能2个协议都能兼容)。如果在表1又在表2中,那么就要看固件了,老固件支持1.X,新固件是全支持还是只支持2.X未知。

8.14日更新:今天重新读到这里,感觉以前对子设备的协议支持理解有偏差,实际上协议只是网关的协议,是否支持协议并不应该是子设备的问题,等下步分析zigbee通讯时再做修订。

1.1 局域网通信协议主要功能(目前)

  • 发现与查询设备
  • 设备状态上报
  • 对设备进行读写操作
  • 设备心跳上报

1.2 发现与查询设备

首先,为了方便,我将局域网内需要与小米网关及下属设备建立通信的主机(比如HA所在主机)简称为服务器,IP记作$Host,小米网关IP记作$Hub(下同,只是方便文章编写,和协议本身以及编程毫无关系),服务器可以是电脑也可以是手机等等,必须按照网关所支持的局域网通信协议,通过自己编写或者通过库函数编写,以及现成的软件、APP,才能完成相应的通讯。

①服务器发现网关(全文默认以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”。

1.3 设备查询上报

  • 当设备状态发生变化时,【网关>>组播(224.0.0.50:9898){"cmd":"report",...}】
  • report具体内容举例:
    {
    "cmd":"report",
    "model":"sensor_magnet.aq2",
    "sid":"158d0000123456",
    "params":[{"window_status":"open"}] 
    }

1.4 设备心跳上报

①网关心跳上报

  • 网关每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"}]
    }

1.5 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报文进行上报。

1.7 两个协议格式之间的区别与联系

TIM图片20180718122203.png

讨论问题:绿米局域网通信协议能否跨越局域网使用?

2.PyXiaomiGateway库

上面学习了绿米局域网通信协议,但是光有通信协议还不够,要把这些协议具体化为实现方法才行。你可以自己按照协议写程序,也可以调用别人已经写好的库函数,比如PyXiaomiGateway,这是一个HA组件正在使用的现成的库函数,目前版本为0.9.5。

库的源代码地址:
https://github.com/Danielhiversen/PyXiaomiGateway

该库提供了两个类

  • XiaomiGatewayDiscovery
  • XiaomiGateway

XiaomiGateway类

  • 用法:XiaomiGateway(ip_adress, port, sid, key, sock, proto=None)
    参数ip_adress:网关的ip地址;
    参数port:网关的通信端口,9898;
    参数sid:网关的sid;
    参数key:网关的key,16位;
    参数sock:传输用的socket;
    参数proto:网关的协议版本;

调用这个类后,会发生这些事:
1.若没有指定协议版本,则向网关发送read命令,获取网关的协议版本。
2.向网关发送discovery(get_id_list)命令,搜索网关及其下属设备的sid(设备编号),并保存到sids(设备编号列表)中,同时保存网关的token。
3.根据sids中的设备编号,向每个设备都发送read命令,获取所有设备的信息,若设备的model在支持列表内,则将此设备的信息按照类型,注册到self.devices中。
所以,将这个类实例化后,顺利的话,可从对象的devices属性中获取设备信息。

XiaomiGatewayDiscovery类

  • 用法:XiaomiGatewayDiscovery(callback_func, gateways_config, interface)
    参数callback_func:指定回传函数;
    参数gateways_config:指定网关配置,可以多个网关;
    参数interface:绑定的服务器IP,可填"any"。

调用这个类后,会发生这些事:
1.自动配置基本参数(组播地址,端口,UDP协议等);
2.初始化socket,绑定interface。
这个类还有一下几个方法可供调用:

  • discover_gateways 发现网关
  • listen 监听端口
  • stop-listen 关闭监听

discover_gateways方法

  • 用法:discover_gateways()

调用这个方法后,会发生这些事:
1.建立socket,绑定interface。
2.若gateways_config中存在完整配置项,则读取网关配置,转换主机名为ip地址等, 并调用XiaomiGateway类,保存为self.gateways[ip_adress]。
3.服务器向网内UDP组播,发送whois命令,通过网关回应,补充网关配置,并调用XiaomiGateway类,保存为self.gateways[ip_adress]

3.xiaomi_aqara组件

小米aqara平台
https://github.com/home-assistant/home-assistant/blob/dev/homeassistant/components/xiaomi_aqara.py

xiao_aqara组件是hass与小米网关通讯的重要模块,目前包含:
核心:xiaomi_aqara.py
平台:binary_sensor/xiaomi_aqara.py, sensor/xiaomi_aqara.py, switch/xiaomi_aqara.py, light/xiaomi_aqara.py, cover/xiaomi_aqara.py, lock/xiaomi_aqara.py。

核心xiaomi_aqara.py

该文件主要包含两个部分:
组件加载方法Setup()与小米设备类库XiaomiDevice()

Setup()主要负责接收用户配置,并初始化和启动小米组件,尝试通过PyXiaomiGateway通讯库与小米网关通信并取得网关对象。
同时定义并注册了4项服务:播放网关音乐、停止播放网关音乐、添加Zigbee子设备,移除Zigbee子设备。

XiaomiDevice()主要负责通过PyXiaomiGateway通讯库,建立小米设备在hass架构下的实体模型,实现通用的功能逻辑,构建了以下属性和方法:

属性:设备名name、唯一识别码unique_id(uid)、反馈特性available、轮询特性should_poll、设备自属性device_state_attributes。

方法:
async_added_to_hass:跟踪可反馈的设备。
push_data:更新Hass实体状态
parse_voltage:解析电压
parse_data:解析数据

平台XXXX/xiaomi_aqara.py

各类平台py文件,通过小米设备类库XiaomiDevice()与平台对应的核心类库生成相应的小米平台类。
从而实现对应的设备的完整功能。

4.homekit组件





评分

参与人数 2金钱 +20 收起 理由
flashsoft + 10 坐等更新
咸味土豆 + 10 膜拜大神!

查看全部评分

回复

使用道具 举报

26

主题

553

帖子

2726

积分

金牌会员

Rank: 6Rank: 6

积分
2726
金钱
2148
HASS币
100

教程狂人

 楼主| 发表于 2018-7-17 17:34:21 | 显示全部楼层
占座自用占座备用
回复

使用道具 举报

8

主题

182

帖子

784

积分

高级会员

Rank: 4

积分
784
金钱
602
HASS币
0
发表于 2018-7-17 21:46:43 | 显示全部楼层
占座
回复

使用道具 举报

13

主题

258

帖子

2072

积分

金牌会员

Rank: 6Rank: 6

积分
2072
金钱
1814
HASS币
0
发表于 2018-7-18 08:56:58 | 显示全部楼层
前来围观天书
回复

使用道具 举报

5

主题

95

帖子

606

积分

高级会员

Rank: 4

积分
606
金钱
511
HASS币
0
发表于 2018-7-18 09:10:04 | 显示全部楼层
占座围观
回复

使用道具 举报

74

主题

1942

帖子

7885

积分

元老级技术达人

积分
7885
金钱
5893
HASS币
430

活跃会员教程狂人

发表于 2018-7-18 09:17:06 | 显示全部楼层
楼主真棒,么么哒。
所有过往,皆为序章。
回复

使用道具 举报

19

主题

341

帖子

1133

积分

金牌会员

Rank: 6Rank: 6

积分
1133
金钱
792
HASS币
0
发表于 2018-7-19 09:50:07 | 显示全部楼层
吃瓜群众看热闹。
回复

使用道具 举报

10

主题

55

帖子

1204

积分

论坛技术达人

积分
1204
金钱
1134
HASS币
60

教程狂人

发表于 2018-7-20 13:34:13 | 显示全部楼层
学习学习
回复

使用道具 举报

4

主题

46

帖子

290

积分

中级会员

Rank: 3Rank: 3

积分
290
金钱
244
HASS币
0
发表于 2018-7-20 18:06:34 | 显示全部楼层
学习学习了
回复

使用道具 举报

6

主题

171

帖子

1057

积分

论坛技术达人

积分
1057
金钱
876
HASS币
20
发表于 2018-7-20 23:28:39 | 显示全部楼层
大神,腻害!
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|手机版|小黑屋|Hassbian

GMT+8, 2024-4-27 02:08 , Processed in 0.112618 second(s), 36 queries .

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表