设备在线了,后台还是 0 台?开放平台从注册到首台设备绑定
周五夜里,同事说摄像头在线、App 能预览,开放平台设备列表却是空的。真不是签名写错——设备还没进开发者账号。配网成功,不等于绑定成功;第一台设备验收,用 listDeviceDetailsByPage 看有没有你的 deviceId。
为什么说现在搞懂这条链路很值
调用乐橙设备能力的第三方与个人开发者:实时预览、录像回放、告警、云存储等,如果想要使用上述这些能力的前提都建立在“设备已挂在你的账号名下”。否则一切功能都无法接入,接下来我重点讲解下如何接入设备。
集成延期,常见不是「业务难」,而是阶段被混在一块:
| 阶段 | 误解 | 实际 |
|---|---|---|
| 配网 | Wi-Fi 连上即可 | 设备可上网、App 可发现 |
| 绑定 | App 能看即可 | 设备须进入开发者账号资产池 |
| 调 API | 有 appId 即可 | 须先 accessToken,再在 params 里带 token |
目前有三种绑定入口:
- 官方App(现场配网 + 单机验证;开发者账号可直接登录 App)
- 控制台 · 设备接入服务(实施/运维友好)
bindDeviceOpenAPI(SaaS、批量自动化)
当前协议 vs 旧版协议(必读)
官方在中写明:旧版本协议后续不再维护,请更换替代接口。
因此新接入应统一走:
POST https://openapi.lechange.cn/openapi/{method}
Content-Type: application/json
{
"system": { "ver", "appId", "time", "nonce", "sign" },
"id": "<请求唯一ID>",
"params": { ...业务参数 }
}
1、鉴权签名规则以平台内的开发规范为准,不要混用网上过时的示例(不同域名、不同 body 结构、无 system.sign 等)。
2、移动端若用 OpenSDK 播放/配网,属于 SDK 集成路径,与本文 HTTP OpenAPI 绑设备 可并行,但服务端业务仍建议云云对接。
总流程(一张图记住)
flowchart TB
subgraph prep [准备]
R[官方平台注册]
A[创建应用 appId/appSecret]
end
subgraph device [设备]
W[Wi-Fi 配网上线]
B[App / 控制台 / bindDevice]
end
subgraph api [当前 OpenAPI]
T[accessToken]
L[listDeviceDetailsByPage 验收]
end
R --> A --> W --> B --> T --> L
L --> N[下一步: bindDeviceLive 等]
【实战应用】
1. 注册与创建应用
https://open.imou.com/ → 登录 → 创建应用 → 应用详情复制 appId / appSecret
根据官方提示完成应用创建:应用与开发者账号关联,后续设备绑定、增值服务均基于该账号体系。
踩坑 A:appSecret 写进前端或公开仓库 → 应仅部署在服务端。
2. 设备配网(物理层)
具体方式如下:
设备上电 → 连接 Wi-Fi → 官方App(开发者账号登录)→ 扫码/手动添加序列号 → App 内可预览
踩坑 B:只完成 App 添加,未进入开发者资产池 → 后续分页列表仍可能为空(若账号不一致或未走绑定流程)。
3. 绑定到开发者账号(三选一)
控制台(方式二)
控制台 → 产品与服务 → 设备接入服务 → 添加设备
输入:deviceId(机身标签序列号)+ code(验证码)
OpenAPI:bindDevice(方式三,推荐自动化)
方式:绑定设备
地址:POST https://openapi.lechange.cn/openapi/bindDevice
| 参数 | 说明 |
|---|---|
token | 管理员 accessToken |
deviceId | 设备序列号 |
code | 见下表 |
code 传参(绑失败优先查这张表):
| 场景 | 传值 |
|---|---|
| 未改密 | 机身标签 8 位安全码 |
| 已改密 | 设备密码(不是安全码) |
| 标签无 8 位安全码 | 空字符串 "" |
高安全可选 encryptCode(AES 规则见同一文档);code 与 encryptCode 同时存在时以 code 为准。
4. 签名 + accessToken(先代码,再解释)
① 签名函数(与官方案例一致)
// demos/lechange-onboarding-full/src/client.js
import crypto from 'node:crypto';
export function calcSign(time, nonce, appSecret) {
const raw = `time:${time},nonce:${nonce},appSecret:${appSecret}`;
return crypto.createHash('md5').update(raw, 'utf8').digest('hex');
}
本地校验(无需真实密钥):
cd demos/lechange-onboarding-full
node src/sign-test.js
# ✓ 签名算法与官方案例一致
② 通用请求封装
import { randomUUID } from 'node:crypto';
export function buildRequestBody(appId, appSecret, params = {}) {
const time = Math.floor(Date.now() / 1000);
const nonce = randomUUID();
return {
system: {
ver: '1.0',
appId,
sign: calcSign(time, nonce, appSecret),
time,
nonce,
},
id: randomUUID(),
params,
};
}
export async function callOpenApi(method, appId, appSecret, params = {}) {
const res = await fetch(`https://openapi.lechange.cn/openapi/${method}`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(buildRequestBody(appId, appSecret, params)),
});
const json = await res.json();
if (json.result?.code !== '0') {
throw new Error(`[${json.result.code}] ${json.result.msg}`);
}
return json.result.data;
}
③ 获取 token
const { accessToken, expireTime } = await callOpenApi(
'accessToken',
appId,
appSecret,
{}
);
// expireTime:剩余有效秒数;管理员 token 有效期 3 天,TK1002 时刷新
注意(文档原文要点):
- 勿频繁刷新 token,避免占用调用额度
- 使用超过 2 天未满 3 天再请求,会下发新 token,新旧 token 在各自生命周期内均可使用
5. bindDevice + listDeviceDetailsByPage 验收
接口切换说明:新接入请使用 listDeviceDetailsByPage 分页查询设备与通道详情。响应体中的
data.deviceList为设备数组字段名,与旧接口方法名deviceList不是同一回事。
绑定
await callOpenApi('bindDevice', appId, appSecret, {
token: accessToken,
deviceId: 'TESTQWERXXXX',
code: 'Admin123', // 按上表替换
});
// 成功:result.code === "0",无 data 字段
分页列表验证
const data = await callOpenApi('listDeviceDetailsByPage', appId, appSecret, {
token: accessToken,
pageSize: 10, // 必填,范围 1~50
page: 1, // 必填,从 1 开始
source: 'bindAndShare', // bind | share | bindAndShare(默认 bindAndShare)
});
const dev = data.deviceList?.find((d) => d.deviceId === 'TESTQWERXXXX');
console.log(dev?.deviceStatus === 'online' ? '在线' : dev?.deviceStatus, dev?.deviceName);
| 参数 | 必填 | 说明 |
|---|---|---|
pageSize | 是 | 每页 1~50 条 |
page | 是 | 页码,从 1 起 |
token | 是 | 管理员或子账户 accessToken |
source | 否 | bind 仅绑定 / share 仅分享 / bindAndShare 二者(子账号下该参数不生效) |
验收 JSON 长什么样(摘自官方样例):
{
"count": 1,
"deviceList": [{
"deviceId": "TESTQWERXXXX",
"deviceName": "TESTQWERXXXX",
"deviceStatus": "offline",
"deviceModel": "TP2",
"deviceAbility": "WLAN,CloudStorage,LocalStorage,...",
"catalog": "IPC",
"source": "bind",
"channelList": [{
"channelId": 0,
"channelName": "TESTQWERXXXX",
"channelStatus": "offline",
"csStatus": "expired",
"channelAbility": "WLAN,CloudStorage,LocalStorage"
}]
}]
}
| 字段 | 用途 |
|---|---|
deviceStatus | online / offline / sleep / upgrading |
deviceAbility | 设备级能力集,决定可调 API |
channelList[].channelId | 直播等接口必填通道号 |
channelList[].channelStatus | 通道在线状态 |
channelList[].csStatus | 云存:notExist / using / expired |
设备较多时循环翻页:
async function findDeviceById(appId, appSecret, token, targetId) {
let page = 1;
const pageSize = 50;
for (;;) {
const data = await callOpenApi('listDeviceDetailsByPage', appId, appSecret, {
token,
pageSize,
page,
source: 'bindAndShare',
});
const hit = data.deviceList?.find((d) => d.deviceId === targetId);
if (hit) return hit;
if (!data.deviceList?.length || data.deviceList.length < pageSize) return null;
page += 1;
}
}
一键脚本
cp .env.example .env # 填写 APP_ID / SECRET / DEVICE_ID / CODE
npm run onboard # token → bind → listDeviceDetailsByPage
npm run verify # 已绑定,仅分页列表验证
踩坑实录(FAQ)
| 现象 | 原因 | 处理 |
|---|---|---|
SN1005 | 5 分钟内 nonce 重复 | 每请求 randomUUID() |
| sign 失败 | 未按 time,nonce,appSecret 顺序拼串 | 跑 sign-test.js |
TK1002 | token 过期 | 重新 accessToken |
| 列表为空 | 未绑定到开发者账号 | 控制台 / App / bindDevice |
| bind 失败 | code 类型错误 | 对照安全码/密码/空串 |
FL1001 | 接入路数不足 | 控制台查看已购资源(全局返回码) |
| 抄旧博客调不通 | 使用了旧版协议或 deviceList 老接口 | 改用 listDeviceDetailsByPage |
| 列表有设备但字段读错 | 仍按 status/devices 解析 | 改用 deviceStatus/data.deviceList |
生产建议
- 服务端缓存
accessToken,临近过期或TK1002再刷新。 time与标准时间误差 ≤ 5 分钟。- 批量
bindDevice加队列与退避,失败设备可重试。 listDeviceDetailsByPage按pageSize(最大 50)翻页拉全量,勿假设一页返回全部设备。- 新需求开发前:在文档站内搜索接口名,确认不在「旧版本协议」目录下。
五句话小结
- 在 官网平台注册并创建应用,拿到
appId/appSecret。 - 设备 配网上线(App 可预览)。
- 用 App / 控制台 /
bindDevice完成开发者账号绑定。 - 仅用
openapi.lechange.cn+ 现行开发规范 调 OpenAPI,避开旧版协议模块。 - 用
listDeviceDetailsByPage返回的deviceList中出现目标deviceId,且deviceStatus === "online"作为第一台设备接入成功的标志。