dp-kit

Info

dp-kit 自 @ray-js/panel-sdk@1.7.0 开始加入

轻量的设备面板 DP 功能点处理工具库,内置支持复杂类型 DP 功能点解析、下发选项 (sendOptions) 增强,以及即时更新等能力。

什么时候需要 dp-kit?

✅ 适合使用:

  • 产品有复杂类型 DP 功能点(如 raw/string 类型的 colour_data 需要解析为 hue / saturation / value)
  • 需要下发节流、防抖、延迟等控制
  • 需要下发后立即更新 UI(immediate 模式)
  • 需要忽略设备上报响应 + 云端 DP 状态同步
  • 需要在下发前后注入自定义逻辑(onBeforeSendDp / onAfterSendDp)

❌ 不需要使用:

  • 产品只有简单 DP 功能点类型(bool / enum / value),直接使用 useProps + useActions 即可
  • 不需要任何下发增强能力

接入指南

Step 1: 配置产品 DP 功能点描述文件

src/devices/schema.ts

可参考 创建 SDM

Step 2: 配置复杂 DP 解析文件(可选)

src/devices/protocols.ts

  • 这一步对应 createDpKit 的 protocols 字段,用来自定义复杂 DP 功能点的解析与反解析规则(若当前产品不存在复杂 DP 数据,可以跳过)
export const protocols = {
  colour_data: {
    parser: (dpValue) => ({
      hue: parseInt(dpValue.slice(0, 4), 16),
      saturation: parseInt(dpValue.slice(4, 8), 16),
      value: parseInt(dpValue.slice(8, 12), 16),
    }),
    formatter: ({ hue, saturation, value }) =>
      `${hue.toString(16).padStart(4, '0')}${saturation
        .toString(16)
        .padStart(4, '0')}${value.toString(16).padStart(4, '0')}`,
  },
};

Step 3: 根据业务场景生成 dp-kit

src/devices/index.ts

import { SmartDeviceModel, createDpKit } from '@ray-js/panel-sdk';
import { protocols } from '@/devices/protocols';
import { defaultSchema } from '@/devices/schema';

type SmartDeviceSchema = typeof defaultSchema;

export const dpKit = createDpKit<SmartDeviceSchema>({ protocols });

const options = {
  interceptors: dpKit.interceptors,
};

export const devices = {
  common: new SmartDeviceModel<SmartDeviceSchema>(options),
};

Step 4: 初始化 dp-kit 复杂 DP 协议数据(可选)

src/app.tsx

  • 接入 dpKit.init 初始化复杂 DP 协议数据方法(若不需要复杂数据,可不调用)

Step 5: 全局类型定义接入(可选)

typings/sdm.d.ts

  • 推荐直接参考最新模板文件: PublicSdmTemplate/typings/sdm.d.ts
  • 按当前项目实际情况,将模板中的 @/devices/schema、@/devices/protocols 等引用路径调整为你的工程路径即可
  • 若不需要 TS 类型自动提示,可以跳过这一步

Step 6: 复杂 DP 协议数据获取及下发

src/pages/home/index.tsx

  • useStructuredProps hooks 接入获取复杂 DP 协议数据
  • useStructuredActions hooks 接入下发复杂 DP 协议数据
import _ from 'lodash-es';
import React from 'react';
import { View } from '@ray-js/ray';
import { useStructuredProps, useStructuredActions } from '@ray-js/panel-sdk';

export default function Home() {
  const colour = useStructuredProps((props) => props.colour_data);
  const actions = useStructuredActions();
  return (
    <View
      style={{ flex: 1 }}
      onClick={() => {
        actions.colour_data.set({
          hue: _.random(0, 360),
          saturation: _.random(0, 1000),
          value: _.random(0, 1000),
        });
      }}
    >
      <View>hue: {colour.hue}</View>
      <View>saturation: {colour.saturation}</View>
      <View>value: {colour.value}</View>
    </View>
  );
}

API

createDpKit 完整 API 参考和类型定义,请查看 createDpKit API 文档。

以下是 createDpKit 各配置项的使用说明和示例。

复杂 DP 解析配置(可选)

