ThingsBoard 自定义阿里云 SMS 规则节点
正文与配图来自原稿。结构与 自定义华为云 SMS 规则节点 类似,本篇换成阿里云短信。
相关:如何发送告警邮件 · 配置发送邮件
1、概述
一个物联网平台承载着很多设备的连接,当设备出现异常的时候,能够快速通知到运维管理员非常重要。ThingsBoard 提供了自定义配置邮箱,但是它对支持发送短信不是很友好,内置多是国外的 SMS 服务商。
在国内常见就是阿里、腾讯、华为、七牛等 SMS 服务商。如果需要发短信,肯定优先用国内厂商。但 ThingsBoard 里没看到哪里可以直接配这些,于是自定义规则节点来实现这个功能,同时把规则节点自定义也介绍了。本文首先以阿里 SMS 为例。

2、自定义规则节点
概述
我是直接在本地的 ThingsBoard 程序中增加的节点,也可以按照官方使用官方提供的那个程序来写,然后打成 jar 引入。
官方教程
这个官方教程说得不清楚,另外一个类如何定义也没说:
创建包
在本地程序创建了一个 external 包,意思是扩展 SMS。

然后在里面分别创建了国内常见的 SMS 服务商文件夹:

定义接口
定义了一个发送短信的接口 SendSms,其它每个厂商来实现这个接口:
package org.thingsboard.rule.engine.sms.external;
public interface SendSms {
boolean sendMsg();
ExternalSmsType getType();
}定义枚举类
定义了 SMS 厂商的名字:
package org.thingsboard.rule.engine.sms.external;
/**
* 扩展外部的 sms 厂商
*/
public enum ExternalSmsType {
ALI_SMS,
TENCENT_SMS,
HUAWEI_SMS,
QINIU_SMS,
YUNPIAN_SMS;
}阿里云发送短信服务
封装了阿里云发送短信依赖的方法,可以参考阿里云官方文档:
- https://next.api.aliyun.com/api-tools/sdk/Dysmsapi?version=2017-05-25&language=java-async-tea
- https://next.api.aliyun.com/api/Dysmsapi/2017-05-25/SendSms?lang=JAVA
- https://github.com/aliyun/alibabacloud-java-sdk/blob/master/dysmsapi-20170525/README-CN.md
package org.thingsboard.rule.engine.sms.external.ali;
import com.aliyun.dysmsapi20170525.models.SendSmsRequest;
import com.aliyun.dysmsapi20170525.models.SendSmsResponse;
import com.aliyun.teaopenapi.models.Config;
import lombok.extern.slf4j.Slf4j;
import org.thingsboard.rule.engine.sms.external.ExternalSmsType;
import org.thingsboard.rule.engine.sms.external.SendSms;
@Slf4j
public class AliSendSmsService implements SendSms {
private String accessKeyId;
private String accessKeySecret;
private String phoneNumbers;
private String signName;
private String templateCode;
private String templateParam;
public AliSendSmsService(String accessKeyId, String accessKeySecret, String phoneNumbers,
String signName, String templateCode, String templateParam) {
this.accessKeyId = accessKeyId;
this.accessKeySecret = accessKeySecret;
this.phoneNumbers = phoneNumbers;
this.signName = signName;
this.templateCode = templateCode;
this.templateParam = templateParam;
}
@Override
public boolean sendMsg() throws Exception {
com.aliyun.dysmsapi20170525.Client client = this.createClient(accessKeyId, accessKeySecret);
SendSmsRequest sendReq = new SendSmsRequest()
.setPhoneNumbers(phoneNumbers)
.setSignName(signName)
.setTemplateCode(templateCode)
.setTemplateParam(templateParam);
SendSmsResponse sendResp = client.sendSms(sendReq);
String code = sendResp.body.code;
if (com.aliyun.teautil.Common.equalString(code, "OK")) {
return true;
}
return false;
}
@Override
public ExternalSmsType getType() {
return ExternalSmsType.ALI_SMS;
}
/**
* 使用 AK&SK 初始化账号 Client
*/
public static com.aliyun.dysmsapi20170525.Client createClient(String accessKeyId, String accessKeySecret)
throws Exception {
Config config = new Config();
config.accessKeyId = accessKeyId;
config.accessKeySecret = accessKeySecret;
return new com.aliyun.dysmsapi20170525.Client(config);
}
}补充(辅): 需在工程中引入阿里云 Dysmsapi SDK 依赖(版本按你工程与官方 README 选择)。
SendSms接口若未声明throws Exception,请与AliSendSmsService#sendMsg签名对齐(接口加throws Exception,或在实现里自行捕获)。
定义规则节点配置
这个类的定义我在官方文档没看到有说明。这个类的作用就是接收规则节点的参数,同时需要实现 NodeConfiguration<AliSendSmsNodeConfiguration> 接口,并实现 defaultConfiguration 方法来进行参数封装。
注意: 下列默认字符串多为说明文案占位,上线前请改成控制台真实的 AccessKey、签名、模板 CODE、手机号与模板变量 JSON,切勿把真实密钥提交到公开仓库。
package org.thingsboard.rule.engine.sms.external.ali;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.thingsboard.rule.engine.api.NodeConfiguration;
@Data
@Slf4j
public class AliSendSmsNodeConfiguration implements NodeConfiguration<AliSendSmsNodeConfiguration> {
// 文档见上文阿里云链接
// 访问 key
private String accessKeyId = "访问key";
// 访问密钥
private String accessKeySecret = "访问密钥";
// 接收短信的手机号码。
// 国内短信:+/+86/0086/86 或无任何前缀的 11 位手机号码,例如 1390000****。
// 国际/港澳台消息:国际区号+号码,例如 852000012****。
// 支持对多个手机号码发送短信,手机号码之间以半角逗号(,)分隔。上限为 1000 个手机号码。
private String phoneNumbers = "接收短信的手机号码。手机号码格式:\n "
+ "国内短信:+/+86/0086/86或无任何前缀的11位手机号码,例如1390000**** \n"
+ "国际/港澳台消息:国际区号+号码,例如852000012**** \n"
+ "支持对多个手机号码发送短信,手机号码之间以半角逗号(,)分隔。上限为1000个手机号码。";
// 短信签名名称。可在控制台「签名管理」获取。
private String signName = "短信签名名称,您可以登录阿里云短信服务控制台,选择国内消息或国际/港澳台消息,在签名管理页面获取";
// 短信模板 CODE。可在控制台「模板管理」查看。
private String templateCode = "短信模板CODE,您可以登录短信服务控制台,选择国内消息或国际/港澳台消息,在模板管理页面查看模板";
// 短信模板变量对应的实际值。示例:{"name":"张三","number":"1390000****"}
private String templateParam = "短信模板变量对应的实际值。支持传入多个参数,示例:{\"name\":\"张三\",\"number\":\"1390000****\"}。";
@Override
public AliSendSmsNodeConfiguration defaultConfiguration() {
AliSendSmsNodeConfiguration configuration = new AliSendSmsNodeConfiguration();
configuration.setAccessKeyId(accessKeyId);
configuration.setAccessKeySecret(accessKeySecret);
configuration.setPhoneNumbers(phoneNumbers);
configuration.setSignName(signName);
configuration.setTemplateCode(templateCode);
configuration.setTemplateParam(templateParam);
return configuration;
}
}定义规则节点
主要关注 @RuleNode 中的配置。这个节点 type 是 EXTERNAL,name 是 ali sms,然后就是 uiResources。这个 uiResources 指向一个静态文件,用来渲染节点的 js 文件。因为是自定义的,所以引入的是官方示例程序里的 static/rulenode/custom-nodes-config.js,并把这个文件拷贝到 ThingsBoard 的 resources 目录下。指定 icon 为 sms 即可。
package org.thingsboard.rule.engine.sms.external.ali;
import lombok.extern.slf4j.Slf4j;
import org.thingsboard.rule.engine.api.*;
import org.thingsboard.rule.engine.api.util.TbNodeUtils;
import org.thingsboard.server.common.data.plugin.ComponentType;
import org.thingsboard.server.common.msg.TbMsg;
@Slf4j
@RuleNode(
type = ComponentType.EXTERNAL,
name = "ali sms",
relationTypes = {"True", "False"},
configClazz = AliSendSmsNodeConfiguration.class,
nodeDescription = "Use ali sms send msg.",
nodeDetails = "If send msg success - send Message via <b>True</b> chain, otherwise <b>False</b> chain is used.",
uiResources = {"static/rulenode/custom-nodes-config.js"},
icon = "sms")
public class AliSendSmsNode implements TbNode {
private AliSendSmsService aliSendSmsService;
@Override
public void init(TbContext ctx, TbNodeConfiguration configuration) throws TbNodeException {
AliSendSmsNodeConfiguration config =
TbNodeUtils.convert(configuration, AliSendSmsNodeConfiguration.class);
String accessKeyId = config.getAccessKeyId();
String accessKeySecret = config.getAccessKeySecret();
String phoneNumbers = config.getPhoneNumbers();
String signName = config.getSignName();
String templateCode = config.getTemplateCode();
String templateParam = config.getTemplateParam();
aliSendSmsService = new AliSendSmsService(accessKeyId, accessKeySecret,
phoneNumbers, signName, templateCode, templateParam);
}
@Override
public void onMsg(TbContext ctx, TbMsg msg) {
try {
ctx.tellNext(msg, aliSendSmsService.sendMsg() ? "True" : "False");
} catch (Exception e) {
ctx.tellFailure(msg, e);
}
}
@Override
public void destroy() {
}
}
3、如何使用
第一步: 首先重启程序,然后打开规则链页面,可以看到自定义的节点已经出现在**外部(EXTERNAL)**分类下面。

