更新时间:2025-11-10 02:57:54下载pdf
在开始进行接口集成之前,您需要完成以下准备工作,以确保后续的API调用能够顺利进行。
注册并登录涂鸦IoT开发平台
创建云项目
获取并提供凭证
在您的云项目概览中,可以查看到一组授权凭证:Client ID 和 Client Secret。
请将这两个值安全地提供给涂鸦平台方并告知回调域名信息。它后续进行接签名验证的唯一依据以及回调。
Client Secret属于高度敏感信息,请务必保密。
为保障通信安全,所有来自涂鸦平台的API调用都附带签名。接入方需要对收到的每一个请求进行签名验证,以确保请求的合法性和数据完整性。
验签算法:HMAC-SHA256
当您的服务收到来自涂鸦的请求时,请遵循以下步骤进行验证:
提取请求信息:从收到的HTTP请求中,获取四个关键信息:
client-id (来自 Body)timestamp (来自 Body)sign (来自 Body,即涂鸦生成的签名)payload (指令相关请求数据)在本地重新构建待签名字符串:使用收到的 client-id、timestamp 和 payload,按照以下固定顺序拼接成一个字符串。
待签名字符串 = client-id + timestamp + payload
payload 字符串。在本地计算签名:使用您在涂鸦平台获取并预存的 Client Secret 作为密钥,对上一步生成的 “待签名字符串” 进行 HMAC-SHA256 哈希计算,并转换为十六进制字符串。这个结果是您的“本地签名”。
比对签名:将第1步中获取的 sign 与第3步中您本地计算出的“本地签名”进行字符串全等比较。
401 Unauthorized。此示例展示了如何在您的服务端使用 Java 实现对涂鸦请求的签名验证。
Maven 依赖:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.17.1</version>
</dependency>
验签逻辑实现:
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public class SignatureVerifier {
// 预先配置好的凭证
private static final String CLIENT_SECRET = "your_configured_client_secret";
/**
* 验证来自涂鸦的请求签名
*
* @param clientId 从请求 'client-id' 中获取的值
* @param timestamp 从请求 'timestamp' 中获取的值
* @param sign 从请求 'sign' 中获取的签名
* @param requestBodyString 从请求中获取的原始 Body JSON 字符串
* @return true 如果签名有效, false 则无效
*/
public boolean verifySignature(String clientId, String timestamp, String sign, String requestBodyString) {
// 步骤 2: 在本地重新构建待签名字符串
String stringToSign = clientId + timestamp + requestBodyString;
try {
// 步骤 3: 在本地计算签名
Mac mac = Mac.getInstance("HmacSHA256");
SecretKeySpec secretKeySpec = new SecretKeySpec(CLIENT_SECRET.getBytes(StandardCharsets.UTF_8), "HmacSHA256");
mac.init(secretKeySpec);
byte[] localSignatureBytes = mac.doFinal(stringToSign.getBytes(StandardCharsets.UTF_8));
String localSign = bytesToHex(localSignatureBytes);
// 步骤 4: 比对签名 (使用固定时间的比较来防止时序攻击)
return MessageDigest.isEqual(localSign.getBytes(StandardCharsets.UTF_8), sign.getBytes(StandardCharsets.UTF_8));
} catch (NoSuchAlgorithmException | InvalidKeyException e) {
// 记录异常日志
e.printStackTrace();
return false;
}
}
private static String bytesToHex(byte[] hash) {
if (hash == null) {
return null;
}
StringBuilder hexString = new StringBuilder(2 * hash.length);
for (byte b : hash) {
String hex = Integer.toHexString(0xff & b);
if (hex.length() == 1) {
hexString.append('0');
}
hexString.append(hex);
}
return hexString.toString();
}
// 模拟调用
public static void main(String[] args) {
SignatureVerifier verifier = new SignatureVerifier();
// 模拟从收到的请求中获取的数据
String clientId = "abcdefg1234567";
String timestamp = "1727320411000";
String requestBody =
"{\"auth\":{\"type\":\"BearerToken\",\"value\":\"some-oauth2-token\"},\"header\":{\"messageId\":\"some-uuid-v4-string\",\"name\":\"Discover\",\"namespace\":\"Tuya.Iot.Smarthome.Discovery\",\"version\":\"1\"},\"payload\":{\"endpointId\":\"voiceDeviceId\"}}";
String receivedSign = "2f4b2a8a1ed34ea8a8c4f9f7...example_sign..."; // 假设这是涂鸦发来的正确签名
boolean isValid = verifier.verifySignature(clientId, timestamp, receivedSign, requestBody);
System.out.println("Signature verification result: " + isValid);
}
}
所有业务请求均由涂鸦平台发起,接入方平台负责接收、验签和响应。
接入方需要实现并暴露以下接口,以供涂鸦平台调用。
| Method | API Endpoint | 调用方 | 实现方 | Description |
|---|---|---|---|---|
| POST | /回调域名/discovery |
涂鸦平台 | 接入方 | 设备发现 |
| POST | /回调域名/control |
涂鸦平台 | 接入方 | 设备控制 |
当需要同步设备信息时,涂鸦平台将调用此接口,以发现指定音响能发现的所有智能设备。接入方需返回该用户可用的设备列表。
POST 回调域名/discovery
涂鸦将发送一个 POST 请求。
| 参数名 | 类型 | 参数类型 | 说明 | 是否必填 |
|---|---|---|---|---|
| header | Object | BODY | 指令的头部信息,用于描述指令的元数据。 | 是 |
| auth | Object | BODY | 用户的身份认证信息。 | 是 |
| payload | Object | BODY | 指令的载荷部分。 | 是 |
header参数说明
| 参数名 | 类型 | 参数类型 | 说明 | 是否必填 |
|---|---|---|---|---|
| namespace | String | BODY | 发现意图,固定为:Tuya.Iot.Smarthome.Discovery | 是 |
| name | String | BODY | 指令名称,固定为:Discover | 是 |
| messageId | String | BODY | 消息的唯一标识符,用于追踪和去重。 | 是 |
| version | String | BODY | 指令版本号,默认为 1。 | 是 |
| clientId | String | BODY | 您在涂鸦平台获取的 Client ID。 | 是 |
| timestamp | String | BODY | 当前的13位毫秒级时间戳。 | 是 |
auth参数说明
| 参数名 | 类型 | 参数类型 | 说明 | 是否必填 |
|---|---|---|---|---|
| type | String | BODY | 认证类型,固定为 sign。 | 是 |
| value | String | BODY | 用户授权后获得的 sign。 | 是 |
payload参数说明
| 参数名 | 类型 | 参数类型 | 说明 | 是否必填 |
|---|---|---|---|---|
| endpointId | String | BODY | 涂鸦音响设备ID。 | 是 |
| 参数名 | 类型 | 说明 |
|---|---|---|
| endpoints | Arrays | 设备端点列表。 |
| result | Object | 响应体 |
| success | Boolean | 成功失败 |
| t | Long | 时间戳 |
endpoints参数说明
| 参数名 | 类型 | 说明 |
|---|---|---|
| endpointId | String | 设备的唯一标识符。 |
| customName | String | 用户为设备设置的自定义名称。 |
| displayCategories | Array | 设备在UI上显示的类别,例如 [“LIGHT”]。 |
| attributes | Array | 描述设备当前状态的属性列表。 |
| actions | Array | 设备支持的操作类型数组 |
endpoints.attributes参数说明
| 参数名 | 类型 | 说明 |
|---|---|---|
| name | String | 属性的名称,用于标识属性的功能,例如 switch。 |
| value | Any | 属性的当前值,可以是字符串、数字或布尔值。 |
| scale | Any | 属性值的单位名称,支持数字、字母和下划线,长度不能超过128个字符。 |
{
"header": {
"namespace": "Tuya.Iot.Smarthome.Discovery",
"name": "Discover",
"messageId": "Unique identifier from Tuya",
"version": "1",
"clientId":"xxx",
"timestamp":"1758875080000"
},
"auth": {
"type": "sign",
"value": "xxxx"
},
"payload": {
"endpointId": "voiceDeviceId_from_tuya"
}
}
{
"result": {
"endpoints": [
{
"endpointId": "001",
"customName": "右床头灯",
"displayCategories": [
"LIGHT"
],
"actions": [
"TurnOn",
"TurnOff",
"SetColor",
"SetColorTemperature",
"IncrementColorTemperature",
"DecrementColorTemperature",
"SetBrightness",
"IncrementBrightness",
"DecrementBrightness"
],
"attributes": [
{
"name": "switch",
"value": false
},
{
"name": "colour_data",
"value": {
"h": 0,
"s": 1000,
"b": 1000
}
},
{
"name": "temp_value",
"value": 0
},
{
"name": "bright_value",
"value": 11
}
]
},
{
"endpointId": "002",
"customName": "左床头窗帘开关",
"displayCategories": [
"SWITCH"
],
"actions": [
"TurnOn",
"TurnOff"
],
"attributes": [
{
"name": "switch",
"value": true
}
]
},
{
"endpointId": "003",
"customName": "睡眠模式",
"displayCategories": [
"SCENE_SWITCH"
],
"actions": [
"SceneActive"
],
"attributes": [
{
"name": "scene",
"value": "active"
}
]
},
{
"endpointId": "004",
"customName": "窗帘",
"displayCategories": [
"CURTAIN"
],
"actions": [
"Pause",
"Continue",
"SetPercentControl",
"IncrementPercentControl",
"StartUp",
"Shutdown"
],
"attributes": [
{
"name": "control",
"value": "open"
},
{
"name": "percent_control",
"value": 0
}
]
},
{
"endpointId": "005",
"customName": "主卧空调",
"displayCategories": [
"AIR_CONDITIONER"
],
"actions": [
"TurnOn",
"TurnOff",
"SetTemperature",
"IncrementTemperature",
"DecrementTemperature",
"SetWindSpeed",
"IncrementWindSpeed",
"DecrementWindSpeed",
"SetMode"
],
"attributes": [
{
"name": "switch",
"value": false
},
{
"name": "temp_set",
"value": 22,
"scale": "℃"
},
{
"name": "fan_speed_enum",
"value": "level_5"
},
{
"name": "mode",
"value": "auto"
}
]
},
{
"endpointId": "006",
"customName": "客厅温控",
"displayCategories": [
"THERMOSTAT"
],
"actions": [
"SetWindSpeed",
"IncrementWindSpeed",
"DecrementWindSpeed"
],
"attributes": [
{
"name": "fan_speed_enum",
"value": "level_5"
}
]
},
{
"endpointId": "007",
"customName": "客厅电视",
"displayCategories": [
"TV"
],
"actions": [
"TurnOn",
"TurnOff",
"SetVolume",
"IncrementVolume",
"DecrementVolume",
"SelectChannel",
"IncrementChannel",
"DecrementChannel"
],
"attributes": [
{
"name": "switch",
"value": false
},
{
"name": "voice_vol",
"value": 91
},
{
"name": "channel",
"value": 1
}
]
}
]
},
"success": true,
"t": 1561381210234
}
当用户通过涂鸦生态入口(如App、音箱)发出控制指令时,涂鸦平台将调用此接口,请求开发者对指定的设备执行操作。
POST 回调域名/control
| 参数名 | 类型 | 参数类型 | 说明 | 是否必填 |
|---|---|---|---|---|
| header | Object | BODY | 指令的头部信息,用于描述指令的元数据。 | 是 |
| auth | Object | BODY | 用户的身份认证信息。 | 是 |
| payload | Object | BODY | 指令的载荷部分。 | 是 |
header参数说明
| 参数名 | 类型 | 参数类型 | 说明 | 是否必填 |
|---|---|---|---|---|
| namespace | String | BODY | 发现意图,固定为:Tuya.Iot.Smarthome.Control | 是 |
| name | String | BODY | 动作名称,固定为:动作识别 | 是 |
| messageId | String | BODY | 消息的唯一标识符,用于追踪和去重。 | 是 |
| version | String | BODY | 指令版本号,默认为 1。 | 是 |
| clientId | String | BODY | 您在涂鸦平台获取的 Client ID。 | 是 |
| timestamp | String | BODY | 当前的13位毫秒级时间戳。 |
auth参数说明
| 参数名 | 类型 | 参数类型 | 说明 | 是否必填 |
|---|---|---|---|---|
| type | String | BODY | 认证类型,固定为 sign。 | 是 |
| value | String | BODY | 用户授权后获得的 sign。 | 是 |
payload参数说明
| 参数名 | 类型 | 参数类型 | 说明 | 是否必填 |
|---|---|---|---|---|
| endpointId | String | BODY | 要控制的目标设备的唯一ID。 | 是 |
| actions | Array | BODY | 当指令需要附带参数时使用。对于无参数指令(如TurnOn),可忽略。 | 否 |
payload.actions参数说明
| 参数名 | 类型 | 参数类型 | 说明 | 是否必填 |
|---|---|---|---|---|
| value | Any | BODY | 指令需要设置的具体值。例如,设置温度为 25。 | 是 |
| name | Any | BODY | 设备属性的code | 是 例如percent_control |
| scale | String | BODY | 参数值的单位或标度,例如温度的单位。 | 否 |
| 参数名 | 类型 | 说明 |
|---|---|---|
| endpoints | Arrays | 设备端点列表。 |
| result | Object | 响应体 |
| success | Boolean | 成功失败 |
| t | Long | 时间戳 |
{
"header": {
"namespace": "Tuya.Iot.Smarthome.Control",
"name": "TurnOn",
"messageId": "Unique identifier from Tuya",
"version": "1",
"clientId":"xxx",
"timestamp":"1758875080000"
},
"auth": {
"type": "BearerToken",
"value": "OAuth2.0 bearer token of the user"
},
"payload": {
"endpointId": "your_unique_device_id_1",
"actions":[{
"name":"switch",
"value":"ON",
"scale":""
}]
}
}
{
"success": true,
"result": true,
"t": 1566053034624
}
| 品类英文名 (displayCategories) | 中文名 |
|---|---|
| SWITCH | 开关 |
| SCENE_SWITCH | 场景开关 |
| SOCKET | 插座 |
| LIGHT | 灯 |
| CURTAIN | 窗帘 |
| THERMOSTAT | 温控器 |
| AIR_CONDITIONER | 空调 |
| TV | 电视 |
| SET_TOP_BOX | 机顶盒 |
注意: 场景和开关不能同时定义!
| 功能点 | 属性名 | 格式 | 值范围 / 枚举值 | 功能备注 | 说明 |
|---|---|---|---|---|---|
| 开关 | switch | Bool | true, false | 控制设备的开启、关闭状态,true表示打开,false表示关闭。 | 用于控制设备的开启和关闭状态。 |
| 场景 | scene | String | active | 场景功能触发,仅仅是表示设备场景能力 | 用于执行场景。此属性仅支持下发控制。 |
| 亮度 | bright_value | Integer | 11 ~ 255 | 控制灯光的亮度值,取值范围为 11-255,11表示最暗,255表示最亮。 | 用于控制设备的亮度。 |
| 色温 | temp_value | Integer | 0 ~ 255 | 控制灯光的色温值,取值范围为 0-255,数值越低代表暖光(偏黄),数值越高代表冷光(偏白)。 | 用于控制灯光的冷暖值。 |
| 颜色 | colour_data | JSON Object | {“h”: 0~360, “s”: 0~1000, “b”: 0~1000} | (H):0-360 (S):0-1000 (V):0-1000 调节灯光的色彩,由色度、饱和度和明度组成,H表示色度,S表示饱和度,V表示明度。 |
HSV颜色模型,用于设置设备颜色。 |
| 电机开关 | control | Enum | open, close, stop, continue | 控制第2路门窗打开、关闭、暂停、继续状态,open表示打开,close表示关闭,stop表示暂停,continue表示继续。 | 用于控制电机类设备,如窗帘。 |
| 百分比控制 | percent_control | Integer | 0 ~ 100 | 范围:0-100;单位:% 按百分比控制门窗开启、闭合程度,取值范围为 0-100,0 表示完全关闭,100表示完全开启。 | 用于按百分比精确控制的设备。 |
| 温度 | temp_set | Number | 摄氏度的范围0-50 华氏度的范围是0-133 |
设置目标温度值, 摄氏度的范围0-50 华氏度的范围是0-133,使用℃或℉ 表示 |
设置设备的目标温度。 |
| 风速 | fan_speed_enum | Enum | 睡眠:标准值 “sleep”. 健康:标准值 “health” 自然:标准值 “natural” 强风:标准值 “strong” 自动:标准值 “auto” 静音:标准值 “mute” 1档:标准值 “level_1” 2档:标准值 “level_2” 3档:标准值 “level_3”, ]4档:标准值 “level_4”, 5档:标准值 “level_5” |
设备风速档位及模式调节,sleep表示睡眠,health表示健康,natural表示自然,strong表示强风,auto表示自动,mute表示静音,level_1表示1档,level_2表示2档,level_3表示3档,level_4表示4档,level_5表示5档 | 用于控制空调/温控风速设备的速度。 |
| 音量 | voice_vol | Integer | 0 ~ 100 | 范围:0-100 | 用于设置设备的音量大小。 |
| 频道切换 | channel | Integer | 0-999 | 范围:0-999 电视频道调节 | 用于设置电视频道等。 |
| 模式 | mode | Enum | auto, cold, hot, wet, wind | 设置空调设备工作模式,auto表示自动,cold表示制冷,hot表示制热,wet表示除湿,wind表示通风。 | 用于设置设备的运行模式,如空调的制冷/制热。 |
| 动作 | 描述 |
|---|---|
| TurnOn | 打开 |
| TurnOff | 关闭 |
| Pause | 暂停 |
| SceneActive | 场景触发 |
| Continue | 继续 |
| SetColor | 设置颜色 |
| SetColorTemperature | 设置灯光色温 |
| IncrementColorTemperature | 增高灯光色温 |
| DecrementColorTemperature | 降低灯光色温 |
| SetBrightness | 设置灯光亮度 |
| IncrementBrightness | 调亮灯光 |
| DecrementBrightness | 调暗灯光 |
| IncrementTemperature | 升高温度 |
| DecrementTemperature | 降低温度 |
| SetTemperature | 设置温度 |
| IncrementWindSpeed | 增加风速 |
| DecrementWindSpeed | 减小风速 |
| SetWindSpeed | 设置风速 |
| SetMode | 设置模式 |
| IncrementVolume | 调高音量 |
| DecrementVolume | 调低音量 |
| SetVolume | 设置音量 |
| DecrementChannel | 上一个频道 |
| IncrementChannel | 下一个频道 |
| SelectChannel | 设置频道 |
| SetPercentControl | 设置百分比控制 |
| IncrementPercentControl | 电机百分比增加 |
| DecrementPercentControl | 电机百分比减少 |
| StartUp | 电机开启 |
| Shutdown | 电机暂停 |
该内容对您有帮助吗?
是意见反馈该内容对您有帮助吗?
是意见反馈