IoT 物模型 + 客户端 SDK:乐橙摄像头、门锁、传感器统一上云管控
去年给硬件商做 全屋 IoT 管控平台:乐橙摄像机、指纹锁、门窗传感器,合同写「统一 App、统一云端」。
交付时却是:摄像机走 bindDeviceLive 拉 HLS;门锁同事另写 MQTT;传感器还在轮询私有 HTTP。三个品类、三套 Cron——运维每周十几条「App 离线、乐橙 App 正常」。
复盘后收成 一条台账 + 两条能力轨:listDeviceDetailsByPage 看 productId 分 IoT 物模型与视频 OpenAPI;OpenSDK 统一配网添加入口。
乐橙全屋场景:两类云协议,一套 App
乐橙开放平台 智能家居方案覆盖 智能摄像机、指纹锁、报警传感器(案例:鸿雁智+)。对接时别按品类写三套后台——云侧实际是 两种协议:
| 品类 | 云侧路径 | 列表线索 |
|---|---|---|
| 乐橙 IPC | 视频 OpenAPI + OpenSDK Media | 常 无 productId;deviceAbility 含 AlarmMD、AudioTalk |
| 锁 / 传感器 | IoT 物模型 | productId 有值 |
文档约定:productId 有值 = IoT 设备,可调:
| 接口 | 用途 |
|---|---|
getProductModel | 拉 Property / Service / Event 模板 |
getIotDeviceProperties | 读电量、门状态等 |
setIotDeviceProperties | 写属性 |
iotDeviceControl | 远程开锁等 Service |
摄像机动检仍走 setMessageCallback,与物模型 并行。
边界:物模型 不能替代 IPC 的 HLS / 对讲;OpenSDK 配网,物模型读写只在 BFF;勿用停维护的 deviceList 方法。
Step 0 · 调用壳(国内网关)
// lib/platform-call.js
import crypto from 'node:crypto';
import { randomUUID } from 'node:crypto';
function calcSign(time, nonce, secret) {
const raw = `time:${time},nonce:${nonce},appSecret:${secret}`;
return crypto.createHash('md5').update(raw, 'utf8').digest('hex');
}
export async function platformCall(method, params = {}) {
const time = Math.floor(Date.now() / 1000);
const nonce = randomUUID();
const body = {
system: {
ver: '1.0',
appId: process.env.APP_ID,
sign: calcSign(time, nonce, process.env.APP_SECRET),
time,
nonce,
},
id: randomUUID(),
params,
};
const res = await fetch(`${process.env.OPENAPI_BASE}/${method}`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(body),
});
const json = await res.json();
if (json.result?.code !== '0') throw new Error(`[${json.result.code}] ${json.result.msg}`);
return json.result.data;
}
export async function adminToken() {
const { accessToken } = await platformCall('accessToken', {});
return accessToken;
}
APP_ID=lcdxxxxxxxxxxxx
APP_SECRET=your_secret
OPENAPI_BASE=https://openapi.lechange.cn/openapi
签名:开发规范 · 先在 open.imou.com 创建应用。
Step 1 · 统一台账 + productId 分轨
// jobs/sync-devices.js
import 'dotenv/config';
import { platformCall, adminToken } from '../lib/platform-call.js';
function classify(dev) {
const isIot = Boolean(dev.productId);
const ability = dev.channelList?.[0]?.channelAbility ?? dev.deviceAbility ?? '';
let category = 'unknown';
if (isIot) {
category = /lock|锁/i.test(dev.deviceModel ?? '') ? 'lock' : 'sensor';
} else if (/AlarmMD|AudioTalk|PTZ/.test(ability)) {
category = 'camera';
}
return {
deviceId: dev.deviceId,
deviceName: dev.deviceName,
productId: dev.productId ?? null,
category,
track: isIot ? 'iot' : 'video',
online: dev.deviceStatus === 'online',
ability,
};
}
const token = await adminToken();
const page = await platformCall('listDeviceDetailsByPage', {
token, pageSize: 50, page: 1, source: 'bindAndShare',
});
console.log(JSON.stringify((page.deviceList ?? []).map(classify), null, 2));
踩坑 1:全是摄像机、无 productId —— 正常,别调 getProductModel。
踩坑 2:锁与传感器 productId 不同,getProductModel 按 productId 分别缓存。
踩坑 3:乐橙 App 能看、列表 0 → 设备未绑 开发者主账号。
Step 2 · IoT 轨:模板 + 读属性
// services/iot-thing.js
const modelCache = new Map();
export async function loadProductModel(productId) {
if (modelCache.has(productId)) return modelCache.get(productId);
const model = await platformCall('getProductModel', {
token: await adminToken(),
productId,
});
modelCache.set(productId, model);
return model;
}
export async function readProperties(deviceId, productId, propertyRefs) {
return platformCall('getIotDeviceProperties', {
token: await adminToken(),
deviceId,
productId,
properties: propertyRefs,
});
}
const model = await loadProductModel('Z8vW2yWE');
const batteryRef = model.properties?.find(p => p.name?.includes('电量'))?.ref ?? '3301';
const state = await readProperties('LOCK_SN_001', 'Z8vW2yWE', [batteryRef]);
console.log(state.properties, state.status);
踩坑 4:properties 传 ref 数字字符串(如 "3301"),不是 identifier 英文名——必须先拉模板。
物模型三维(见 概述):
Property — 读/写状态(电量、开关)
Service — 方法(远程开锁)
Event — 上报(可经 setMessageCallback 进告警中心)
Step 3 · IoT 轨:远程开锁 Service
await platformCall('iotDeviceControl', {
token: await adminToken(),
deviceId: 'LOCK_SN_001',
productId: 'Z8vW2yWE',
ref: '3010',
content: {},
});
ref / content 以 getProductModel 返回的 services[] 为准;无入参时 content: {}。
踩坑 5:方案写「一键开锁」→ BFF 必须 二次确认 + 审计日志,物模型只提供能力。
Step 4 · 视频轨:乐橙 IPC
const live = await platformCall('bindDeviceLive', {
token: await adminToken(),
deviceId: 'CAM_SN_001',
channelId: '0',
streamId: 1,
liveMode: 'proxy',
});
await platformCall('setMessageCallback', {
token: await adminToken(),
status: 'on',
callbackUrl: process.env.CALLBACK_URL,
callbackFlag: 'alarm,deviceStatus',
basePush: '2',
});
告警统一入库:
function normalizeAlarm(raw, row) {
if (row.track === 'video') {
return { source: 'video', type: raw.msgType, deviceId: raw.did, ts: raw.time };
}
return { source: 'iot', deviceId: raw.did, payload: raw };
}
Step 5 · OpenSDK:统一添加入口
GET /api/v1/rooms/{id}/devices → BFF 统一 REST
camera → { preview, talk } # OpenSDK MediaModule
lock → { unlock, readState } # iotDeviceControl + getIotDeviceProperties
sensor → { readProperties }
String host = "openapi.lechange.cn:443";
LCOpenSDK_Api.initOpenApi(new InitParams(context, host, bff.fetchAccessToken()));
// DeviceAddModule — 摄像机/锁/传感器统一添加入口
详见 Android 标准化 Demo · 资源下载。
Checklist
□ listDeviceDetailsByPage 全量,classify 打印 track
□ 摄像机:bindDeviceLive + setMessageCallback
□ 锁/传感器:getProductModel + getIotDeviceProperties
□ 开锁:iotDeviceControl + 鉴权
□ OpenSDK DeviceAdd 统一入口
物模型缓存与多租户
getProductModel按 productId Redis 缓存,固件升级清缓存。- 前端用 ref→中文名 映射,勿硬编码
"3301"。 - 多租户 按 deviceId 鉴权,共用 productId 模板。
选型速查
| 需求 | 乐橙路径 |
|---|---|
| 门口直播 + 对讲 | OpenSDK + bindDeviceLive |
| 锁电量 / 门磁 | IoT Property |
| 远程开锁 | iotDeviceControl |
| 摄像机动检 | setMessageCallback |
| 7 天 H5 PoC | 轻应用 |
排错
| 现象 | 处理 |
|---|---|
| getProductModel 失败 | 无 productId,误分类 |
| properties 空 | ref 错 / offline |
| 三类三个 Cron | 合并 sync-devices |
| IPC 走 iot 轨 | 无 productId → video |
安全
appSecret仅 BFF;开锁 Service 强鉴权。- 回调 HTTPS + 200;我的资源 关注配额。
小结
乐橙摄像头、门锁、传感器 不必三套后台:
OpenSDK DeviceAdd 统一配网
→ listDeviceDetailsByPage 台账(productId 分轨)
→ iot 轨:getProductModel / Property / Service
→ video 轨:bindDeviceLive / setMessageCallback
→ BFF 一套 REST 给 App
客户验收:一个 App、三个房间 Tab、锁能开、摄像机能看、传感器刷新——底层两条轨,用户无感。
延伸阅读
开始接入
在 乐橙开放平台 open.imou.com 注册并创建应用。平台以视频技术与安全为核心,提供 OpenAPI、IoT 物模型、OpenSDK、轻应用,帮助集成商一站式落地 摄像机 + 锁 + 传感器 混合全屋方案。
建议先跑 sync-devices 打印 classify,看清哪些设备有 productId,再写 UI——比先画原型后补协议省一半返工。
你的项目里是锁先接入还是 IPC 先接入?评论区说说。
5858

被折叠的 条评论
为什么被折叠?



