Skip to content

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, ...) 返回。

想更深入 ​

LPPLC 工业物联网云平台 · 软硬件与云平台全栈自研