云端接入

云端接入

接入方式

云端接入架构图

如上图所示,涂鸦云支持第三方云以HTTPS、Kafka方式接入涂鸦云。HTTPS接入的场景主要有:设备指令下发、设备状态数据获取、用户信息同步等。Kafka接入等场景有:订阅设备实时数据。

名词解释:

  • HTTPS 涂鸦的API是基于HTTPS协议来调用的,开发者可以根据涂鸦的API协议来封装HTTPS请求进行调用。

  • Kafka Kafka是一种高吞吐量的分布式发布订阅消息系统,它可以处理消费者规模的网站中的所有动作流数据。涂鸦云平台支持Kafka方式调用,并提供相应的accessId与accessKey供用户使用。

建议:

  • 如果需要获取分析统计类或用户、设备类等非实时数据,推荐使用HTTPS。
  • 如果需要实时的硬件上报数据类数据,推荐使用Kafka订阅。

调用入口

涂鸦云根据中国企业内外销区域结合海底光缆分布和全球各城市的实测结果,部署覆盖亚、欧、美三个可用区。

调用API的服务域名如下:

可用区 协议 域名 数据安全级别 服务区域
AY https *.tuyacn.com HTTPS+AES 亚洲
AZ https *.tuyaus.com HTTPS+AES 美洲
EU https *.tuyaeu.com HTTPS+AES 欧洲