第二步: 配置自定义的规则节点,按照阿里云官方配置指定的参数。需要配置的参数全部抽出来了:

参数不解释,请看阿里云 Java 案例,说得很清楚。其它内容和上一节(华为云)讲解的类似,只要明白一种,其它就能够写出来。
关键字段对照:
| 配置项 | 含义 |
|---|---|
accessKeyId / accessKeySecret | 阿里云 AK/SK |
phoneNumbers | 接收号码(可多个,逗号分隔) |
signName | 短信签名名称 |
templateCode | 短信模板 CODE |
templateParam | 模板变量 JSON,如 {"name":"张三","number":"1390000****"} |
成功时 SDK 返回 code 为 OK(与原稿判断一致)。
4、验证
由于我使用的是测试,没有权限开通阿里云的 SMS(一般要企业才能开通),但是我调试过,规则节点都没有问题,就是在执行发送短信的时候提示没有开通服务,那些 key 都是无效的。如果你们公司开通了就可以配置进行测试。
补充(辅)
- 与华为篇共用
SendSms/ExternalSmsType时,注意sendMsg的异常签名统一。 - Maven 需加入
dysmsapi20170525等相关依赖,版本以阿里云当前文档为准。 Configuration字段默认值会渲染到节点表单;生产环境应改成真实参数,勿提交密钥。- 改完代码需重新编译并重启;打 jar / 镜像见专栏前文。
