向所有家庭自动化爱好者致以问候。 我决定分享使用带ZigBee界面的小米无线设备的经验。 老实说,我反对在任何自动化系统中使用任何无线设备,从大型对象的严重过程控制系统到小型自动化系统(例如火灾报警系统或智能家居),但是...小米解决方案充斥着价格便宜,价格适中,出色的设计以及用户的积极反馈,我决定尝试。
本文应作为将ZigBee设备集成到智能家居基础架构中的分步说明。 此处描述的绝不是公理,您可以找到许多其他方式连接ZigBee设备。 但是,如果您跳过详细说明,则可以使用ZigBee和ioBroker的示例(稍后再介绍),将来自不同制造商的设备组合到一个本地平台中的复杂性或易用性印象。 在本文中,我将告诉您如何将设备连接到智能家居,如何在平板电脑上或仅在浏览器中显示来自设备的信息,以及如何通过电报发送有关设备状态更改的消息。 如果您对我感兴趣,那么我要一只猫。
根据小米制造商的说法,用户应使用具有云连接和Wi-Fi网关的本机应用程序用于ZigBee设备。 然而,很早就已知一种用于在应用中激活开发者模式以获得控制令牌的方法。 因此,在本地网络内部,您可以与网关进行通信,这允许您执行
mihome驱动程序,该驱动程序是ioBroker的一部分。
ioBroker是物联网的开放平台,包括用于构建智能家居系统的平台。 在上
一篇文章中可以找到什么是ioBroker。
现在,我的智能家居正在“旋转”在Cubietruck ARM板上,带有一些“主体套件”,包括80GB硬盘,5000mAh可充电电池,1线USB主设备,用于使用Modbus协议轮询设备的USB-RS485转换器。 随根分区的传输,将Armbian OS安装在硬盘上,只有引导加载程序保留在microSD存储卡上。
我通过购买温度和湿度传感器开始了解小米无线设备。 当时,它们集成的唯一可能性是
mihome驱动程序,上面已经提到过。
Mihome驱动程序
向系统添加驱动程序非常简单,您需要单击可用驱动程序列表中的“ +”按钮,并注意安装过程。
我不会描述本地Android应用程序Mi Home的安装和初始配置,您可以在驱动程序的
github页面或Internet上看到它。 因此,开发人员模式被激活,令牌被接收,我们配置mihome适配器,如果尚未启动,则保存并运行。
Mi Home应用程序中连接的小米网关和设备应出现在对象树中。
接下来,您可以配置新创建的对象。 例如,存储温度和湿度传感器的历史记录。 我将SQL驱动程序用于在SQLite数据库上配置的历史数据。
变量历史记录的存储是在系统对象窗口中配置的:在对象层次结构中,您需要进入变量本身,然后使用右侧的扳手按按钮。 在“设置”选项卡上,我已通过传感器激活了历史记录存储-仅更改了变量。
其他设定:
- 最小间隔为10秒-如果变量将更频繁地更改,则数据库中的条目将被忽略
- 每300秒(5分钟)记录一次值-如果变量的更改时间超过5分钟,则当前值仍会写入数据库
- 值类型-数字
- 保质期-1年
通过本机应用程序添加新设备。 即 您需要根据随附的说明将新设备与网关配对,然后它将自动出现在ioBroker对象列表中。
Zigbee驱动程序
使用本机小米应用程序给我带来了不便,特别是因为我必须购买带有中文插件的网关,在我的情况下,除了与设备进行通信外,它对任何事情都没有用。 是的,它可以用作夜灯,或在某些情况下使用光传感器。 在Internet上,您可以找到有关如何将播放列表“馈送”到网关以播放其自己的广播电台的说明,但是这些都没有困扰我。 另外,以为我的数据在某个地方泄漏,正在处理和存储在某个地方的想法困扰着人们。
ioBroker平台的活跃用户之一在Internet上找到了node.js上的
zigbee-shepherd库,其中提到了连接小米设备的问题。 ioBroker的驱动程序是在此基础上编写的,该驱动程序的作者不仅限于小米设备,支持的设备列表也在不断更新,可以在该项目的
github页面上找到。
它应该使用基于TI
CC25xx芯片的廉价现成设备作为网络协调器。 您可以购买现成的具有USB连接和内置天线的ZigBee模块,以及更昂贵和更严格的型号:带有外部天线,放大器和通过UART的连接。
要使用驱动程序,您只需要更改固件。 因此,事实证明该驱动程序不需要昂贵的网关,也不需要Wi-Fi网络。 “入口”是协调器-一种基于带有特殊固件的SS25xx芯片的设备。 通过协调器,zigbee设备与智能家居系统之间可以进行直接通信,还可以绑定新设备。
作为协调员,我使用现成的主板,该主板基于带有外部天线的CC2530芯片,并通过UART连接到服务器。
对于设备固件,购买了一个特殊的SmartRF04EB调试器,我将其的microUSB端口连接到了计算机,并通过接线连接了ZigBee模块,以根据方案进行调试:
SS2530 | SmartRF04EB板 |
---|
P22 | 直流电 |
P21 | DD |
Rst | 重设 |
地线 | 地线 |
Vcc | 3.3伏 |
在项目的
github页面上,下载固件(此设备的文件名为CC2530ZNP-Pro-Secure_LinkKeyJoin.hex)和固件程序(闪存编程器),然后将必要的驱动程序添加到系统中。
将调试器板连接到计算机的USB端口时,程序将立即显示连接的设备。 您只需要指定固件文件的路径,然后单击“执行操作”按钮即可
ZigBee模块端口P03(Rx)和P02(Tx)连接到UART4(在操作系统中,如ttyS4)cubietruck板上,由3V3供电,GND接在相邻的引脚上。 为了稳定运行,您仍然需要将协调器的端口P20,P4,P5接地。 如我上面所写,我使用的是Armbian OS,使用“
系统 -
硬件”部分中的
armbian-config命令非常简单地激活UART端口,您需要激活所需的端口并重新启动系统。
一键式从管理面板添加zigbee驱动程序。
在我的情况下,协调器连接到端口
/ dev / ttyS4 (如上所述),我们在设置中指定它。
默认情况下可以保留其他设置。 首次启动驱动程序后,您需要配对(添加)设备。 通过该驱动程序在
github上的完整说明比通过本机应用程序进行配对要复杂一些,但是我没有任何问题。
因此,例如,添加小米按钮(Mijia系列),为此,我们在驱动程序设置中按绿色按钮,然后按照说明先用回形针按住背面的配对按钮,直到LED开始闪烁,然后大约每2秒单击一次此按钮,查看配对进度。
我的公寓并不大,所有设备的连接都稳定,即使平台上有开门传感器(钢筋混凝土墙为100mm,直5m)也是如此。 当我决定在街道空气中增加一个温度和湿度传感器时,问题就开始了,我从绝缘凉廊的侧面将其安装在房屋的外墙上。 同样在街上的弱传感器的信号也没有到达自动化柜。 这个问题可以简单地解决-您需要在ZigBee网络中添加路由器并将其放置在距离传感器较近的位置。 某些无线设备(例如小米插座)可以用作路由器,但我没有此类设备。 我不想购买昂贵的插座或可控灯,只是为了从街道上的传感器“转发”数据。 事实证明,对于基于SS25xx芯片的相同终端设备,有一种特殊的固件,可将它们用作系统中的路由器。 结果,我添加了基于CC2531芯片并带有USB连接的路由器。 我不会详细介绍固件过程,该方案和固件文件本身可以在项目的
github页面上找到。
由于实际上没有发生通过USB端口与模块进行通信的情况,因此我暂时将其插入USB端口进行充电,然后将其插入厨房的插座。 我计划在不久的将来将其固定在凉廊上的正常建筑物中,并使用家用UPS的正常电源供电。
将路由器添加到系统的过程很简单:按下驱动程序中的配对按钮,然后在打开的路由器上按下S2按钮几次,直到设备连接。
让我们添加一个温度/湿度传感器的示例,该传感器位于室外凉廊上,可以通过路由器工作。
您可以简单地通过驱动程序进行配对,并且传感器应该通过路由器连接(如果距离更近)。 但是您可以强制指定配对通过特定路由器,为此,请在路由器图标上的设备列表中,单击绿色的配对按钮。
确保传感器连接正确-请参阅网络图。
该图显示了设备的配对线,指示网段之间的信号质量。 就像我在上面写的那样,我有一个35平方米的小odnushka,因此地图相当适中。 在其他用户的允许下,我将使用更大的卡发布可能的选项。
通过此适配器的某些小米设备的功能甚至比通过本机应用程序和mihome驱动程序获得的功能还要多。 将来,我想扩展网络并尝试新设备,尤其是用于窗帘和传感器立方体的智能驱动器。
物料驱动
我们获得了数据,将执行器捆绑在一起,现在该如何处理? 首先,让我们在漂亮的界面中进行显示。 我在VIS驱动程序中有一个大项目,该项目存在多个版本,具有不同的权限,但是有足够的材料来撰写单独的文章。 也许她会是下一个。
为了简单快速地显示数据和控制各种设备,我使用了材质驱动程序。 单击几次即可像往常一样安装。 界面磁贴根据类别设置显示数据,这些设置添加在同名的界面管理窗口中。 类别列表仅受想象力限制,我使用“房间功能”方案,并根据它们进行分组。 添加了所有房间(厨房,走廊,房间,阳台等)和功能(照明,传感器,系统等)。
现在,在系统对象的设置窗口中,将需要在屏幕上显示的变量指示房间,功能和分配角色(为了正确显示)。 一个例子是小米的室内墙壁温度和湿度传感器,通过蓝牙连接。
根据这些设置,磁贴将在屏幕上显示数据,图标将收紧,并且对于某些类型的控件,将可用。
示例1.一类所有信息的输出-函数。 整个公寓照明。
示例2.有关该房间的所有信息的输出-浴室。
示例3.所有小米无线设备
的电压和电池放电水平。
该驱动程序为我工作,可通过移动电话(墙上的固定平板电脑)对系统进行本地控制。 有时我通过VPN连接。 为了远程控制和查看状态,以接收通知,我使用电报驱动程序。
电报驱动程序
我不会描述安装,只需要检查设置即可。 我通过定期轮询(默认为300毫秒)和通过代理服务器进行连接来使用操作模式。 要获得令牌,您需要与机器人的创建者BotFather“对话”。 过程很简单-查找该机器人的搜索,提供创建新机器人的命令,指定其唯一名称和密钥,在驱动程序设置中进行指示,并为安全起见,请确保指定“欢迎”密码。 与新用户交流时,您的机器人会询问他。
现在,您需要通过机器人配置通信案例。 您可以使用text2command驱动程序或JavaScript来执行此操作。 历史上曾经发生过这样的事情,我同时以文本和Blockly块的形式使用JavaScript。 安装JS驱动程序应该不会造成困难,在这种情况下不需要配置。 安装和启动后,需要启用菜单的显示以创建和编辑脚本。
示例1.警报。
首先,让我们尝试发送有关前门打开的通知。 我在前门上有一个小米无线传感器簧片开关。 当我在工作时,我会监视妻子如何走小路。 在公共组中创建一个新脚本。
指定将被“绘制”的块,并将其称为“ telegram_bot”。
在“事件”组中,我们使用反应单位来更改变量并将其拖动到工作字段中。
接下来,选择我们要订阅的对象的ID,然后通过“ if”-“否则”通过true / false插入对象检查。 结果应该类似于以下内容。
好的,现在我们在跑步,打开门-关闭门,然后在电报中看到消息。
示例2.通过菜单进行照明控制。
一个例子更加复杂,让我们使用电报中的菜单按钮进行照明控制。 在这里,您必须稍稍徘徊,即 创建脚本时,需要选择JS。 任务大致如下:以菜单形式制作按钮,并在按钮签名文本中立即显示灯具的状态。 仍要尝试确保当您按下按钮时,照明状态会反转,并且按钮文本中的状态会随着灯泡的出现/消失而立即更新。 另外,如果菜单已经在运行,并且使用按钮手动打开/关闭灯,则还必须使用灯具的状态来更新菜单。
该代码包含注释,并且相对简单:
| // ID |
| const LIGHT1 = ' ', CH_LIGHT1 = 'mqtt.0.PLC55_Lighting.lighting.MainRoom_main1'; |
| const LIGHT2 = ' ', CH_LIGHT2 = 'mqtt.0.PLC55_Lighting.lighting.MainRoom_main2'; |
| const LIGHT3 = ' ', CH_LIGHT3 = 'mqtt.0.PLC55_Lighting.lighting.MainRoom_sec'; |
| const LIGHT4 = ' ', CH_LIGHT4 = 'mqtt.0.PLC55_Lighting.lighting.Kitchen_main'; |
| const LIGHT5 = ' 1', CH_LIGHT5 = 'mqtt.0.PLC55_Lighting.lighting.Kitchen_sec_top'; |
| const LIGHT6 = ' 2', CH_LIGHT6 = 'mqtt.0.PLC55_Lighting.lighting.Kitchen_sec_bottom'; |
| const LIGHT7 = ' ', CH_LIGHT7 = 'mqtt.0.PLC55_Lighting.lighting.BathRoom_main'; |
| const LIGHT8 = ' ', CH_LIGHT8 = 'mqtt.0.PLC55_Lighting.lighting.Hall_main'; |
| const LIGHT9 = ' ', CH_LIGHT9 = 'mqtt.0.PLC55_Lighting.lighting.Balcon_main'; |
| |
| // |
| function sendLightMenu(message_text) { |
| // |
| var l1 = getState(CH_LIGHT1).val ? '💡' : ''; |
| var l2 = getState(CH_LIGHT2).val ? '💡' : ''; |
| var l3 = getState(CH_LIGHT3).val ? '💡' : ''; |
| var l4 = getState(CH_LIGHT4).val ? '💡' : ''; |
| var l5 = getState(CH_LIGHT5).val ? '💡' : ''; |
| var l6 = getState(CH_LIGHT6).val ? '💡' : ''; |
| var l7 = getState(CH_LIGHT7).val ? '💡' : ''; |
| var l8 = getState(CH_LIGHT8).val ? '💡' : ''; |
| var l9 = getState(CH_LIGHT9).val ? '💡' : ''; |
| // |
| sendTo('telegram.0', { |
| text: message_text, |
| reply_markup: { |
| keyboard: [ |
| [LIGHT1+l1, LIGHT2+l2, LIGHT3+l3], |
| [LIGHT4+l4, LIGHT5+l5, LIGHT6+l6], |
| [LIGHT7+l7, LIGHT8+l8, LIGHT9+l9] |
| ], |
| resize_keyboard: true, |
| one_time_keyboard: true |
| } |
| }); |
| } |
| // |
| function changeLight(room, channel) { |
| // |
| var alive = getState("mqtt.0.info.connection").val; |
| if (alive.includes("PLC55_Lighting")) { |
| if (getState(channel).val) { |
| setState(channel, false, false, function () { |
| sendLightMenu(room+' '); |
| }); |
| } else { |
| setState(channel, true, false, function () { |
| sendLightMenu(room+' '); |
| }); |
| } |
| } else { |
| sendLightMenu(' OFFLINE'); |
| } |
| } |
| // request , |
| on({id: "telegram.0.communicate.request", ack: false, change: 'any'}, function (obj) { |
| var msg = obj.state.val; |
| var command = obj.state.val.substring(obj.state.val.indexOf(']')+1); |
| var user = obj.state.val.substring(obj.state.val.indexOf('[')+1,obj.state.val.indexOf(']')); |
| var chat_id = getState("telegram.0.communicate.requestChatId").val; |
| var message_id = getState("telegram.0.communicate.requestMessageId").val; |
| var handled = false; |
| // |
| if (command == '/start' || command == '/menu' || command == '' || command == '') { |
| sendLightMenu(""); |
| handled = true; |
| } |
| //######################## Lighting menu and commands ################ |
| if (command == LIGHT1 || command == LIGHT1+'💡') { |
| changeLight(LIGHT1, CH_LIGHT1); |
| handled = true; |
| } |
| if (command == LIGHT2 || command == LIGHT2+'💡') { |
| changeLight(LIGHT2, CH_LIGHT2); |
| handled = true; |
| } |
| if (command == LIGHT3 || command == LIGHT3+'💡') { |
| changeLight(LIGHT3, CH_LIGHT3); |
| handled = true; |
| } |
| if (command == LIGHT4 || command == LIGHT4+'💡') { |
| changeLight(LIGHT4, CH_LIHT4); |
| handled = true; |
| } |
| if (command == LIGHT5 || command == LIGHT5+'💡') { |
| changeLight(LIGHT5, CH_LIGHT5); |
| handled = true; |
| } |
| if (command == LIGHT6 || command == LIGHT6+'💡') { |
| changeLight(LIGHT6, CH_LIGHT6); |
| handled = true; |
| } |
| if (command == LIGHT7 || command == LIGHT7+'💡') { |
| changeLight(LIGHT7, CH_LIGHT7); |
| handled = true; |
| } |
| if (command == LIGHT8 || command == LIGHT8+'💡') { |
| changeLight(LIGHT8, CH_LIGHT8); |
| handled = true; |
| } |
| if (command == LIGHT9 || command == LIGHT9+'💡') { |
| changeLight(LIGHT9, CH_LIGHT9); |
| handled = true; |
| } |
| // , text2command |
| if (!handled) { |
| sendTo('text2command.0', { |
| text: command.replace(/\//g, '#').replace(/_/g, ' '), |
| id: chat_id, |
| user: user |
| }, function (response) { |
| if (response && response.response) { |
| sendTo('telegram.0', {user: user, text: response.response}); |
| } |
| }); |
| } |
| }); |
| // , |
| on({id: /^mqtt\.0\.PLC55_Lighting\.lighting\..*$/, ack: true, change: 'ne'}, function (obj) { |
| sendLightMenu(" "); |
| }); |
| // |
| sendLightMenu(""); |
结果应该是这样的:
结论
文章虽然篇幅长,但我希望它有用,并且也许可以使某些用户的生活更轻松,或者鼓励某人创建自己的智能家居。
目前,我基于ioBroker的智能家居系统已经运转了4年,对此我感到非常满意。 除了ZigBee之外,还通过MQTT和HTTP连接了几个自制控制器,用于控制照明,通风和其他系统,1-wire总线上的温度传感器网络,RS485总线上的电源监视设备和modbus RTU协议等等。
在我的智能家居解决方案储钱罐中,我积累了很多想法,尽管可能应该将它们更多地看作是智能家居问题的储钱罐(那些了解我的意思的人)。 在评论中留下您的希望,以帮助我决定下一篇文章的主题。