智能语音技能

更新时间:2025-11-10 02:57:54下载pdf

1. 接入流程

在开始进行接口集成之前,您需要完成以下准备工作,以确保后续的API调用能够顺利进行。

1.1. 前期准备流程

  • 注册并登录涂鸦IoT开发平台

    • 访问 并完成企业或个人开发者的注册。
  • 创建云项目

    • 在开发平台中,进入 “云开发” 标签页,创建一个新的 “云项目”。此项目将作为您应用和服务的容器。
  • 获取并提供凭证

    • 在您的云项目概览中,可以查看到一组授权凭证:Client ID 和 Client Secret。

    • 请将这两个值安全地提供给涂鸦平台方并告知回调域名信息。它后续进行接签名验证的唯一依据以及回调。

      Client Secret属于高度敏感信息,请务必保密。

开发者涂鸦云服务1. 注册账号,创建云项目12. 返回 Client ID & Client Secret23. 保留好云项目 Client ID & Client Secret34. (通过商务/运营)通知涂鸦绑定云项目信息以及开发者回调域名45. 确认绑定5开发者涂鸦云服务

1.2. 安全与鉴权机制

为保障通信安全,所有来自涂鸦平台的API调用都附带签名。接入方需要对收到的每一个请求进行签名验证,以确保请求的合法性和数据完整性。

验签算法:HMAC-SHA256

1.3. 验签步骤

当您的服务收到来自涂鸦的请求时,请遵循以下步骤进行验证:

  1. 提取请求信息:从收到的HTTP请求中,获取四个关键信息:

    • client-id (来自 Body)
    • timestamp (来自 Body)
    • sign (来自 Body,即涂鸦生成的签名)
    • payload (指令相关请求数据)
  2. 在本地重新构建待签名字符串:使用收到的 client-idtimestamppayload,按照以下固定顺序拼接成一个字符串。

    待签名字符串 = client-id + timestamp + payload
    
    • 注意:为确保签名一致,涂鸦在生成 payload 时,会对JSON的key按字母顺序排序。您在拼接字符串时,应使用原始的 payload 字符串。
  3. 在本地计算签名:使用您在涂鸦平台获取并预存的 Client Secret 作为密钥,对上一步生成的 “待签名字符串” 进行 HMAC-SHA256 哈希计算,并转换为十六进制字符串。这个结果是您的“本地签名”。

  4. 比对签名:将第1步中获取的 sign 与第3步中您本地计算出的“本地签名”进行字符串全等比较

    • 如果一致:验签通过,继续处理业务逻辑。
    • 如果不一致:验签失败,应立即拒绝该请求,返回 HTTP 状态码 401 Unauthorized

1.4. Java 示例代码

此示例展示了如何在您的服务端使用 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);
    }
}

1.5. API 交互流程

所有业务请求均由涂鸦平台发起,接入方平台负责接收、验签和响应。

涂鸦平台接入方1. 构建请求体 (Request Body)12. 拼接签名字符串 (client_id + timestamp + payload)23. 使用 client_secret 加密, 生成 sign34. 发起业务请求45. 接收请求, 在本地重新计算签名56. 执行验签(比对请求签名与本地计算的签名)67. 执行业务逻辑 (查询设备/控制设备)78. 返回业务处理结果 (Success Response)88. 拒绝请求, 返回 401 Unauthorized9alt[验签成功 (Signature Match)][验签失败 (Signature Mismatch)]涂鸦平台接入方

2. API 列表

接入方需要实现并暴露以下接口,以供涂鸦平台调用。

Method API Endpoint 调用方 实现方 Description
POST /回调域名/discovery 涂鸦平台 接入方 设备发现
POST /回调域名/control 涂鸦平台 接入方 设备控制

2.1 设备发现接口

2.1.1. 接口描述

当需要同步设备信息时,涂鸦平台将调用此接口,以发现指定音响能发现的所有智能设备。接入方需返回该用户可用的设备列表。

2.1.2. 接口地址

POST 回调域名/discovery

**2.1.3. 请求参数 **

涂鸦将发送一个 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。

2.1.4. 响应参数

参数名 类型 说明
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个字符。

2.1.5. 请求示例

{
    "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"
    }
}

2.1.6. 响应示例

{
    "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
}

2.2. 设备控制接口

2.2.1. 接口描述

当用户通过涂鸦生态入口(如App、音箱)发出控制指令时,涂鸦平台将调用此接口,请求开发者对指定的设备执行操作。

2.2.2. 接口地址

POST 回调域名/control

2.2.3. 请求参数

参数名 类型 参数类型 说明 是否必填
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 参数值的单位或标度,例如温度的单位。

2.2.4. 响应参数

参数名 类型 说明
endpoints Arrays 设备端点列表。
result Object 响应体
success Boolean 成功失败
t Long 时间戳

2.2.5 请求示例

{
    "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":""
         }]
    }
}

2.2.6. 响应示例

{
  "success": true,
  "result": true,
  "t": 1566053034624
}

3. 附录

3.1. 设备品类

品类英文名 (displayCategories) 中文名
SWITCH 开关
SCENE_SWITCH 场景开关
SOCKET 插座
LIGHT
CURTAIN 窗帘
THERMOSTAT 温控器
AIR_CONDITIONER 空调
TV 电视
SET_TOP_BOX 机顶盒

3.2. 设备属性

注意: 场景和开关不能同时定义!

功能点 属性名 格式 值范围 / 枚举值 功能备注 说明
开关 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表示通风。 用于设置设备的运行模式,如空调的制冷/制热。

3.3. 动作识别

动作 描述
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 电机暂停