iot-gnvirtualdevice-manage
自然语言驱动的 IoT 业务操作指南
本文档面向「大模型 / 对话式助手」场景:用户通过自然语言完成设备注册、设备解绑、设备上/下线、IoT 设备列表查询等操作。底层业务逻辑与 IoT 平台 HTTP 接口约定一致,与具体宿主(Web、Box、独立 Agent)无关。
使用前必须先完成环境配置;配置须与当前对话角色(或用户)绑定,并由宿主持久化记忆后注入模型上下文——模型本身不长期「背诵」密钥。
1. 文档目标与适用范围
| 目标 | 说明 |
|---|---|
| 意图识别 | 将用户说法映射到固定业务意图(注册 / 解绑 / 上下线 / 列表等)。 |
| 槽位填充 | 从话术中抽取或通过澄清问题补全:productKey、deviceId、单台/批量、家庭名(familyName)、白名单/排除名单、在线状态等;口语说法与 PK/设备 ID 的对应关系见 §4。 |
| 回复与确认 | 成功/失败须含 PK、设备 id、操作名称及失败原因(失败时);列表仅用六列表格;禁止省略明细;环境与入参未明说时须二次确认并可提示「上次沿用」——详见 §1.1。 |
| 安全与上下文 | 登录态、环境(测试/预发/正式)、账号身份由会话上下文提供,模型不杜撰密钥。 |
不在本文范围:账户密码明文存储、绕过平台鉴权、未授权解绑他人设备。
1.1 助手回复格式、列表展示与环境二次确认(必选)
本节约束自然语言助手在执行业务后的输出形态及缺参时的交互,与 §6 各意图配合使用。
1.1.1 业务成功 / 失败时的答复内容
| 场景 | 须包含的信息 |
|---|---|
| 业务成功 | PK(productKey)、设备 id(deviceId)、业务操作名称(如:设备注册、设备绑定、单台解绑、批量解绑、设备上线、设备下线、设备列表查询等)。 |
| 业务失败 | 同上三项,并追加失败原因(接口 msg/message、HTTP 状态或宿主错误信息要点,可脱敏;勿仅用「操作失败」等模糊语)。 |
缺字段时的写法:若某次操作没有 PK 或没有设备 id(例如仅环境校验失败、或列表查询未点名单台),对应项显式留空或写「—」,不得臆造。
明细完整(禁止省略):凡涉及多台设备或多步结果(如批量解绑、按家庭多设备、分批结果),须逐条列出每台设备的 PK、设备 id、操作结果与失败原因(失败时)。禁止使用「等设备共 N 台」「……」「同上」等省略整段明细的方式;允许用完整 Markdown 表格承载多行,但行数须与实际设备/结果一一对应。
特例(设备列表查询成功):须有一句明确 业务操作 为「设备列表查询成功」(可含当前环境、账号脱敏)。每台设备的 PK、设备 id 以 §1.1.2 表格逐行呈现即可,视同已满足成功答复中对 PK、设备 id 的要求,无需在表外再对每台重复一遍。
1.1.2 设备列表查询的专用输出(表格)
查询 user_devices 时,助手仅使用下列列,以 Markdown 表格输出(每台设备一行,不得省略行):
| PK(productKey) | 设备 id | 设备状态 | 家庭 | 品类 | 名称 |
| 列 | 数据来源 |
|---|---|
| PK | productKey,无则空 |
| 设备 id | deviceId,无则空 |
| 设备状态 | 由 isOnline 等映射为「在线」/「离线」(或与平台一致文案),无则空 |
| 家庭 | family.familyName(无 family 则空) |
| 品类 | category,无则空 |
| 名称 | nickName 优先,否则 deviceName,均无则空 |
可按「家庭」列排序以便阅读,但仍为单张表、逐行完整列出,不用「家庭 → 子列表」两级里再省略设备行。
1.1.3 环境与入参未指明时的二次确认
当用户发起依赖环境或关键入参的操作(写操作及依赖 iot_host/账号的读操作),但未明确:
- 运行环境(如 test / pre / prod),或
- 关键入参(如操作账号、
deviceId、productKey、批量白名单/排除名单、家庭范围等,随意图而定)
助手须先不执行或不下结论,向用户二次确认,并:
- 列出在连续对话中可推断的 「上次使用的环境」 与 「上次使用的入参摘要」(账号等敏感字段可脱敏,如
138****47246); - 明确询问:是否沿用上述环境与入参;若用户否定,则请其逐项确认本次环境(及必要入参)。
不得在用户未确认前默认环境或替用户选定设备/账号。
2. 环境配置(使用前必做)与角色绑定记忆
2.1 为什么必须先做环境配置
用户在与大模型进行 IoT 设备列表 / 注册 / 解绑 / 上·下线 等对话之前,必须先完成环境配置。否则助手无法确定调用哪一套 account_host、iot_host、MQTT 入口,也无法安全完成账户登录与后续接口调用。
未完成配置时,助手应仅引导配置,不执行上述业务意图(可回答通用知识类问题除外,由产品策略决定)。
2.2 环境配置应包含什么(业务视角)
| 配置项 | 说明 |
|---|---|
| 地域 | 如国内 / 海外(决定配置域与合规字段)。 |
| 运行环境 | test / pre / prod 等,与 IoT、账户中心域名解析一致。 |
| 登录账号 | IoT 业务使用的用户账号标识。不同运行环境下的账号可以不同(例如测试号与预发号不是同一套账号),须与对应环境成对记录。 |
| 凭证 | 密码等仅允许由宿主系统按安全规范存储(如密文/MD5);不得依赖模型权重记忆明文。不同环境的密码通常不同,必须与该环境下的账号一起、按环境分别记录;禁止假设「同一账号在 test/pre/prod 共用同一密码」。 |
| 解析结果 | 由配置解析出的 account_host、iot_host、MQTT hostname/port 等,供工具调用使用。 |
| 应用凭证(账户中心 Query) | appId、tenantId、appSecret:实际取值见 §2.6(与 device_register_service.account_login 一致)。仓库 env.json 当前未收录这三项,后端仍为代码常量;详见 §2.7。 |
具体字段名可与宿主(如 Box「环境设置」)对齐,但语义须满足上表。
账密按环境分别记录(必选语义)
- 业务事实:测试 / 预发 / 正式等环境的账户中心往往使用不同账号或不同密码(或二者皆不同)。
- 存储粒度:宿主持久化时应至少按
(角色 ID, 地域, 运行环境 env)区分一条配置,每条内保存该环境下的account+ 密码(MD5)及解析出的 host 等。用户切换到「预发」时,应加载预发对应的账密,而不是复用测试环境的口令。 - 自然语言:用户说「切到预发做列表」时,助手应基于当前 env 已绑定的那套账密发起登录;若该环境尚未配置账密,应引导单独补全预发的账号密码,而非沿用测试环境密码。
- 与当前 TestBox 后端的关系:仓库内
EnvConfig表当前为按account唯一的一条记录,内含单个env字段;即同一账号在库中只保留一套环境与密码。若业务要求同一角色名在 test、pre、prod 各有一套独立账密并同时可查可切,存储层需支持多环境多条(例如(account 或 角色键, region, env)联合唯一),本文档的语义以按环境分别记录为准,实现可逐步对齐。
2.3 「大模型记住」的正确含义:与角色绑定 + 持久化上下文
-
不是把账号密码训练进模型参数,也不是要求模型在超长对话中靠注意力「记住」敏感信息。
-
而是:宿主将「当前已生效的环境配置」写入与角色绑定的持久存储(或会话级配置服务),并在每次调用大模型时作为 System / 工具上下文 / 会话变量 注入,例如:
- 角色键:
user_id、tenant_id、或产品内的 「角色 / 助手身份」ID(以下统称 角色 ID)。 - 绑定关系:
角色 ID → 当前环境配置快照(含 env、region、该环境下的 account、解析后的 host 等;密码仅存宿主侧,注入模型时可只注入「已配置、可登录」标志或短时 Token,按安全策略裁剪)。更完整的模型是角色 ID → 多张快照(按 region + env 索引),每张快照含该环境独立的账号与密码摘要。 - 一致性:同一角色在同一时刻应有唯一一套「当前生效环境」(当前选中的 env);用户说「切换到预发」时,应切换为预发已保存的账密与 host,并刷新注入内容,而非仅当轮口头承认,也不得沿用其它环境的密码。
- 角色键:
2.4 对话中助手应对用户可见的「记忆」表述
助手可根据绑定结果复述非敏感信息,便于用户确认,例如:
- 「您当前环境为:预发;预发账号为:138****1170(脱敏);已具备 IoT 操作条件。」(若用户多问测试环境,应说明测试与预发账密分别保存,不会混用。)
若用户询问「你记得我的环境吗」,应基于宿主注入的配置摘要回答,而非编造。
2.5 与下文「会话前置条件」的关系
本节解决 「连哪套环境、绑定哪个角色」;下一节解决 「是否已登录、是否持有 accessToken」。二者均满足后,才执行设备类意图。
2.6 与本仓库 config/env/env.json 对齐的域名(GN)
TestBox 将环境域名集中在 config/env/env.json。自然语言助手在已解析用户为 GN 地域且选定 test / pre / prod 之一时,应使用下表字段拼接 HTTP / MQTT,勿臆造域名。若仓库内 JSON 与下表不一致,以 config/env/env.json 为准,并建议同步更新本文档。
说明:account_host、iot_host、ioc_host 为主机名(不含 https://);下表「完整 URL」类字段按文件中原样给出。
核心对接(账户 / IoT API / MQTT)
| 环境键 | account_host |
iot_host |
MQTT hostname |
mqtt_port |
|---|---|---|---|---|
test |
account-sit.tcljd.com |
io.zx.test.tcljd.net |
device.zx.test.tcljd.net |
1885 |
pre |
account-uat.tcljd.com |
io.zx.pre.tcljd.com |
device.pre.tcljd.com |
1885 |
prod |
cn.account.tcl.com |
io.zx.tcljd.com |
device.tcljd.com |
1885 |
登录网关、IoT 管理台与 IOC
| 环境键 | gn_login_url |
iot_operation_manager_url |
iot_manager_url |
ioc_host |
|---|---|---|---|---|
test |
https://gateway-security-test.tcljd.com/auth-service/sso/login |
https://iot-operation-manager-test.tcljd.com |
https://iot-manager-test.tcljd.com |
gateway-security-test.tcljd.com |
pre |
https://gateway-security-pre.tcljd.com/auth-service/sso/login |
https://iot-operation-manager-pre.tcljd.com |
https://iot-manager-pre.tcljd.com |
gateway-security-pre.tcljd.com |
prod |
https://gateway-security.tcljd.com/auth-service/sso/login |
https://iot-operation-manager-prod.tcljd.com |
https://iot-manager-prod.tcljd.com |
gateway-security.tcljd.com |
账户中心登录 Query:appId / tenantId / appSecret(GN,各环境相同)
调用 POST https://{account_host}/auth/auth/login 时,Query 须携带下列三参数。取值与 backend/app/services/device_register_service.py 中 account_login 字面量一致;test / pre / prod 共用同一组。
| 参数 | 值 |
|---|---|
appId |
53311662522411095 |
tenantId |
tcl |
appSecret |
fb4dde164befadecf3ff9c7da9b5e7b08339bde138f52e47ef1b5a7e4d309607 |
完整 方法 / 路径 / Query / 请求头 / 请求体 见 §10.2.1(勿用 JSON 字段名 account,须用 username)。
安全:
appSecret属应用密钥;自然语言助手勿向用户全文朗读,宿主注入工具上下文时可裁剪或由后端持有。
JSON 路径:根键为地域 GN,其下为 test | pre | prod 对象,字段名与上表一致。域名类字段全文快照见 §10.1(§10.1 中另附三参数,便于与 env.json 对照合并使用)。
2.7 TestBox 现有代码中 appId / tenantId / appSecret 的取数逻辑(只读说明)
本节仅整理当前仓库实现,便于对接自然语言宿主时知道「这三项从哪来」;不要求据此改业务代码。
| 问题 | 结论 |
|---|---|
config/env/env.json 是否提供这三项? |
否(仓库 JSON 当前无此三字段)。具体字面量见 §2.6 表格,与代码一致。 |
| 主项目里从哪里取值? | 全部在 backend/app/services/device_register_service.py 的 account_login 内,以字面量拼进账户中心登录 URL 的 Query。三者为固定字符串,不随 test / pre / prod 分支变化(即当前实现下各环境共用同一组应用凭证)。 |
谁在用 account_login? |
凡需先登账户中心拿 accessToken 的流程都会走到该函数,例如:设备注册、环境保存前校验登录、设备列表/解绑/上下线/导出等与 IoT 列表相关的路由与服务(通过 account_login(account_host, …) 间接使用上述 Query)。取数路径唯一:只有 account_login 这一处拼装三参数。 |
| 仓库内是否还有其它拷贝? | hiscode/ 下历史脚本(如各 ElectricityReport 的 check_data.py、iot_openplatform.py)中,登录 URL 同样硬编码了与主项目相同语义的一组 appId、tenantId、appSecret(与 TestBox 后端是否为逐字符一致以源码为准)。不属于 env.json 加载链路。 |
自然语言 / 宿主侧含义:助手描述「登录账户中心」时,应知三参数在现网代码中为应用级常量、非用户口令;用户账号密码仍在 Body(MD5)中。若未来改为从配置读取,再同步更新本节与 env.json 说明即可。
3. 会话前置条件(环境配置与角色绑定完成之后)
自然语言执行任何 IoT 写操作前,在已完成第 2 节环境配置且已注入当前角色上下文的前提下,系统还应具备(或由助手引导用户完成):
- 环境解析结果:已由配置解析出
account_host、iot_host(测试 / 预发 / 正式之一),并与当前角色绑定。 - 身份:已使用当前选中环境下已保存的账号与密码(MD5)登录账户中心,会话内持有有效
accessToken(及业务需要的accountId等)。换环境即应换对应账密;不可用 A 环境密码去登 B 环境account_host。 - 密码形态:调用账户中心登录时使用 MD5 小写 密码;若用户只说「帮我登录」,由上层系统按当前 env 取数并完成登录后再进入设备类意图。
模型提示:若用户直接说「注册设备」但未完成环境配置,应先引导完成配置与角色绑定;若未完成登录,应回复需先登录或请用户在 App 内完成授权,而不是编造 Token。
4. productKey 与 deviceId:多种说法与识别
本节供模型与宿主做槽位归一:用户口语中的「设备号」「PK」等需映射到接口字段 productKey 或 deviceId,二者不可混用(注册用 PK;解绑/上下线以 deviceId 为主)。
4.1 productKey(产品 Key / PK)
| 用户可能的说法 | 归一槽位 | 说明 |
|---|---|---|
PK、pk、productKey、product key |
productKey |
英文缩写与驼峰字段同名。 |
| 产品 Key、产品 key、产品密钥、产品码 | productKey |
中文全称与「码」类口语。 |
| 物模型产品 ID、品类 Key(口误时) | productKey |
若后接一串字母数字且用于注册/选品,按 PK 处理;若用于点名某台已绑定设备,应先拉列表用 productKey 字段匹配。 |
| 三元组里的产品、IoT 产品标识 | productKey |
与「设备三元组」连用时,产品侧一般指 PK。 |
形态提示:多为字母与数字混合(如 geBEo11zghKClWae、u1N2g4o9tgBavstB),也可能为数字 + 字母后缀(如 1112017519N)。不等同于单台设备的 deviceId。
4.2 deviceId(设备 ID)
| 用户可能的说法 | 归一槽位 | 说明 |
|---|---|---|
设备 ID、设备 id、deviceId |
deviceId |
与接口字段一致。 |
| 设备号、设备编号、终端 ID、DID、did | deviceId |
口语「号」在解绑/上下线语境下优先指 deviceId。 |
| thing ID、物模型设备 ID、云端设备标识 | deviceId |
与平台文档用语对齐。 |
| 「第几个设备」「列表里第一台」 | 需解析 | 无直接 ID:应先 user_devices 再按序或按昵称确认,不得臆造。 |
形态提示:可能是纯数字(如 45974875、51252289),也可能是字母数字混合或类 Base64(如 BCXBzQAAAAE)。纯数字串在部分话术里易被误认为手机号片段,需结合列表是否存在该 deviceId 校验。
4.3 易混淆项(不是 deviceId / 不是 productKey 时的处理)
| 用户说法 | 更像什么 | 处理 |
|---|---|---|
MAC、网卡地址、aa:bb:cc:dd:ee:ff |
mac |
用列表项的 mac 字段匹配,再得到 deviceId。 |
| SN、序列号、机身码 | sn |
用列表项的 sn 匹配(可能含掩码 X),再得到 deviceId / 确认 PK。 |
| 昵称、房间里的名字(如「空调 1」「客厅柜机」) | nickName / 描述 |
列表匹配 nickName、deviceName、locationName,再得到 deviceId。 |
familyName、家庭名 |
家庭范围 | 见 §6.4 批量解绑(按家庭),不是单个 ID。 |
4.4 识别优先级(建议)
- 注册、要「新增一台」且只给了一串码 → 缺省填
productKey,除非形态明显为已有列表中的deviceId(极少用于注册话术)。 - 解绑、上下线、查某台是否在列表 → 优先
deviceId;若用户给的是 PK → 拉user_devices后按productKey筛选,若多台则追问昵称/MAC/deviceId。 - 同时出现「产品」+「设备」 → 「产品 xxx」多指 PK,「设备 xxx」多指
deviceId。 - 无法判断 → 一轮澄清:「您指的是产品 Key(PK)还是已绑定设备的设备 ID?」必要时展示列表中的两项字段供用户对选。
5. 意图与平台动作映射总表
| 用户意图(示例) | 平台侧动作概要 |
|---|---|
| 查看设备列表 / 我有哪些设备 | 登录(若需)→ GET /v1/tclplus/user/user_devices;按 data[].family.familyName(及 familyId)分群展示 |
| 注册设备 / 新增一台虚拟设备 | get_bind_code → thing_register → POST …/v1/thing/bind(把设备绑到当前用户,详见 §10.2.5) |
| 解绑某台设备 | user_devices 定位 → POST /v1/thing/multiUnbind(单条) |
| 按家庭解绑(整户 / 除某台外其余) | user_devices 按 family 筛选 → 得到 deviceId 集合 → multiUnbind(可一批或多批) |
| 批量解绑 / 全部解绑保留几台 | user_devices 过滤 → 分批 multiUnbind(可与「按家庭」组合:先定家庭再套白名单) |
| 设备上线 / 下线 | POST /shadows/device/connection |
6. 各意图详细说明
6.1 IoT 设备列表
业务含义:列出当前用户已绑定设备,含设备 ID、产品 Key、在线状态、所属家庭等。
接口数据结构要点(GET .../user_devices)
- 返回体中
data为设备数组;每条设备对象常含:deviceId、productKey、category、mac、nickName、deviceName、isOnline、locationName,以及嵌套对象family:familyId、familyName。 - 同一
familyId(或同一familyName)下的设备属于同一家庭;接口返回为扁平数组。助手输出须遵守 §1.1.2:用单张六列表格逐行展示,可按「家庭」列排序,不再要求「家庭 → 二级设备列表」结构以免与「仅表格、不省略」冲突。
自然语言示例
- 「列出我账号下所有设备」
- 「看看预发环境绑定了哪些空调」
- 「设备 12729664 在不在列表里」
- 「1234 这个家庭里有哪些设备」
槽位
| 槽位 | 必填 | 说明 |
|---|---|---|
| 环境 | 否 | 未指定则用会话默认环境。 |
| 过滤条件 | 否 | 如指定 deviceId / productKey / 家庭名 / 品类关键字,在返回列表中筛选后再按家庭分组展示。 |
平台调用链
- 确保有
accessToken。 GET https://{iot_host}/v1/tclplus/user/user_devices- Header:
accessToken、Content-Type: application/json
- Header:
- 解析
data数组:排序可按familyName、nickName等便于阅读;输出格式严格按 §1.1.2 六列表格,一行一台设备,字段齐全不省略。
回复要点:与 §1.1 一致;本意图不展示房间(locationName)等超出六列的字段,除非用户另行追问。勿在回复中复述完整鉴权头、Token、签名或用户粘贴的 curl。 环境与账号未在本次说清时,执行前按 §1.1.3 二次确认。
6.2 设备注册(thing 注册 + 绑定)
业务含义:在 IoT 侧创建设备实例并绑定到当前用户(虚拟设备注册场景)。
自然语言示例
- 「用产品 Key
ae9ndacm8mqcij9j帮我注册一台设备」 - 「给我注册一个新设备,PK 是 xxx」
- 「再注册一台,还是刚才那个产品」
槽位
| 槽位 | 必填 | 说明 |
|---|---|---|
productKey |
是 | 产品密钥(设备 PK),可从用户话术中抽取或追问。 |
| 环境 | 否 | 默认会话环境。 |
平台调用链(顺序固定)
POST https://{iot_host}/v1/auth/get_bind_code→ 取bindCode。- 生成平台要求的
did/sn(与产品规则一致,如 PK + 随机后缀)。 POST https://{iot_host}/v1/auth/thing_register- Body 含:
bindCode、productKey、sn、did、时间、版本信息等平台约定字段。
- Body 含:
- 绑定到用户:
POST https://{iot_host}/v1/thing/bind(与平台约定一致,见 §10.2.5)- Header:
accessToken(要绑定到的用户,注册场景为当前登录用户)、Content-Type: application/json - Body:仅
deviceId(字符串)。
- Header:
回复要点:输出遵守 §1.1.1(PK、设备 id、操作名称;失败加原因;缺项留空)。鉴权 token 若需展示须脱敏。环境与账号未明确时 §1.1.3。
换绑:同一接口、同一路径;先对旧用户 multiUnbind,再用新账号登录得到的 accessToken 调用 POST …/v1/thing/bind,Body 仍为 deviceId(§10.2.5)。
澄清话术示例:「请提供要注册的产品 Key(PK)。」
6.3 设备解绑(单台)
业务含义:将指定设备从当前用户解绑。
与「按家庭解绑」的关系:若用户表述的是某个家庭下全部设备或某家庭下除某几台外的所有设备,应先识别为 6.4 批量解绑(含家庭范围),不要按单条误执行;单本节仅适用于明确指向一台(或经列表唯一匹配的一台)。
自然语言示例
- 「把设备 12729664 解绑掉」
- 「解绑 productKey 是 xxx 的那台」
- 「删掉绑定里的第一台设备」(需先列表再确认 ID,避免误操作)
槽位
| 槽位 | 必填 | 说明 |
|---|---|---|
deviceId |
条件 | 优先用明确 ID;若只有 PK,需先拉列表再匹配或追问。 |
平台调用链
GET .../v1/tclplus/user/user_devices- 找到目标项,组装一条
deviceInfo:deviceId、userId(masterId/userId/accountId)、model(数组)。 POST https://{iot_host}/v1/thing/multiUnbind- Body:
{ "deviceInfos": [ 单条 ] }
- Body:
回复要点:遵守 §1.1.1(含失败时 msg 等原因);单台解绑一台一行信息,不省略。
安全:执行前可对高风险话术二次确认:「确认解绑设备 12729664?该操作不可撤销。」
6.4 批量解绑
业务含义:一次解绑多台;支持全账号范围或限定在某个家庭(family)内,常配合「白名单」(保留不解绑)或「排除名单」(只不解绑这几台,其余在该范围内全部解绑)。
意图识别(必须先判范围)
| 用户说法类型 | 解析要点 |
|---|---|
| 整户解绑 | 如「把『1234』这个家庭里设备全解绑」「鸿蒙正式测试家庭全部解绑」→ 槽位:familyName(或 familyId)无排除名单 → 目标集合 = user_devices 中 family.familyName(或 familyId)匹配项的全部 deviceId。 |
| 家庭内「除某台外其余」 | 如「厨房那户除了空调 45974875,其他都解绑」「1234 家庭里除了 nickName 空调1 的都删掉」→ 槽位:familyName + excludeDeviceIds(或昵称 → 解析为 deviceId) → 目标集合 = 该家庭内设备 减去 排除列表。 |
| 全账号 + 白名单 | 如「除了 A、B 其它全解绑」且未提家庭 → 与现有一致:whitelist 保留,其余全部解绑。 |
| 歧义 | 家庭名与用户昵称、房间名可能混淆(如「厨房」既是 locationName 也可能是家庭名的一部分)→ 先拉列表,用 family.familyName / familyId 与用户确认后再组 deviceInfos。 |
自然语言示例
- 「除了 12729664 和 12729665,其它全部解绑」
- 「批量解绑,白名单 12729664」
- 「清空我账号下所有设备绑定」(无白名单 = 全部解绑,高风险,必须确认)
- 「1234 这个家庭下面的设备全部解绑」
- 「鸿蒙正式测试里除了空调 D 柜机,其它都解绑」(昵称 → 列表匹配
deviceId)
槽位
| 槽位 | 必填 | 说明 |
|---|---|---|
scope |
否 | account(默认,全账号)或 family;为 family 时必须解析出 familyName 或 familyId。 |
familyName / familyId |
条件 | scope=family 时必填其一;以列表接口返回字段为准。 |
whitelist |
否 | 全账号批量时:保留的 deviceId 列表。 |
excludeDeviceIds |
否 | 家庭内「除某几台外」:在该家庭子集中排除这些 deviceId(与全账号 whitelist 二选一或按产品优先级:先定 scope 再选保留/排除语义)。 |
| 分批大小 | 系统 | 例如每批最多 100 条,多批调用 multiUnbind。 |
平台调用链
GET .../user_devices- 若
scope=family:筛出family匹配的设备子集;再应用excludeDeviceIds(从子集中移除)或(全账号场景)过滤掉whitelist中的deviceId。 - 将待解绑项转为
deviceInfos,按批POST .../v1/thing/multiUnbind
回复要点:遵守 §1.1.1:每台被解绑(或尝试解绑)的设备均须给出 PK、设备 id、操作结果与失败原因;可另附汇总(总数、跳过数、批错误),但不得以汇总替代逐台明细。环境与白名单/家庭范围未明确时 §1.1.3。
安全:家庭级「全部解绑」与全账号全部解绑同属高风险,必须二次确认,并复述即将解绑的 deviceId 或昵称列表(可脱敏 MAC)。
6.5 设备上线 / 下线
业务含义:通过影子连接接口上报设备在线状态(HTTP 侧声明,与真实 MQTT 状态可能配合使用)。
自然语言示例
- 「把 12729664 上线」
- 「设备 xxx 设为离线」
- 「全部标记为在线」(需列表后逐台或按平台是否支持批量决定)
槽位
| 槽位 | 必填 | 说明 |
|---|---|---|
deviceId |
是 | 一台或多台(多台则循环或按平台批量能力)。 |
online |
是 | true = 上线,false = 下线。 |
平台调用链
- 确保
accessToken。 POST https://{iot_host}/shadows/device/connection- Header:
accessToken、Content-Type: application/json - Body:
[ { "deviceId": "<id>", "online": true|false } ]
- Header:
回复要点:遵守 §1.1.1:每台设备一行(或表一行),含 PK、设备 id、上线/下线操作结果与失败原因,禁止仅用「共 N 台成功」省略。
消歧:「下线」可能被理解成解绑;若用户同时提到「解绑」「删除绑定」,应走 multiUnbind,而非仅 online: false。
7. 意图歧义与路由规则(供模型参考)
| 用户说法 | 更可能意图 | 备注 |
|---|---|---|
| 「注销设备」 | 解绑 | 非账户注销。 |
| 「删除设备」 | 解绑 | 若平台另有「删除设备」API,需产品定义;默认按解绑。 |
| 「下线」 | 连接状态 | 若明确说「解绑」「取消绑定」则走 multiUnbind。 |
| 「注册」 | thing_register | 与「添加已有设备」(仅对已存在 did 调 POST …/v1/thing/bind)区分。 |
| 「有哪些设备」 | 列表 | 与「统计数量」同属列表意图;输出须为 §1.1.2 六列表格、逐行完整,可另起一句给出台数。 |
| 「XX 家庭全解绑 / 除了某台都解绑」 | 批量解绑(家庭范围) | 先 user_devices 用 family 定界,再算目标 deviceId 集合;勿当作单台解绑。 |
| 「厨房的设备…」 | 视上下文 | 可能是 locationName(房间内多台)或口误指家庭名;以列表中的 family / locationName 澄清。 |
| 「产品 / PK / 产品 Key + 一串码」 | 注册或按 PK 筛设备 | 映射 productKey;解绑场景下多台同 PK 需追问 deviceId 或昵称。 |
| 「设备号 / 设备 ID / DID + 一串码」 | 解绑、上下线、查列表 | 映射 deviceId(先与列表校验)。 |
| 仅给一串码、未说产品/设备 | 依赖意图与形态 | 注册意图 → 倾向 PK;解绑/下线 → 倾向 deviceId;仍不确定则按 §4.4 澄清。 |
8. 标准对话流程示例(非代码)
环境与入参二次确认(§1.1.3)
用户:帮我把那台空调解绑掉。
助手:您未说明环境、账号或具体设备 id。根据上一轮对话,您使用的是测试环境、账号 138****47246、曾操作设备 45974875。是否沿用该环境与账号?若有多台空调,请补充要解绑的设备 id 或名称。
切换环境须用对应账密
用户:改成测试环境查一下设备。
助手:已切换到测试环境。请确认已在测试环境保存过测试专用账号与密码(与预发可能不同);若尚未配置,请先完成测试环境的环境设置后再查列表。
列表(六列表格,不省略)
用户:我预发账号下有哪些设备?
助手:设备列表查询成功(环境:预发,账号:138****)。共 2 台。
PK(productKey) 设备 id 设备状态 家庭 品类 名称 1112017519N 45974875 在线 1234 AC 空调2宿舍 1112694669N 51252289 离线 1234 AC 空调1
按家庭批量解绑
用户:把 1234 这个家庭里除了空调 1 以外都解绑。
助手:将解绑家庭「1234」下除设备 51252289(空调1)外的共 1 台,是否确认?
用户:确认。
助手:已提交解绑,…
注册
用户:用 PK ae9ndacm8mqcij9j 注册一台。
助手:PK:ae9ndacm8mqcij9j;设备 id:ae9ndacm8mqcij9jabc123;业务操作:设备注册与绑定;结果:成功。(鉴权 token:***,已脱敏)
PK 与设备 ID 澄清
用户:帮我把 1112017519N 解绑掉。
助手:1112017519N 是您列表里空调的产品 Key,同 PK 下有多台设备。要解绑哪一台?可以说设备 ID 或昵称(例如「空调 1」)。
解绑(成功答复格式 §1.1.1)
用户:解绑 12729664。
助手:确认将设备 12729664 从当前账号解绑吗?
用户:确认。
助手:PK:1112017519N;设备 id:12729664;业务操作:单台解绑;结果:成功。
失败答复格式(§1.1.1)
用户:解绑 99999999。
助手:PK:—;设备 id:99999999;业务操作:单台解绑;结果:失败;失败原因:当前账号设备列表中未找到该设备 id。
上下线
用户:把 12729664 上线。
助手:PK:1112017519N;设备 id:12729664;业务操作:设备上线;结果:成功。
9. 错误与降级
| 情况 | 建议行为 |
|---|---|
| Token 过期 | 提示重新登录,不重复提交写操作。 |
| 登录失败且刚切换环境 | 可能使用了其它环境的密码或该环境尚未单独保存账密;提示用户按 §2.2 为当前 env 配置/更新账号与密码,勿假定各环境口令相同。 |
| 设备不在列表 | 解绑/下线前提示「当前列表未找到该设备」。 |
| 家庭名匹配不到或多义 | 列出 user_devices 中出现的 familyName 列表请用户点选或复述确认;勿猜测 familyId。 |
| 用户把 PK 当成设备号(或相反) | 按 §4 区分;同 productKey 多台时禁止默认解绑第一台,须点名 deviceId/昵称/MAC。 |
| 批量解绑部分失败 | 汇报成功数、失败数及失败原因片段。 |
| 用户未说 PK | 注册意图触发澄清轮:「请提供产品 Key。」 |
POST …/v1/thing/bind 失败 |
核对 deviceId 是否已为当前 Token 对应用户完成 thing 注册、Token 是否过期、是否应先解绑再换绑;HTTP 非 200 时看响应 msg/message。 |
10. 附录
10.1 环境参数合并快照(config/env/env.json + 账户中心 Query)
- 域名等字段:与仓库
config/env/env.json一致;维护域名时以仓库文件为唯一事实来源。 appId/tenantId/appSecret:仓库env.json尚未包含;下列 JSON 为文档增补,取值与device_register_service.account_login同步。若日后写入env.json,应同时改后端读配置逻辑并更新本文。
{
"GN": {
"test": {
"account_host": "account-sit.tcljd.com",
"iot_host": "io.zx.test.tcljd.net",
"gn_login_url": "https://gateway-security-test.tcljd.com/auth-service/sso/login",
"iot_operation_manager_url": "https://iot-operation-manager-test.tcljd.com",
"iot_manager_url": "https://iot-manager-test.tcljd.com",
"hostname": "device.zx.test.tcljd.net",
"mqtt_port": 1885,
"ioc_host": "gateway-security-test.tcljd.com",
"appId": "53311662522411095",
"tenantId": "tcl",
"appSecret": "fb4dde164befadecf3ff9c7da9b5e7b08339bde138f52e47ef1b5a7e4d309607"
},
"pre": {
"account_host": "account-uat.tcljd.com",
"iot_host": "io.zx.pre.tcljd.com",
"gn_login_url": "https://gateway-security-pre.tcljd.com/auth-service/sso/login",
"iot_operation_manager_url": "https://iot-operation-manager-pre.tcljd.com",
"iot_manager_url": "https://iot-manager-pre.tcljd.com",
"hostname": "device.pre.tcljd.com",
"mqtt_port": 1885,
"ioc_host": "gateway-security-pre.tcljd.com",
"appId": "53311662522411095",
"tenantId": "tcl",
"appSecret": "fb4dde164befadecf3ff9c7da9b5e7b08339bde138f52e47ef1b5a7e4d309607"
},
"prod": {
"account_host": "cn.account.tcl.com",
"iot_host": "io.zx.tcljd.com",
"gn_login_url": "https://gateway-security.tcljd.com/auth-service/sso/login",
"iot_operation_manager_url": "https://iot-operation-manager-prod.tcljd.com",
"iot_manager_url": "https://iot-manager-prod.tcljd.com",
"hostname": "device.tcljd.com",
"mqtt_port": 1885,
"ioc_host": "gateway-security.tcljd.com",
"appId": "53311662522411095",
"tenantId": "tcl",
"appSecret": "fb4dde164befadecf3ff9c7da9b5e7b08339bde138f52e47ef1b5a7e4d309607"
}
}
}
10.2 HTTP 接口请求约定(与 TestBox 后端一致)
以下与 backend/app/services/device_register_service.py、device_unbind_service.py 中实际调用一致。{account_host}、{iot_host} 见 §2.6 / §10.1;除登录外,IoT 接口均需在请求头携带用户 accessToken(账户中心登录返回)。
10.2.1 账户中心登录
| 项目 | 内容 |
|---|---|
| 方法 | POST |
| 路径 | https://{account_host}/auth/auth/login |
| Query | appId=53311662522411095&tenantId=tcl&appSecret=fb4dde164befadecf3ff9c7da9b5e7b08339bde138f52e47ef1b5a7e4d309607(取值见 §2.6 表) |
| 请求头 | Content-Type: application/json |
| 请求体(JSON) | username:登录账号(字符串);password:密码 MD5 32 位小写;channel:固定 "app"。 |
示例(测试环境,仅作格式参考):
POST https://account-sit.tcljd.com/auth/auth/login?appId=53311662522411095&tenantId=tcl&appSecret=fb4dde164befadecf3ff9c7da9b5e7b08339bde138f52e47ef1b5a7e4d309607
Content-Type: application/json
{
"username": "<登录账号>",
"password": "<密码 MD5 小写 32 位>",
"channel": "app"
}
常见错误:请求体使用字段名
account而非username时,接口可能返回业务错误「账号或者密码错误」。须与后端account_login一致使用username。
成功时响应体含 accessToken、accountId 等(后端以 msg == "login success!" 且存在 accessToken 为准)。
10.2.2 用户设备列表
| 项目 | 内容 |
|---|---|
| 方法 | GET |
| 路径 | https://{iot_host}/v1/tclplus/user/user_devices |
| Query | 无 |
| 请求头 | Content-Type: application/json;accessToken: <用户 accessToken> |
| 请求体 | 无 |
10.2.3 获取绑定码
| 项目 | 内容 |
|---|---|
| 方法 | POST |
| 路径 | https://{iot_host}/v1/auth/get_bind_code |
| Query | 无 |
| 请求头 | Content-Type: application/json;accessToken: <用户 accessToken> |
| 请求体 | 无(空 body) |
10.2.4 设备注册(thing_register)
| 项目 | 内容 |
|---|---|
| 方法 | POST |
| 路径 | https://{iot_host}/v1/auth/thing_register |
| Query | 无 |
| 请求头 | Content-Type: application/json;accessToken: <用户 accessToken> |
| 请求体(JSON) | 与后端 thing_register 一致,主要字段见下表;timezoneTime 为调用时刻本地时间字符串;bindCode 来自 §10.2.3。 |
| 字段 | 说明 |
|---|---|
timezoneTime |
如 "2026-04-09 12:00:00"(后端 strftime) |
longitude / latitude |
后端示例 "113.92" / "22.58" |
productSecret |
与 productKey 相同(产品 Key) |
sn |
与 did 一致时为设备侧序列/标识(后端用生成的 device_id) |
productKey |
产品 Key |
bindCode |
绑定码 |
ssid |
后端示例 "HUAWEI-004J0O" |
version |
对象,含 partitions 数组与 main 版本号(与后端示例结构一致即可) |
parentId |
后端传 "" |
did |
设备 ID(与 sn 一致) |
10.2.5 设备绑定(统一接口:/v1/thing/bind)
平台约定:将设备关联到某用户账号时,使用同一路径(注册后挂当前用户、换绑挂新用户,区别仅 accessToken 归属与前置是否已解绑)。
| 项目 | 内容 |
|---|---|
| 方法 | POST |
| 路径 | https://{iot_host}/v1/thing/bind |
| Query | 无 |
| 请求头 | Content-Type: application/json;accessToken: <要绑定到的用户的 accessToken> |
| 请求体(JSON) | 仅 deviceId(字符串,与 thing_register 返回或本地生成的设备 ID 一致)。 |
{
"deviceId": "<设备ID>"
}
场景说明
| 场景 | accessToken |
前置条件 |
|---|---|---|
注册流程:thing_register 成功后,把设备绑到当前登录用户 |
当前执行注册/登录的用户 | 已完成账户中心登录;deviceId 已在 IoT 侧创建 |
| 换绑:设备改挂到另一账号 | 新账号登录后的 Token | 旧账号侧已对该设备 multiUnbind;再调本接口(与 device_unbind_service.rebind_single / thing_bind 一致) |
与 App / 手工调试:直连 env.json 的 iot_host 即可;App 若走 BFF,域名可能不同,路径仍以 /v1/thing/bind 为语义基准。
与当前仓库代码(实现差异):device_register_service.bind_device 仍调用历史路径 POST …/user/devices/binding(且 Body 含 productKey)。本文档与平台联调、自然语言宿主工具以 /v1/thing/bind 为准;后端宜后续改为与本节一致,避免测试环境旧路径 404 与文档脱节。
10.2.6 批量解绑(含单台)
| 项目 | 内容 |
|---|---|
| 方法 | POST |
| 路径 | https://{iot_host}/v1/thing/multiUnbind |
| Query | 无 |
| 请求头 | Content-Type: application/json;accessToken: <用户 accessToken> |
| 请求体(JSON) | deviceInfos:数组;每项含 model(数组,列表项无型号时可为 [])、userId(如列表中的 masterId)、deviceId。 |
{
"deviceInfos": [
{
"model": [],
"userId": "<masterId>",
"deviceId": "<设备ID>"
}
]
}
10.2.7 设备上线 / 下线(连接状态)
| 项目 | 内容 |
|---|---|
| 方法 | POST |
| 路径 | https://{iot_host}/shadows/device/connection |
| Query | 无 |
| 请求头 | Content-Type: application/json;accessToken: <用户 accessToken> |
| 请求体(JSON) | 数组:每项 deviceId、online(布尔)。 |
[
{
"deviceId": "<设备ID>",
"online": true
}
]
索引:登录 → 10.2.1;列表 → 10.2.2;绑定码 → 10.2.3;thing 注册 → 10.2.4;设备绑定 /v1/thing/bind → 10.2.5;批量解绑 → 10.2.6;上/下线 → 10.2.7。
appId / tenantId / appSecret 见 §2.6;若未迁入 env.json,由宿主按 account_login 与上文 Query 一致拼接。其它地域以 env.json 为准并同步更新 §2.6 / §10.1。
文档版本:设备绑定以 POST /v1/thing/bind 为准(与平台约定);bind_device 实现路径见 §10.2.5 说明。其它与 config/env/env.json(GN)、account_login 及后端 HTTP 一致;平台若有变更,以 OpenAPI / 对接说明为准。