连接不同系统
当听到“接口”这个词时,大多数人可能会想到图形用户界面或物理硬件接口(串行,USB)。如果您涉足脚本编写或是一位严肃的开发人员,那么您无疑也熟悉软件接口的概念。有时,需要集成不同的系统,而这些系统之间尚不存在接口,但是,凭借一点 ingenuity,可以创建一个接口来弥合这种差距,并帮助您满足您的特定需求。
多年来,我开发了一个广泛的家庭自动化实现方案。当我完成了“简单”的集成后,我最终想要集成那些对家庭自动化不太友好的系统。我的警报系统就是一个例子。市场上存在着出色的警报面板,可以轻松实现集成,但我已经拥有一个功能齐全的警报系统,并且决心将其集成到我的家庭自动化设置中,而不是更换它。
我的第一反应是破解键盘或构建自己的硬件接口,以便能够捕获状态信息。这两种方法都是可行的,但是,当我考虑其他选择时,我意识到,即使不打开警报面板,我也可以将我的专有警报系统集成到我的家庭自动化系统中。
在透露我如何实现预期结果的细节之前,让我首先阐述我的集成目标。虽然从警报系统捕获传感器数据会很好,但在我的情况下,这完全没有必要,因为唯一可能有用的数据是运动传感器数据或特定区域故障。由于我已经安装了许多原生于我的家庭自动化构建的运动传感器,并且由于故障数据不是我当前集成要求的因素,因此我得出结论,我只需要知道我的警报是“布防”还是“撤防”。了解警报系统的状态有助于使我的家庭自动化系统更智能。这种增加的智能的一个例子可能是,如果警报状态变为布防,则更改恒温器设置并关闭所有灯。另一个例子可能是,当天黑后车库门打开且警报已布防时,打开房屋中的所有灯。
当我对这些场景进行更深入的思考时,我很快意识到我需要更多的数据。根据警报系统的安装方式及其用户的习惯,可能需要考虑多个布防状态,也可能不需要。在我的例子中,我有两个独立的布防状态。一种状态是“外出布防”(家中无人),另一种状态是“在家布防”(家中有人)。例如,如果系统设置为在家布防,则关闭房屋中的所有灯是没有意义的,但如果设置为外出布防,则完全有意义。当我继续思考我的需求时,我得出结论,知道系统是外出布防、在家布防还是撤防,就足以显著提高我的家庭自动化场景的智能性。
一旦我对自己的需求有了牢固的把握,我意识到我的警报监控公司已经在以电子邮件消息的形式向我提供我正在寻找的一些数据。每次警报布防或撤防时,我都会收到一封电子邮件消息,指示状态更改。我已经使用此功能一段时间了,因为它有助于了解我的孩子何时到家或何时早上离开去上学。由于我已经使用此通知机制一段时间了,因此我也知道它非常及时且可靠。
因为我已经获得了我需要的大部分数据,所以我开始思考如何利用我的电子邮件系统作为我的专有警报面板接口的基础。在过去,我曾使用 procmail 来处理传入的电子邮件,因此我知道将脚本注入到入站邮件处理过程中以扫描内容并采取行动将非常容易。
在我开始编写脚本并弄清楚如何让我的电子邮件系统通过它运行入站邮件之前,我需要处理我在状态通知方面存在的不足。您可能已经注意到,我说我的警报监控公司向我发送了两个状态通知:一个用于布防,一个用于撤防。我非常确定可以配置一个额外的继电器,以便公司的人员可以用我需要继续进行的“布防”的两种变体来通知我,所以我打电话给他们讨论此事,果然,他们能够做出我要求的更改。在很短的时间内,我就收到了我想要的三个通知。
有了通知,我就可以开始创建脚本来扫描传入邮件的任务了。
为了尽可能保持简单,我决定用 Bash 编写脚本。
要遵循此示例,您需要做的第一件事是捕获所有管道输入到脚本中的数据并保存以供处理
#!/bin/bash
while read a
do
echo "$a" >>~/tmp/results.tmp
done
此代码块将入站电子邮件消息重定向到一个临时文件,您现在可以对该文件执行搜索操作。由于电子邮件消息是简单的文本文件,因此有很多方法可以利用来搜索告诉您警报系统状态的字符串。在本例中,您可能会收到三条可能的消息,分别是“您的警报已设置为在家布防”、“您的警报已设置为外出布防”或“您的警报已撤防”。
现在您确切地知道要查找什么,请使用 grep 执行搜索操作。当您将 grep 搜索与 if 语句结合使用时,可以在其中一个搜索评估为 true 时执行特定命令。
我的家庭自动化系统的核心是一个软件包,它具有广泛的 REST API,我可以利用它来更改设备状态、设置变量、控制访问组和控制设备链接。这使得设置警报状态的变量变得非常容易,然后我可以使用该变量来触发各种操作并控制我家中的场景。要与 REST API 交互,让我们使用 curl。
在我的例子中,我的家庭自动化软件期望数据以 PUT 而不是 curl 的默认 GET 形式发送。为了实现这一点,让我们使用 -X 参数告诉 curl 使用 PUT。使用 -d 参数后跟您需要发送到服务器的数据,来标识您要发送到服务器的数据。最后,您需要告诉 curl 要连接到哪个 URL
url="http://ha.example.com/vars/alarmstate"
if grep -q 'Armed Stay' ~/tmp/results.tmp; then
curl -X PUT -d value="ARMED Stay" $url
elif grep -q 'Armed Away' ~/tmp/results.tmp; then
curl -X PUT -d value="ARMED Away" $url
elif grep -q 'Disarmed' ~/tmp/results.tmp; then
curl -X PUT -d value=DISARMED $url
fi
当您将所有这些放在一起时,结果是一个类似于此的代码块,它将扫描您的文件以查找三个字符串可能性,这些可能性将告诉您当前的警报状态。由于评估设置为 else/if,因此当其中一个表达式评估为 true 时,if 评估块将终止。
为了使此接口正确且一致地工作,必须在每次执行脚本后进行清理。您可能已经在第一个代码块中注意到,数据是使用 >>
I/O 重定向运算符附加到临时文件中的。您附加数据,因为数据一次一行地流式传输到文件中。如果您未能使用 append 运算符,则生成的文件将仅包含消息中的最后一行数据。幸运的是,自我清理就像删除我们的临时文件一样容易
rm ~/tmp/results.tmp
现在您有了一个脚本,需要将其注入到电子邮件处理过程中,以便强制入站邮件通过该脚本。如何实现这一点会因系统而异,因此我不会在此处赘述,但是我已经非常容易地在 Zimbra 的开源版本实现和一家非常大型且知名的美国托管提供商提供的电子邮件平台上实现了这一点。我当前的实现位于一家托管提供商处,我在那里有一个名为 ha@example.com 的电子邮件帐户。我将此帐户配置为将所有入站邮件“转发”到我的脚本,然后丢弃该消息。
现在一切就绪,我将我的监控服务配置为将警报状态更改消息发送到我的 ha@example.com 地址和我的个人电子邮件地址。如果您需要进行故障排除,将消息发送到这两个位置会很有帮助。测试消息是否来自监控公司就像检查我的个人电子邮件以查看状态更改消息是否存在一样容易。如果消息存在于我的个人邮件中,但状态更改数据没有流向我的家庭自动化系统,那么我就知道我需要对我的连接性或我的接口脚本本身进行故障排除。
在我的接口脚本到位几个月后,我有机会使用这种故障排除方法。我注意到我在我的电子邮件中收到了电子邮件通知,但是状态更改没有流向我的家庭自动化系统。为了验证数据是否流向我的脚本以及数据是否正在保存到我的临时文件中,我注释掉了删除临时文件的行,然后我强制进行了警报状态更改。果然,文件已创建,但我的家庭自动化系统中的变量没有更改。为了排除连接性或防火墙问题,然后我从命令行手动运行 curl 命令,以查看我的 REST API 调用是否可以到达我的家庭自动化系统并更改变量。这工作正常。这最终证明我的接口脚本存在问题。此时,我更仔细地检查了临时文件的内容,并且在标头中看到了一行新行,上面写着“Content-Transfer-Encoding: base64”。显然,我的监控公司对他们的电子邮件系统进行了一些更改,我需要对此进行说明。为此,我需要添加一个新的代码块,以查看新收到的电子邮件消息的内容是否为 base64 编码。如果您发现消息已编码,请使用 Perl 和 MIME::Base64 模块中的 decode_base64 函数来解码消息
if grep -q 'Content-Transfer-Encoding: base64' ~/tmp/results.tmp;
then perl -MMIME::Base64 -ne 'print decode_base64($_)'
<~/tmp/results.tmp>~/tmp/results2.tmp
rm ~/tmp/results.tmp
mv ~/tmp/results2.tmp ~/tmp/results.tmp
fi
在执行 grep 评估的代码块之前添加此代码块解决了问题,我再次开始顺利运行。
我已经使用这个自制脚本接口一年多了。除了编码问题外,该接口的性能一直非常稳定可靠。这使我能够实现我想要的集成,即使这种集成有点松散。这不仅使我能够使用我已经拥有的系统,而且实现也是完全免费的,毕竟,谁不喜欢免费呢?
手部图像,来自 Shutterstock.com。