设备在线了,后台还是 0 台?开放平台从注册到首台设备绑定

9 阅读6分钟

设备在线了,后台还是 0 台?开放平台从注册到首台设备绑定


周五夜里,同事说摄像头在线、App 能预览,开放平台设备列表却是空的。真不是签名写错——设备还没进开发者账号。配网成功,不等于绑定成功;第一台设备验收,用 listDeviceDetailsByPage 看有没有你的 deviceId


为什么说现在搞懂这条链路很值

调用乐橙设备能力的第三方与个人开发者:实时预览、录像回放、告警、云存储等,如果想要使用上述这些能力的前提都建立在“设备已挂在你的账号名下”。否则一切功能都无法接入,接下来我重点讲解下如何接入设备。

集成延期,常见不是「业务难」,而是阶段被混在一块:

阶段误解实际
配网Wi-Fi 连上即可设备可上网、App 可发现
绑定App 能看即可设备须进入开发者账号资产池
调 API有 appId 即可须先 accessToken,再在 params 里带 token

目前有三种绑定入口:

  1. 官方App(现场配网 + 单机验证;开发者账号可直接登录 App)
  2. 控制台 · 设备接入服务(实施/运维友好)
  3. bindDevice OpenAPI(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

根据官方提示完成应用创建:应用与开发者账号关联,后续设备绑定、增值服务均基于该账号体系。

踩坑 AappSecret 写进前端或公开仓库 → 应仅部署在服务端。


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 规则见同一文档);codeencryptCode 同时存在时以 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
sourcebind 仅绑定 / 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"
    }]
  }]
}
字段用途
deviceStatusonline / 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)

现象原因处理
SN10055 分钟内 nonce 重复每请求 randomUUID()
sign 失败未按 time,nonce,appSecret 顺序拼串sign-test.js
TK1002token 过期重新 accessToken
列表为空未绑定到开发者账号控制台 / App / bindDevice
bind 失败code 类型错误对照安全码/密码/空串
FL1001接入路数不足控制台查看已购资源(全局返回码
抄旧博客调不通使用了旧版协议deviceList 老接口改用 listDeviceDetailsByPage
列表有设备但字段读错仍按 status/devices 解析改用 deviceStatus/data.deviceList

生产建议

  1. 服务端缓存 accessToken,临近过期或 TK1002 再刷新。
  2. time 与标准时间误差 ≤ 5 分钟。
  3. 批量 bindDevice 加队列与退避,失败设备可重试。
  4. listDeviceDetailsByPagepageSize(最大 50)翻页拉全量,勿假设一页返回全部设备。
  5. 新需求开发前:在文档站内搜索接口名,确认不在「旧版本协议」目录下。

五句话小结

  1. 在 官网平台注册并创建应用,拿到 appId / appSecret
  2. 设备 配网上线(App 可预览)。
  3. App / 控制台 / bindDevice 完成开发者账号绑定。
  4. 仅用 openapi.lechange.cn + 现行开发规范 调 OpenAPI,避开旧版协议模块
  5. listDeviceDetailsByPage 返回的 deviceList 中出现目标 deviceId,且 deviceStatus === "online" 作为第一台设备接入成功的标志。