IoT 物模型 + 客户端 SDK:乐橙摄像头、门锁、传感器统一上云管控

IoT 物模型 + 客户端 SDK:乐橙摄像头、门锁、传感器统一上云管控

去年给硬件商做 全屋 IoT 管控平台:乐橙摄像机、指纹锁、门窗传感器,合同写「统一 App、统一云端」。

交付时却是:摄像机走 bindDeviceLive 拉 HLS;门锁同事另写 MQTT;传感器还在轮询私有 HTTP。三个品类、三套 Cron——运维每周十几条「App 离线、乐橙 App 正常」。

复盘后收成 一条台账 + 两条能力轨listDeviceDetailsByPageproductId 分 IoT 物模型与视频 OpenAPI;OpenSDK 统一配网添加入口。


乐橙全屋场景:两类云协议,一套 App

乐橙开放平台 智能家居方案覆盖 智能摄像机、指纹锁、报警传感器(案例:鸿雁智+)。对接时别按品类写三套后台——云侧实际是 两种协议

品类云侧路径列表线索
乐橙 IPC视频 OpenAPI + OpenSDK Media无 productIddeviceAbilityAlarmMDAudioTalk
锁 / 传感器IoT 物模型productId 有值

文档约定:productId 有值 = IoT 设备,可调:

接口用途
getProductModel拉 Property / Service / Event 模板
getIotDeviceProperties读电量、门状态等
setIotDeviceProperties写属性
iotDeviceControl远程开锁等 Service

摄像机动检仍走 setMessageCallback,与物模型 并行

乐橙云 openapi.lechange.cn

你的 BFF

自有品牌 App · OpenSDK DeviceAdd

统一设备列表

sync-devices

productId 分轨

video 轨

iot 轨

listDeviceDetailsByPage

getProductModel

边界:物模型 不能替代 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);

踩坑 4propertiesref 数字字符串(如 "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 / contentgetProductModel 返回的 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 先接入?评论区说说。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值