SDM 拦截器

Warning

自定义拦截器及其类型仍属于实验性能力。logger、dp-kit、matter-kit 可直接使用;若自行实现拦截器,请关注后续 API 调整。

interceptors 用来在 SDM 的初始化、下发和事件回调链路中插入统一逻辑,适合处理日志打印、协议转换、结构化数据映射以及条件短路等场景。

什么时候需要拦截器?

✅ 适合使用:

  • 需要在 publishDps 前后统一处理数据、日志或埋点
  • 需要把设备原始上报转换为更适合业务消费的数据结构
  • 需要复用 logger、dp-kit、matter-kit
  • 多个页面都依赖同一套初始化或协议处理逻辑

❌ 不需要使用:

  • 只是单个页面的临时展示逻辑
  • 只是读取简单 DP 功能点,useProps / useActions 已足够
  • 不需要改写 SDM 初始化、下发或事件链路

内置拦截器怎么选?

拦截器适用场景核心能力文档
logger开发调试打印 init / request / response 链路日志logger
dp-kit复杂 DP 功能点协议解析、结构化状态、下发选项 (sendOptions)、即时更新dp-kit
matter-kitMatter 照明设备Matter 与标准 DP 双向转换,可与 dp-kit 组合matter-kit
Info

SDM 默认会在内置链路上挂载 logger。但当你手动配置某个拦截器数组时,该链路会被新数组覆盖;如果仍需要日志,请把 logger 显式放回数组里。

接入方式

Step 1: 选择拦截点

分类可配置项常见用途
initinitDpState、initDevInfo初始化时修正 dpState 或 devInfo
requestpublishDps下发前格式化、补充参数、节流 / 防抖
responseonDpDataChange、onDeviceInfoUpdated、onDeviceOnlineStatusUpdate、onNetworkStatusChange、onBluetoothAdapterStateChange上报转换、状态同步、事件增强
onInit(device) => void设备初始化完成后的额外逻辑,例如 dpKit.init(device)

Step 2: 挂载到 SmartDeviceModel

src/devices/index.ts

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

type SmartDeviceSchema = typeof defaultSchema;

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

const options = {
  interceptors: {
    onInit: (device) => {
      dpKit.init(device);
    },
    init: {
      initDpState: [logger, ...dpKit.interceptors.init.initDpState],
      initDevInfo: [logger, ...dpKit.interceptors.init.initDevInfo],
    },
    request: {
      publishDps: [logger, ...dpKit.interceptors.request.publishDps],
    },
    response: {
      onDpDataChange: [logger, ...dpKit.interceptors.response.onDpDataChange],
      onDeviceInfoUpdated: [logger, ...dpKit.interceptors.response.onDeviceInfoUpdated],
      onDeviceOnlineStatusUpdate: [logger],
      onNetworkStatusChange: [logger],
      onBluetoothAdapterStateChange: [logger],
    },
  },
};

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

如果只使用单个内置套件,也可以直接传入 interceptors: dpKit.interceptors 或 interceptors: matterKit.interceptors。

Step 3: 处理多个拦截器的顺序

  • 同一链路中的拦截器按数组顺序包裹执行;数组越靠前,越早接收到输入
  • 如果某个拦截器不调用 next(...),后续拦截器和底层方法都不会继续执行
  • 同时使用 dp-kit 和 matter-kit 处理 publishDps 时,通常让 dp-kit 在前、matter-kit 在后
request: {
  publishDps: [
    ...dpKit.interceptors.request.publishDps,
    ...matterKit.interceptors.request.publishDps,
  ],
},

自定义拦截器

常用类型

类型对应链路说明
Interceptor通用快速定义任意链路的通用拦截器
InitDpStateInterceptorinit.initDpState初始化 dpState 前后的统一处理
InitDevInfoInterceptorinit.initDevInfo初始化 devInfo 前后的统一处理
PublishDpsInterceptorrequest.publishDps改写下发参数、补充 DP 功能点或 options
OnDpDataChangeInterceptorresponse.onDpDataChange将设备上报转换为业务态
OnDeviceInfoUpdatedInterceptorresponse.onDeviceInfoUpdated响应设备信息变化
OnDeviceOnlineStatusUpdateInterceptorresponse.onDeviceOnlineStatusUpdate响应上下线变化
OnNetworkStatusChangeInterceptorresponse.onNetworkStatusChange响应网络变化
OnBluetoothAdapterStateChangeInterceptorresponse.onBluetoothAdapterStateChange响应蓝牙状态变化

群组场景还有 OnGroupDpDataChangeInterceptor 与 OnGroupInfoChangeInterceptor,本文聚焦单设备 SDM 的常用链路。

执行模型

Info

拦截器采用三层柯里化写法:ctx => next => data。

参数说明
ctx.type当前拦截的链路名,例如 publishDps 或 onDpDataChange
ctx.log当前链路对应的日志对象,可调用 log.info / log.warn / log.fatal
ctx.instance当前 SmartDeviceModel 实例,可获取 getDpState()、getDevInfo() 等数据
next下一个拦截器或底层方法;调用后链路才会继续
data当前请求入参或事件回调数据
const interceptor = (ctx) => (next) => (data) => {
  return next(data);
};

常见模式示例

1. 在下发前补充或改写数据

import { PublishDpsInterceptor } from '@ray-js/panel-sdk';

export const appendExtraDp: PublishDpsInterceptor<SmartDeviceSchema> =
  (ctx) => (next) => (dpState, options) => {
    const nextDpState = {
      ...dpState,
      power_go: !ctx.instance.getDpState().power_go,
    };

    ctx.log.info('append extra dp before publishDps', nextDpState, 'appendExtraDp');

    return next(nextDpState, options);
  };

2. 将原始上报转换为业务数据

import { DpState, DpValue, OnDpDataChangeInterceptor } from '@ray-js/panel-sdk';

export const mapDpsToDpState: OnDpDataChangeInterceptor<
  SmartDeviceSchema,
  DpState
> = (ctx) => (next) => (data) => {
  const devInfo = ctx.instance.getDevInfo();
  const dpState = {} as DpState;

  Object.keys(data.dps).forEach((dpId) => {
    dpState[devInfo.idCodes[dpId]] = data.dps[dpId] as DpValue;
  });

  return next(dpState);
};

3. 在特定条件下短路后续链路

import { Interceptor } from '@ray-js/panel-sdk';

export const skipWhenReadonly: Interceptor<SmartDeviceSchema> =
  (ctx) => (next) => (data) => {
    const isReadonlyMode = true;

    if (isReadonlyMode) {
      ctx.log.info('skip current interceptor chain', data, 'skipWhenReadonly');
      return null;
    }

    return next(data);
  };

进阶阅读

logger

查看默认日志拦截器的接入方式与类型定义

logger 文档

dp-kit

复杂 DP 功能点解析、sendOptions 和结构化 hooks 的完整用法

dp-kit 使用指南

matter-kit

Matter 照明设备的 DP 映射与组合方式

matter-kit 使用指南

SmartDeviceModel

interceptors 最终挂载在 SmartDeviceModel 上,完整初始化参数见 API 文档

SmartDeviceModel 初始化 API