注: 涂鸦云提供Https等多种通信协议,根据业务需求可以灵活选择使用。第三方云获取实时数据推荐使用Kafka。

  • https: a1.tuya(cn/eu/us).com/api.json(云端调用,如:https: //a1.tuyacn.com/api.json)
  • Kafka: kafka.cloud.tuyacn/eu/us.com:8092(云端调用,如:kafka.cloud.tuyacn.com:8092)

协议概述

HTTPS

  • 调用流程 根据ATOP的协议,通过HTTPS调用云服务的详细步骤为:填充参数 > 生成签名 > 拼装HTTPS请求 > 发起HTTPS请求> 得到HTTPS响应 > 解释json结果。

    大致的调用过程如下图所示: HTTP调用

  • 公共参数 调用任何一个API都必须传入的公共参数有:

参数名称 参数类型 是否必须 是否签名 参数描述
a String API名称
v String API接口版本
sid String 用户登录授权成功后,ATOP颁发给应用的用户session
time String 时间戳,格式为数字,大小到秒非毫秒,时区为标准时区,例如:1458010495。API服务端允许客户端请求最大时间误差为540分钟。
sign String API输入参数签名结果,签名算法参照下面的介绍
clientId String 用户的APPID(注各平台不一样如:ios、android、云云对接的id都不一样)
lang String APP的语言,如"en",“zh_cn”,错误信息根据语言自动翻译
ttid String APP渠道或云端渠道,如公司名,用于数据分析跟踪
os String 手机操作系统,如"Android",“ios”,云端可以写linux或写公司名

注:因为部分云对云接口可以提供客户端的信息,如获取设备控制面板,这时需要根据不同的客户端为os赋值,否则无法获得正确的数据。 如Android则os=Android

接入教程

QuickStart(SDK方式)

涂鸦推荐使用SDK的方式接入涂鸦云,本节将为您分别介绍使用 HTTPS SDK 和 Kafka SDK 接入涂鸦云。如果您需要使用API的方式接入涂鸦云服务,您可以直接查看下一节的内容。

如果需要调用涂鸦云服务,您需要:

1.注册账号并登录涂鸦开发者

2.在我的账号的下拉菜单栏里选择“云API授权”,再点击页面的“点此申请”进行提交,涂鸦的工作人员会在1个工作日左右进行处理

申请云API授权

申请云API授权

HTTPS SDK

1.要使用HTTPS SDK,请先下载SDK:HTTPS-SDK下载

2.将SDK引入到自己的工程的classpath中

3.本例中用到了fastjson,故需要在pom.xml中添加相应的依赖(读者也可自行选择喜欢的JSON工具)

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.14</version>
</dependency>

4.SDK需要使用到Apache的一些工具包,故也需要在pom.xml中添加相应依赖。

<dependency>
  <groupId>org.apache.httpcomponents</groupId>
  <artifactId>httpcore</artifactId>
  <version>4.4.5</version>
</dependency>
<dependency>
  <groupId>org.apache.httpcomponents</groupId>
  <artifactId>httpclient</artifactId>
  <version>4.5.2</version>
</dependency>
<dependency>
  <groupId>commons-lang</groupId>
  <artifactId>commons-lang</artifactId>
  <version>2.6</version>
</dependency>

5.编写代码

此处以获取验证码为例,讲解SDK的使用

首先,创建TuyaCloudClient对象,它是我们访问涂鸦云的客户端。传入accessId&accessKey(联系涂鸦获取)和指定可用区API的URI。

然后,创建request对象,并指定HTTP属性。

在某些API中可能需要请求参数,我们需要将这些参数以key-value对的形式封装到Map中,再将Map传给Request对象。

最后,通过client.sendRequest()方法发出请求,响应结果是该方法的返回值。

下面的代码是获取验证码的示例程序,供开发者参考:

 	public class HttpSDKTest {

private static final String END_URI = "https://a1.tuyacn.com/api.json";// 调用中国区的API(您可换成其他可用区)

private static final String ACCESS_ID = "xxxx"; // TODO: 请联系涂鸦获取

private static final String ACCESS_KEY = "xxxxxxxxxxx"; // TODO: 请联系涂鸦获取

public static void main(String[] args) {
/*
创建client,accessId&accessKey由涂鸦提供
accessId作为clientId
accessKey用于签名
*/
TuyaCloudClient client = new TuyaCloudClient(ACCESS_ID, ACCESS_KEY, END_URI);

// 构造HTTPS请求
RequestMessage request = new RequestMessage();
request.setApi("tuya.m.user.mobile.sendcode"); //通过手机号注册的API
request.setApiVersion("1.0");
request.setOs("Linux");
request.setLang("zh");

// 封装请求参数(接口需要的具体参数请参考API手册)
Map<String, String> params = new HashMap<String, String>();
params.put("countryCode", "86");
params.put("mobile", "186xxxx7671");

/*
注:
除注册及获取统计数据等少量接口,大部份接口都需要sessionId。
具体是否需要sessionId请参考API手册。
您可以从注册和登录接口返回结果得到,返回结果字段为sid
*/

// 将请求参数加入到HTTPS请求中
request.setParams(params);

/*
发起请求,获得响应
如果请求成功, 则response里的result会是个JSON对象封装的结果.
如果请求失败, 请查看errorMsg和errorCode,进行相应的处理.
*/
ResponseMessage response = client.sendRequest(request);
}
}

运行程序,发现成功执行,同时手机也能够收到验证码:

发送验证码成功

更多API请参考云端API定义。

HTTPS接入方式

业务参数

API调用除了必须包含公共参数外,如果API本身有业务级的参数也必须传入,每个API的业务级参数请考API文档说明。业务参数全部放入postData中传到服务端,例如API有一个参数countryCode,则格式为postData={“countryCode”:“CN”}

签名

为了防止API调用过程中被黑客恶意篡改,调用任何一个API都需要携带签名,ATOP服务端会根据请求参数,对签名进行验证,签名不合法的请求将会被拒绝。ATOP目前支持多种签名算法,但所有的签名算法都需要做MD5摘要。

注意:对于非必填参数且参与签名的,在值为空的情况下不参与签名,只有在有值情况下才参与签名。

如果调用SDK实现云云对接,则无须关心签名的具体实现方式。

如果需要直接调用API,则可参照以下步骤进行签名:

1.对所有API请求参数(包括公共参数和业务参数postData)postData需要对值进行组装签名。 例如,有如下请求参数:

a=tuya.p.weather.city.info.list,v=1.0,lang=zh-Hans,os=Linux,clientId=accessId,postData={“countryCode”:“CN”},time=1490003743

2.根据参数名称的字典顺序排序,按参数名称进行排序后的参数如下: a=tuya.p.weather.city.info.list,clientId=accessId,lang=zh-Hans,os=Linux,postData={“countryCode”:“CN”},time=1490003743,v=1.0

3.将排序好的参数名和参数值拼装在一起,参数间通过|连接,拼接后的结果为:

a=tuya.p.weather.city.info.list|clientId=accessId|lang=zh-Hans|os=Linux|postData={“countryCode”:“CN”}|time=1490003743|v=1.0

至此,参数的组装就完成了。接下来,就需要对参数进行签名了。

涂鸦云根据不同的业务场景提供多套签名算法,云对云签名算法具体说明如下:

4.在上面已经拼接好的请求串的最前面添加accessKey,假定accessKey为accesskey,则最后签名的字符串为:

accessKeya=tuya.p.weather.city.info.list|clientId=accessId|lang=zh-Hans|os=Linux|postData={“countryCode”:“CN”}|time=1490003743|v=1.0

5.把拼装好的字符串采用utf-8编码,使用MD5算法对编码后的字节流进行摘要,摘要后的值为9aa12fb3dde7fb89146aba0bb3abfb97

6.组装HTTPS请求,将所有参数名和参数值采用utf-8进行URL编码(参数顺序可随意,但必须要包括签名参数),然后通过GET或POST发起请求。组装的请求如下:

https://a1.tuyacn.com/api.json?a=tuya.p.weather.city.info.list&time=1490004310&lang=zh-Hans&v=1.0&os=Linux&clientid=accessId&sign=9aa12fb3dde7fb89146aba0bb3abfb97&postData={“countryCode”:“CN”}

具体的签名工具类实现如下:

    public class AtopThirdCloudMobileSignUtil {

//封装请求参数
private static TreeMap<String, String> paramsBuild(ApiRequestDO apiRequestDo) {
TreeMap<String, String> params = new TreeMap<String, String>();
params.put("a", apiRequestDo.getApi());
params.put("v", apiRequestDo.getApiContextDo().getApiVersion());
params.put("lat", apiRequestDo.getApiContextDo().getLat());
params.put("lon", apiRequestDo.getApiContextDo().getLon());
params.put("lang", apiRequestDo.getApiContextDo().getLang());
params.put("deviceId", apiRequestDo.getApiContextDo().getDeviceid());
params.put("appVersion", apiRequestDo.getApiContextDo().getAppVersion());
params.put("ttid", apiRequestDo.getApiContextDo().getTtid());
params.put("os", apiRequestDo.getApiContextDo().getOs());
params.put("clientId", apiRequestDo.getAppInfoDo().getClientId());
if (StringUtils.isNotBlank(apiRequestDo.getN4h5())) {
params.put("n4h5", apiRequestDo.getN4h5());
}
params.put("sp", apiRequestDo.getSp());
params.put("time", apiRequestDo.getT());
if (StringUtils.isNotBlank(apiRequestDo.getSession())) {
params.put("sid", apiRequestDo.getSession());
}
if (StringUtils.isNotBlank(apiRequestDo.getData())) {
params.put("postData", apiRequestDo.getData());
}
return params
}

//拼接参数字符串
private static String signAssembly(TreeMap<String, String> params, String secretKey) {
StringBuilder str = new StringBuilder();
str.append(secretKey);
Set<String> keySet = params.keySet();
Iterator<String> iter = keySet.iterator();
while (iter.hasNext()) {
String key = iter.next();
if (StringUtils.isBlank(params.get(key))) {
continue;
}
str.append(key);
str.append("=");
str.append(params.get(key));
str.append("|");
}
String strValue = str.toString();
strValue = strValue.substring(0, (strValue.length() - 1));
return strValue;
}

//获取签名
private static String getSign(ApiRequestDO apiRequestDo, String secretKey) {
TreeMap<String, String> params = paramsBuild(apiRequestDo);
String signString = signAssembly(params, secretKey);
return signString;
}

//对签名进行MD5
public static String getMD5(byte[] source) {
String s = null;
char hexDigits[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
try {
MessageDigest md = MessageDigest.getInstance("MD5");
md.update(source);
byte tmp[] = md.digest(); // MD5 的计算结果是一个 128 位的长整数,
// 用字节表示就是 16 个字节
char str[] = new char[16 * 2]; // 每个字节用 16 进制表示的话,使用两个字符,
// 所以表示成 16 进制需要 32 个字符
int k = 0; // 表示转换结果中对应的字符位置
for (int i = 0; i < 16; i++) { // 从第一个字节开始,对 MD5 的每一个字节
// 转换成 16 进制字符的转换
byte byte0 = tmp[i]; // 取第 i 个字节
str[k++] = hexDigits[byte0 >>> 4 & 0xf]; // 取字节中高 4 位的数字转换,
// >>> 为逻辑右移,将符号位一起右移
str[k++] = hexDigits[byte0 & 0xf]; // 取字节中低 4 位的数字转换
}
s = new String(str); // 换后的结果转换为字符串

} catch (Exception e) {
logger.warn("MD5加密异常!", e);
}
return s;
}

public static void main(String[] args) {
ApiRequestDO apiRequestDo = new ApiRequestDO();

apiRequestDo.setApi("tuya.p.weather.city.info.list");
ApiContextDO apiContextDO = new ApiContextDO();
apiContextDO.setApiVersion("1.0");
apiContextDO.setOs("Linux");
apiContextDO.setLang("zh-Hans");

AppInfoDO appInfoDO = new AppInfoDO();
appInfoDO.setClientId("accessId");
apiRequestDo.setAppInfoDo(appInfoDO);

apiRequestDo.setT("1490004310");
apiRequestDo.setData("{\"countryCode\":\"CN\");

apiRequestDo.setApiContextDo(apiContextDO);

String s = getSign(apiRequestDo, "accessKey");
String sign = MD5Util.getMD5(s.getBytes());
System.out.println(sign);
}
}

注意:

  • 所有的请求和响应数据编码皆为utf-8格式,URL里的所有参数名和参数值请做URL编码
  • postData使用POST传输,其它参数支持POST和GET
  • 请求不同的可用区,请使用相应的域名
  • 提供多个不同域名的原因:

1.不同的域名可以由不同的DNS服务商进行解析,以提供各区域最好的解析稳定性和加速服务;

2.可以更有效的避免运营商劫持问题;

3.减少解析次数,可以更稳定的优化部分偏远地区的DNS服务商性能问题。

400-881-8611