6 月 16 日晚间,#米家崩了#这一话题“喜提”微博热搜榜。广大网友们纷纷留言:”为什么我的米家智能家居设备都失灵了?“、”我还以为买的东西(米家智能家居)都坏了“。
原来当日米家 App 内的各种设备都无法远程控制,大量用户都表示在尝试了重启路由器、重装 App 等一系列操作之后,还是无法正常使用米家 App 来控制智能家居。
随后,米家 App 发表官方声明,原来是出现了网络故障,导致 App 服务异常。
类似因为网络波动导致智能家居设备一下子成了”摆设”的问题在诸如谷歌智能家居系统 Google Home、亚马逊 Alexa 智能音箱上都屡见不鲜。这主要是因为这些智能家居极大地依赖厂商提供的云服务来进行配置和复杂命令处理,因此一旦厂商的云服务出现网络不稳定的情况,就会导致很多用户无法正常使用 App 控制智能家居,这无疑给用户体验造成了很大影响。
除此之外,还有另外一种情况,就是有一些小众厂商的智能家居产品没用几年也会成为了“摆设”。这主要是因为这一部分小型厂商基于多种原因,自主终止了相关的云服务。这样一来,用户花费高昂价格购买的智能家居设备就再也无法连上云端了,简直成为了无用的”砖头“。
智能家居控制大难题
一直以来,智能家居厂商一般都推荐“将用户的智能设备连上云端”这种模式。这样的好处确实很多,比如:可以收集更多的用户数据,帮助提供更“智能“的服务(例如智能音箱语音对话功能)、方便用户在无法访问智能家居本地网络的情况下远程操控这些智能家居。不过,这一模式带来的问题也显而易见。
首先,这个模式极其依赖厂商云平台的稳定性,一旦智能家居厂商云服务的网络出现波动,就很容易出现类似米家 App 奔溃而无法操纵智能家居的问题;其次,使用其他厂商的服务器,用户数据就必须上传到厂商的云服务器,这对于某些“数据安全性和隐私性要求较高的用户”来说,无疑是一个潜在的威胁。
既然有这么多问题,那么有没有不如此依赖云服务的模式呢?答案是:有的。苹果就是此类方案的最大推动者,其 HomeKit 系统就高度强调本地化联动,让用户可以选择将数据限定在局域网内使用。另外,苹果还在今年(2022 年)的全球开发者大会(WWDC)上推出了 Matter 协议。这一新协议具有极强的开放性和互联互通性,一改之前苹果独立生态的原则。也就是说,任何支持 Matter 协议的智能家居设备都可以互联互通,而且无需经过复杂的设置和匹配,就可以通过苹果的“家庭“ app 来控制。
如此一来,就不会再出现类似于买了小米智能门锁、绿米智能开关、飞利浦智能灯就得下载三家 App 来分别控制的这种糟糕使用体验了。
那么,在等待设备引入 Matter 协议支持的这段时间里面,有没有什么方法能够让我们体验在本地就能支持不同厂商设备的智能家居互联互控呢?我把目光投向了一款叫做 Zigbee2MQTT 的开源项目。
Zigbee2MQTT 开源项目
目前,我的身边就有一只绿米 LED 灯泡 T1以及一个绿米魔方控制器,这两者都支持 Zigbee 协议。我们都知道,Zigbee 是一种低速短距离无线传输协议,采用动态自主的路由协议。它和蓝牙技术相比具有更低的功耗、更低的成本、更高的可靠性等特点,因此支持 Zigbee 的智能家居设备在市场上很常见。
现在,我将关键词锁定为“Zigbee”,在经过了一段时间的搜寻后,我找到在开源社区中非常受欢迎的开源项目:Zigbee2MQTT:它可以在本地网络实现不同厂商的 Zigbee 设备互联互控。
在我写本教程的时候(2022 年 7 月),该项目已经可以支持来自 135 个厂家的 2301 款 Zigbee 智能家居设备了,而且数量还一直在增长。
我的设想是这样的,因为绿米的魔方控制器可以识别 6 种动作,推、摇、转、敲击、翻转 180°、翻转 90°,因此我可以将其配置成一个“控制器”,来控制智能灯泡的开关、冷暖、明暗状态。而 Zigbee2MQTT 就起到了将 Zigbee 数据通过 MQTT 协议发布、转发的桥接作用。
为了搭建这个网络,我们还需要一个 Zigbee 网关。Zigbee 网关有多种形式,包括 USB 接口的和使用网线接口。为了易于部署,我直接选择了最为常见的 USB 直插型 Zigbee 网关,价格只需要人民币 100 多元。因为网关设备需要一直开机,所以功耗越低越好,同时我手边刚好有一台闲置的树莓派 4B,因此,我计划将所有的服务都搭建在树莓派上,让它成为一台 Zigbee 网关。
Zigbee2MQTT 软件部署
相关的软件部署非常简单,因为 Zigbee2MQTT 提供了容器镜像,我们可以使用 docker-compose 或者容器管理工具来部署。具体的 docker-compose 文件可以参照官网的示例。此处为了易于展示,我们使用了 Portainer,这是一款可视化容器管理工具,在确保你已经安装了 docker 的前提下,你可以使用下面的命令启动 Portainer:
1 2 3 4 5 6 |
docker volume create portainer_data docker run -d -p 8000:8000 -p 9443:9443 --name portainer \ --restart=always \ -v /var/run/docker.sock:/var/run/docker.sock \ -v portainer_data:/data \ portainer/portainer-ce:2.14.0. |
Portainer 的登录页面是 :9443″ >https://<server>:9443, 尖括号内是你部署 Portainer 服务的网址。登陆后按照页面提示设置好账户名和密码就可以开始用了。接着你可以搭配使用我们提供的 Portainer App 模板,该模板内含十多种 Home Server 和家庭自动化常用镜像的部署模版,可以做到“傻瓜式”一键部署。在 Portainer 的 Settings 页面,输入下方提供的 App Template 网址即可:
https://raw.githubusercontent.com/RAKWireless/portainer-templates/master/portainer_app_template.json
点击“Save Settings”保存,接着在 App Template 页面你将看到如下的 App 列表,点击 Zigbee2MQTT 即可进入服务配置页面:
在配置页面你可以修改一些基础设置
设置的大部分是无需更改,保持默认即可,其中最重要的就是需要填入 USB 直插式 Zigbee 网关的设备地址。Zigbee2MQTT 官网提供了一个最简单粗暴的方法,就是插拔 USB 网关,然后在终端中输入 dmesg 来查看新增/消失的 USB 设备。我购买的 USB 式 Zigbee 网关在树莓派上被识别成了/dev/ttyUSB0,因此我只需要修改对应的选项(即 Zigbee Adapter Path 选项)即可。
接下来直接点击页面左下角的“Deploy”就可以部署该服务了。部署完成的时间取决于你和 docker-hub 之间网络连接速度,拉取镜像和部署的过程可能会持续数分钟,你可以提前将 docker-hub 的镜像源换成国内源以节省时间。
Zigbee2MQTT 的网页前端页面地址是 :8002″ >http://<server-address>:8002。根据你的实际情况,将<server-address>改成你的页面 IP 即可,例如 http://10.2.21.157:8002。你可以在之前的设置页面中修改前端端口选项,以防止和树莓派上已在使用中的端口产生冲突。
Zigbee2MQTT 的可视化页面如下:
添加终端设备
现在,我们开始添加终端设备。关于 Aqara(绿米)智能家居魔方,我们可以参照 Zigbee2MQTT 提供的说明,只需长按魔方内部的按钮,等待数秒,之后页面上会显示魔方正在加入的信息。之后短按按钮,直到界面右上角出现加入成功的标识。而根据 Zigbee2MQTT 文档上对 Aqara(绿米)T1 智能 LED 灯泡的描述,绿米智能灯则采用了另外一种加入网络的方式。在把灯泡拧到插槽上后,你需要打开灯,然后关闭,确保每次开灯关灯之间的时间间隔小于 2 秒,重复 5 次,你会看到灯泡开始变亮并且以很慢的速度闪烁两下,之后观察页面右上角是否有成功加入的提示出现。如果没有,则重复该过程。两个设备成功连入 Zigbee2MQTT 后,会在页面上看到如下信息。可以看到此时两个 Zigbee 设备都添加成功了,但是它们的 Friendly Name 都太长了,为了后续方便辨识这两个设备,我们需要编辑其名字。
此处我们对这两个设备进行了重命名,这样后续使用起来更加方便。你可以将名字更改为任何的名称,但是要确保更改后的名字和我们后续在自动化脚本中订阅或发布的 MQTT 主题一致。
修改后设备名字就变成了这样:
现在你可以单击每个设备,然后在 Zigbee2MQTT 的页面中查看每个设备的状态或者直接控制它们。
编写 Python 自动化脚本订阅或发布主题
由于 Zigbee2MQTT 提供了发布/订阅 MQTT Topic 的功能,我们可以编写一个 Python 自动化脚本来订阅/发布主题,以此来实现直接通过控制魔方来控制智能灯的冷暖、明暗、以及开关。
这里我们提供了一个很基础的 Python 脚本,请确保你已经在树莓派或者在同一个局域网的电脑上已经安装了 Python3 和 paho-mqtt 库,或者你可以运行下面这个命令来安装它:
1 |
pip3 install paho-mqtt |
这个脚本会自动订阅一个 MQTT 主题: ‘zigbee2mqtt/cube’, 即订阅灯泡的 MQTT 主题
并将控制命令发布到这个主题:‘zigbee2mqtt/light-bulb/set’,即发布到灯泡的 Set 主题
脚本会依据检查到魔方的各种动作,做出以下判定:
- 一旦检测到魔方的“fall”动作,就会触发灯泡的开关;
- 一旦检测到魔方的“flip90”动作,就会触发将灯泡的色温设置为冷色;
- 一旦检测到魔方的“flip180”动作,就会触发将灯泡的色温设置为暖色;
- 一旦检测到魔方的“slide”动作,就会厨房将灯泡的色温设置为中性色温;
- 您还可以通过顺时针(rotate-right)或逆时针(rotate-left)旋转来控制智能灯泡的亮度。
以下是 Python 示例脚本:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 |
#!/usr/bin/env python import paho.mqtt.client as mqtt_client # mqtt server IP broker = '10.2.21.157' #替换为你的host-ip # mqtt server port port = 1883 # mqtt username # mqtt_username = 'xxx' # mqtt password # mqtt_password = 'xxx' # mqtt subscribe topic and publish topic sub_topic = 'zigbee2mqtt/cube' pub_topic = 'zigbee2mqtt/light-bulb/set' # define keywords for the cube and corresponding message for light_bulb keyword_toggle = "fall" msg_toggle = '{"state":"TOGGLE"}' keyword_coolest = "flip90" msg_coolest = '{"color_mode":"color_temp","color_temp":153,"transition":1}' keyword_warmest = "flip180" msg_warmest = '{"color_mode":"color_temp","color_temp":370,"transition":1}' keyword_neutral = "slide" msg_neutral = '{"color_mode":"color_temp","color_temp":200,"transition":1}' keyword_increase_brightness = "rotate_right" msg_increase_brightness = '{ "brightness_step": 40,"transition":2 }' keyword_decrease_brightness = "rotate_left" msg_decrease_brightness = '{ "brightness_step": -40,"transition":2 }' def connect_mqtt() -> mqtt_client: def on_connect(client, userdata, flags, rc): if rc == 0: print("Connected to MQTT Broker!") else: print("Failed to connect, return code %d\n", rc) client = mqtt_client.Client() client.on_connect = on_connect client.connect(broker, port) return client def subscribe(client: mqtt_client): def on_message(client, userdata, msg): print(f"Received `{msg.payload.decode()}` from `{msg.topic}` topic") payload = str(msg.payload) print(payload) if keyword_toggle in payload: print("toggle it") publish(client, msg_toggle) elif keyword_coolest in payload: print("set color_temp to coolest") publish(client, msg_coolest) elif keyword_warmest in payload: print("set color_temp to warmest") publish(client, msg_warmest) elif keyword_neutral in payload: print("set color_temp to neutral") publish(client, msg_neutral) elif keyword_increase_brightness in payload: print("increase brightness") publish(client, msg_increase_brightness) elif keyword_decrease_brightness in payload: print("decrease brightness") publish(client, msg_decrease_brightness) else: print("nothing detected") client.subscribe(sub_topic) client.on_message = on_message def publish(client, msg): try: client.publish(pub_topic, msg, qos=0, retain=False) except Exception as e: raise e finally: pass def run(): client = connect_mqtt() subscribe(client) client.loop_forever() if __name__ == '__main__': run() |
确保该脚本在树莓派上后台运行,就可以用魔方来控制灯了。
对了,Zigbee2MQTT 还可以支持设备分组,所以如果你有更多的灯或者其他 Zigbee 智能家居设备,也可以在 Zigbee2MQTT 的配置页面上,将这智能家居分到一组,然后用魔方来控制整个组的状态。
文章翻译者:Taylor Lee,瑞科慧联(RAK)高级嵌入式开发工程师,有丰富的物联网和开源软硬件经验,熟悉行业主流软硬件框架,对行业发展动向有着敏锐的感知力和捕捉能力。