这部分对应 createDpKit 的 protocols 字段,用来指定复杂 DP 功能点的解析方式。配置后会自动解析 & 反解析这类 DP 功能点,无需再在业务代码里进行处理。

const dpKit: Middleware = createDpKit<SmartDeviceSchema>({
  protocols: {
    colour_data: {
      parser: (dpValue) => ({
        hue: parseInt(dpValue.slice(0, 4), 16),
        saturation: parseInt(dpValue.slice(4, 8), 16),
        brightness: parseInt(dpValue.slice(8, 12), 16),
      }),
      formatter: ({ hue, saturation, brightness }) =>
        `${hue.toString(16).padStart(4, '0')}${saturation
          .toString(16)
          .padStart(4, '0')}${brightness.toString(16).padStart(4, '0')}`,
    },

    custom_raw_dp: {
      parser: (dpValue) => {
        // 自定义解析
      },

      formatter: (dpValue) => {
        // 自定义格式化
      },
    },
  },
});

/**
 * 获取 colour_data DP 功能点数据
 * useProps(props => props.colour_data) = 000003e803e8
 * useStructuredProps(props => props.colour_data) = { hue: 0, saturation: 1000, brightness: 1000 }
 */

/**
 * 下发 colour_data DP 功能点数据
 * const actions = useActions(); => actions.colour_data.set('000003e803e8');
 * const actions = useStructuredActions(); => actions.colour_data.set({ hue: 0, saturation: 1000, brightness: 1000 });
 */

如果数据结构比较固定,也可以改用更简短的映射配置;但在教程场景里,优先使用 parser / formatter 通常更直观。

下发选项 (sendOptions)(可选)

下发 DP 功能点的选项,对应 createDpKit 的 sendDpOption 字段。若配置在 createDpKit 中,将影响后续所有的下发指令。

完整类型定义请查看 createDpKit API > SendDpOption

属性类型默认值说明
immediatebooleanfalse下发后立即更新 UI,无需等待设备上报
throttlenumber0下发节流 (ms)
debouncenumber0下发防抖 (ms),与节流冲突
delaynumber0延迟下发 (ms)
checkRepeatbooleanfalse重复值不下发
orderedbooleanfalse多个 DP 功能点按顺序下发
ignoreDpDataResponseboolean | IgnoreDpChangeInterceptorOptionsfalse忽略设备上报响应(1.11.0+)
synchronizeDevPropertyboolean | DevPropInterceptorOptionsfalse云端 DP 状态同步(1.11.0+)
protocolsRecord<string, CustomRawDpMap>-单次下发临时协议转换

ignoreDpDataResponse 对象配置:

属性类型默认值说明
whiteDpCodesstring[]-白名单 DP 功能点,不会忽略上报
timeoutnumber10000防抖超时 (ms),超时后才触发 dpDataChange(1.14.0+)
customRulefunction-自定义忽略规则,优先级最高(1.14.0+)
debugboolean-开启调试日志

synchronizeDevProperty 对象配置:

属性类型说明
blackDpCodesstring[]黑名单 DP 功能点,不同步到云端
defaultStateobject | function云端数据为空时的默认值,支持异步

例如,如果期望应用内的下发都具有 600ms 的节流,您可以:

const dpKit = createDpKit<SmartDeviceSchema>({
  sendDpOption: {
    throttle: 600,
  },
});

或者,如果期望 DP 功能点不再等到设备上报后才更新,您可以:

const dpKit = createDpKit<SmartDeviceSchema>({
  sendDpOption: {
    immediate: true,
  },
});

注意,通过 dp-kit immediate 即时更新的 DP 功能点数据,在 SDM 内部 onDataChange 事件触发时会额外附带一个 __from__: dp-kit 的回调值,方便业务判断或区分实际 DP 功能点上报自行处理,原始 onDpDataChange 事件则不会被触发。

此外,如果您想针对单个 DP 功能点下发不再等到设备上报后才更新,那么您可以:

const actions = useActions();
actions.power.toggle({ immediate: true });

但仅使用 immediate 在设备上报时还会触发一次更新,容易导致 UI 闪烁。如果期望忽略设备上报的响应,即仅下发,您可以:

const dpKit = createDpKit<SmartDeviceSchema>({
  sendDpOption: {
    immediate: true,
    ignoreDpDataResponse: true, // 忽略设备上报响应
  },
});

或者,有部分 DP 功能点需要进行响应,可以使用白名单配置:

const dpKit = createDpKit<SmartDeviceSchema>({
  sendDpOption: {
    immediate: true,
    ignoreDpDataResponse: {
      whiteDpCodes: ['switch'], // switch DP 功能点上报时进行响应,其他 DP 功能点上报会忽略
    },
  },
});

您也可以使用 timeout 参数配置防抖超时时间:

const dpKit = createDpKit<SmartDeviceSchema>({
  sendDpOption: {
    immediate: true,
    ignoreDpDataResponse: {
      timeout: 1000 * 5, // 5 秒超时,超过此时间才触发 dpDataChange
    },
  },
});

或者使用 customRule 自定义忽略规则(优先级最高,配置后 whiteDpCodes 和 timeout 将失效):

const dpKit = createDpKit<SmartDeviceSchema>({
  sendDpOption: {
    immediate: true,
    ignoreDpDataResponse: {
      customRule: (dpState, lastSendTimestamp) => {
        // 自定义逻辑:仅响应特定 dp 或时间条件
        if (dpState.switch !== undefined) {
          return true; // switch dp 始终响应
        }
        if (lastSendTimestamp === null) {
          return true; // 首次上报响应
        }
        const timeDiff = Date.now() - lastSendTimestamp;
        return timeDiff > 8000; // 超过 8 秒才响应
      },
    },
  },
});

当忽略设备上报响应时,退出面板重新进入后,设备上一次的状态会无法获取,您可以使用 synchronizeDevProperty 实现拉取云端 DP 状态:

const dpKit = createDpKit<SmartDeviceSchema>({
  sendDpOption: {
    immediate: true,
    ignoreDpDataResponse: {
      whiteDpCodes: ['switch'],
    },
    synchronizeDevProperty: true, // 同步云端设备存储属性
  },
});

当每次下发 DP 功能点时,会自动写入到云端设备存储数据。退出面板重新进入时,会做一次 DP 状态初始化,将云端设备存储数据拉取到面板。

同样地,您也可以配置哪些 DP 功能点不需要进行同步:

const dpKit = createDpKit<SmartDeviceSchema>({
  sendDpOption: {
    synchronizeDevProperty: {
      blackDpCodes: ['switch'], // switch DP 功能点不会做云端数据同步
    },
  },
});

云端数据为空时,可以配置默认值:

const dpKit = createDpKit<SmartDeviceSchema>({
  sendDpOption: {
    ignoreDpDataResponse: true,
    synchronizeDevProperty: {
      defaultState: {
        switch: false, // switch DP 功能点默认值配置(如果云端数据为空)
      },
    },
  },
});

onBeforeSendDp / onAfterSendDp(可选)

下发 DP 前/后的钩子(在 publishDps 之前/之后),建议传入 SmartDeviceSchema 以确保获得正确的 type。

export const dpKit = createDpKit<SmartDeviceSchema>({
  onBeforeSendDp(dpState) {
    console.log('=== onBeforeSendDp', dpState);
  },
  onAfterSendDp(dpState) {
    console.log('=== onAfterSendDp', dpState);
  },
});

进阶阅读

logger 拦截器

dp-kit 内置了 logger 拦截器,可在控制台查看 DP 下发和上报日志。

logger 文档

matter-kit 拦截器

如果需要同时处理 Matter 设备,可搭配 matter-kit 使用。

matter-kit 文档

SmartDeviceModel 使用指南

dp-kit 基于 SmartDeviceModel 的拦截器机制工作。

SmartDeviceModel 文档

使用 SDM 拦截器和 DP 自定义协议解析

一个基于 dp-kit 的自定义拦截器和自定义协议解析的教程示例。

教程地址

Beacon 设备面板模板

一个基于 dp-kit 内置功能实现的 Beacon 设备面板教程示例。

教程地址