主题
2.8 让设备自己发报警邮件
问题场景
设备在现场无人值守,出现异常(水位超限、温度过高、设备停机)时需要立刻收到邮件通知。不希望为此单独部署邮件服务,也不想在设备固件里实现 SMTP 认证和加密。
做完之后能得到什么
设备脚本判断到异常后,调用一个函数即可发出邮件。SMTP 连接、TLS 加密和投递都由云服务器完成,设备只发送一段 JSON。
这个功能和云平台的「事件触发」没有关系
两者都能发邮件,但判断逻辑的执行位置不同:
- 事件触发:在云平台网页上拖节点配置规则,判断在云服务器上执行,设备离线时规则仍然运行。见 WEB 云组态 · 事件触发。
- 本篇的
plc.cloud_compute:判断写在设备脚本里,由设备通过云连接把发信动作交给服务器执行。
需要在设备本地按自定义逻辑判断并发送通知时,用本篇的方法;需要长期无人值守地监视数据点并自动报警时,用事件触发。
需要准备
- 设备已联网,并已连接 LP 云服务(发送时设备必须在线,云连接断开时任务会被丢弃)
- 一个用于发信的邮箱(推荐 163),在邮箱设置中开通 SMTP 服务并获取授权码
- 一个接收邮件的地址
预计耗时:15 分钟
动手做
第 1 步 · 注册回调,接收云端返回
plc.cloud_compute 是异步接口,调用后不会立即返回结果。执行结果由服务器通过 plc.COMPUTE 事件回调送回,所以先把回调注册好:
lua
plc.cloud_reg(plc.COMPUTE, function(jsonstring)
-- 服务器把结果原样回传,成功时 cmdreq 为 "ok"
local ok, resp = pcall(json.decode, jsonstring)
if ok and type(resp) == "table" and resp.cmdreq == "ok" then
print("邮件已发出")
else
print("发信失败:", jsonstring) -- 失败时 cmdreq 为服务器返回的错误描述
end
end)第 2 步 · 发一封邮件
构造一个 cmd = "mail_send" 的任务表,用 json.encode 转成字符串后交给 plc.cloud_compute:
lua
local task = {
cmd = "mail_send",
to = "you@example.com", -- 收件人
subject = "现场报警", -- 邮件标题
body = "1号泵房水位超限", -- 邮件正文
-- 指定发信邮箱,见下方说明
username = "yourbox@163.com", -- 发信邮箱
password = "你的SMTP授权码", -- 是授权码,不是邮箱登录密码
}
plc.cloud_compute(json.encode(task))字段说明:
| 字段 | 必填 | 说明 |
|---|---|---|
cmd | 是 | 固定填 "mail_send" |
to | 是 | 收件人邮箱 |
subject | 是 | 邮件标题,为空时服务器返回参数错误 |
body | 是 | 邮件正文,为空时服务器返回参数错误 |
username | 否 | 发信邮箱账号。不填时使用平台内置邮箱,见下方说明 |
password | 否 | 发信邮箱的 SMTP 授权码,与 username 成对出现 |
smtp_host | 否 | 自定义 SMTP 服务器,默认 smtp.163.com |
smtp_port | 否 | 自定义端口,默认 465 |
use_ssl | 否 | 是否启用 SSL,默认开启 |
id | 否 | 任务标识,填写后服务器在回执中原样带回,用于区分是哪一次任务 |
应指定自己的发信邮箱
不填 username / password 时,服务器使用一个内置的公共邮箱发信。该邮箱供注册验证码等内部流程使用,为多方共享,不保证可用性,也可能被限流或更换,不适用于正式项目。正式部署请填写自有的发信邮箱和授权码。
password 以明文保存在设备脚本中,注意脚本文件的访问控制。
第 3 步 · 结合业务判断触发
实际场景通常只在判断到异常时才发送。下面的例子配合定时器,并加入最小发送间隔:
lua
last_mail = 0 -- 上次发送时间,用全局变量保存
timer.start(5000, function()
local level = plc.df(100) -- D100 存放水位(放大后的整数)
if level > 3000 then -- 超过阈值
local now = os.time()
if now - last_mail >= 300 then -- 距上次发送至少 300 秒
last_mail = now
plc.cloud_compute(json.encode({
cmd = "mail_send",
to = "you@example.com",
subject = "水位超限报警",
body = "当前水位 " .. (level / 1000) .. " 米,已超过 3 米",
username = "yourbox@163.com",
password = "你的SMTP授权码",
}))
end
end
end)需要自行控制发送频率
plc.cloud_compute 只负责把任务发出,服务器不做去重或限频(这一点和事件触发不同)。当监视的是在阈值附近波动的模拟量时,若不加最小发送间隔,短时间内会触发大量重复邮件。上例中的 last_mail 时间戳即为最简单的限频方式。
验证成功
- 接收邮箱收到邮件
- 第 1 步的回调打印出「邮件已发出」
若收不到,先查看回调中 jsonstring 的内容:cmdreq 为 ok 表示服务器已投递,此时可检查收件箱的垃圾邮件过滤;cmdreq 不为 ok 时,其内容即服务器返回的错误描述(例如授权码错误、发信邮箱未开通 SMTP、发送时设备不在线)。
注意事项
WARNING
- 设备必须在线:云连接断开时
plc.cloud_compute的任务被丢弃,不会排队补发。安全相关的报警不应仅依赖这一条路径 - 接口为异步:调用返回不代表发送成功,结果只能在
plc.cloud_reg(plc.COMPUTE, ...)回调中确认 - 需自行限频:服务器不去重、不限频,需在脚本中加入最小发送间隔
- password 为授权码:163/QQ 等邮箱需在邮箱设置中开通 SMTP 并生成授权码,不是邮箱登录密码
- 内置邮箱仅供测试:不填 username/password 会使用公共邮箱,正式项目应填写自有邮箱
- 凭据为明文:账号密码保存在脚本中,注意脚本文件的访问控制
举一反三
mail_send 只是云计算任务的一种,plc.cloud_compute 还可提交其它 cmd(例如 server_version 查询云服务版本)。回执统一通过 plc.cloud_reg(plc.COMPUTE, ...) 返回。
- 换用其它通知方式:通过 HTTP 触发企业微信/钉钉机器人,见 对接 HTTP 上报与拉取数据
- 无人值守自动报警:不在脚本中写判断,改用云平台 事件触发
想更深入
- Lua 编程 · 云平台通信 (cloud):
cloud_compute/cloud_reg完整说明 - WEB 云组态 · 事件触发:云端规则引擎,另一种发邮件的方式